eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/mercurial/lock.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 # lock.py - simple advisory locking scheme for mercurial
       
     2 #
       
     3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
       
     4 #
       
     5 # This software may be used and distributed according to the terms of the
       
     6 # GNU General Public License version 2 or any later version.
       
     7 
       
     8 import util, error
       
     9 import errno, os, socket, time
       
    10 import warnings
       
    11 
       
    12 class lock(object):
       
    13     '''An advisory lock held by one process to control access to a set
       
    14     of files.  Non-cooperating processes or incorrectly written scripts
       
    15     can ignore Mercurial's locking scheme and stomp all over the
       
    16     repository, so don't do that.
       
    17 
       
    18     Typically used via localrepository.lock() to lock the repository
       
    19     store (.hg/store/) or localrepository.wlock() to lock everything
       
    20     else under .hg/.'''
       
    21 
       
    22     # lock is symlink on platforms that support it, file on others.
       
    23 
       
    24     # symlink is used because create of directory entry and contents
       
    25     # are atomic even over nfs.
       
    26 
       
    27     # old-style lock: symlink to pid
       
    28     # new-style lock: symlink to hostname:pid
       
    29 
       
    30     _host = None
       
    31 
       
    32     def __init__(self, file, timeout=-1, releasefn=None, desc=None):
       
    33         self.f = file
       
    34         self.held = 0
       
    35         self.timeout = timeout
       
    36         self.releasefn = releasefn
       
    37         self.desc = desc
       
    38         self.lock()
       
    39 
       
    40     def __del__(self):
       
    41         if self.held:
       
    42             warnings.warn("use lock.release instead of del lock",
       
    43                     category=DeprecationWarning,
       
    44                     stacklevel=2)
       
    45 
       
    46             # ensure the lock will be removed
       
    47             # even if recursive locking did occur
       
    48             self.held = 1
       
    49 
       
    50         self.release()
       
    51 
       
    52     def lock(self):
       
    53         timeout = self.timeout
       
    54         while 1:
       
    55             try:
       
    56                 self.trylock()
       
    57                 return 1
       
    58             except error.LockHeld, inst:
       
    59                 if timeout != 0:
       
    60                     time.sleep(1)
       
    61                     if timeout > 0:
       
    62                         timeout -= 1
       
    63                     continue
       
    64                 raise error.LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
       
    65                                      inst.locker)
       
    66 
       
    67     def trylock(self):
       
    68         if self.held:
       
    69             self.held += 1
       
    70             return
       
    71         if lock._host is None:
       
    72             lock._host = socket.gethostname()
       
    73         lockname = '%s:%s' % (lock._host, os.getpid())
       
    74         while not self.held:
       
    75             try:
       
    76                 util.makelock(lockname, self.f)
       
    77                 self.held = 1
       
    78             except (OSError, IOError), why:
       
    79                 if why.errno == errno.EEXIST:
       
    80                     locker = self.testlock()
       
    81                     if locker is not None:
       
    82                         raise error.LockHeld(errno.EAGAIN, self.f, self.desc,
       
    83                                              locker)
       
    84                 else:
       
    85                     raise error.LockUnavailable(why.errno, why.strerror,
       
    86                                                 why.filename, self.desc)
       
    87 
       
    88     def testlock(self):
       
    89         """return id of locker if lock is valid, else None.
       
    90 
       
    91         If old-style lock, we cannot tell what machine locker is on.
       
    92         with new-style lock, if locker is on this machine, we can
       
    93         see if locker is alive.  If locker is on this machine but
       
    94         not alive, we can safely break lock.
       
    95 
       
    96         The lock file is only deleted when None is returned.
       
    97 
       
    98         """
       
    99         locker = util.readlock(self.f)
       
   100         try:
       
   101             host, pid = locker.split(":", 1)
       
   102         except ValueError:
       
   103             return locker
       
   104         if host != lock._host:
       
   105             return locker
       
   106         try:
       
   107             pid = int(pid)
       
   108         except ValueError:
       
   109             return locker
       
   110         if util.testpid(pid):
       
   111             return locker
       
   112         # if locker dead, break lock.  must do this with another lock
       
   113         # held, or can race and break valid lock.
       
   114         try:
       
   115             l = lock(self.f + '.break', timeout=0)
       
   116             os.unlink(self.f)
       
   117             l.release()
       
   118         except error.LockError:
       
   119             return locker
       
   120 
       
   121     def release(self):
       
   122         if self.held > 1:
       
   123             self.held -= 1
       
   124         elif self.held == 1:
       
   125             self.held = 0
       
   126             if self.releasefn:
       
   127                 self.releasefn()
       
   128             try:
       
   129                 os.unlink(self.f)
       
   130             except OSError:
       
   131                 pass
       
   132 
       
   133 def release(*locks):
       
   134     for lock in locks:
       
   135         if lock is not None:
       
   136             lock.release()
       
   137