Mercurial > hg > mercurial-crew-with-dirclash
comparison mercurial/lock.py @ 1877:d314a89fa4f1
change lock format to let us detect and break stale locks.
old style: symlink to pid
new style: symlink to hostname:pid
if lock code finds new-style lock, it breaks lock if locking pid is on
same machine and pid is not alive.
otherwise, lock is left alone. this makes locking code safe with
old-style locks and with locks on other machines.
new code makes server part of mercurial more robust in case machine
crashes, power fails, or crazy user does kill -9.
author | Vadim Gelfer <vadim.gelfer@gmail.com> |
---|---|
date | Fri, 10 Mar 2006 08:31:31 -0800 |
parents | cd5c1db2132a |
children | ff5c9a92f556 |
comparison
equal
deleted
inserted
replaced
1876:2e0fd78587bd | 1877:d314a89fa4f1 |
---|---|
4 # | 4 # |
5 # This software may be used and distributed according to the terms | 5 # This software may be used and distributed according to the terms |
6 # of the GNU General Public License, incorporated herein by reference. | 6 # of the GNU General Public License, incorporated herein by reference. |
7 | 7 |
8 from demandload import * | 8 from demandload import * |
9 demandload(globals(), 'errno os time util') | 9 demandload(globals(), 'errno os socket time util') |
10 | 10 |
11 class LockException(Exception): | 11 class LockException(Exception): |
12 pass | 12 pass |
13 class LockHeld(LockException): | 13 class LockHeld(LockException): |
14 pass | 14 pass |
15 class LockUnavailable(LockException): | 15 class LockUnavailable(LockException): |
16 pass | 16 pass |
17 | 17 |
18 class lock(object): | 18 class lock(object): |
19 # lock is symlink on platforms that support it, file on others. | |
20 | |
21 # symlink is used because create of directory entry and contents | |
22 # are atomic even over nfs. | |
23 | |
24 # old-style lock: symlink to pid | |
25 # new-style lock: symlink to hostname:pid | |
26 | |
19 def __init__(self, file, timeout=-1, releasefn=None): | 27 def __init__(self, file, timeout=-1, releasefn=None): |
20 self.f = file | 28 self.f = file |
21 self.held = 0 | 29 self.held = 0 |
22 self.timeout = timeout | 30 self.timeout = timeout |
23 self.releasefn = releasefn | 31 self.releasefn = releasefn |
32 self.id = None | |
33 self.host = None | |
34 self.pid = None | |
24 self.lock() | 35 self.lock() |
25 | 36 |
26 def __del__(self): | 37 def __del__(self): |
27 self.release() | 38 self.release() |
28 | 39 |
39 timeout -= 1 | 50 timeout -= 1 |
40 continue | 51 continue |
41 raise inst | 52 raise inst |
42 | 53 |
43 def trylock(self): | 54 def trylock(self): |
44 pid = os.getpid() | 55 if self.id is None: |
56 self.host = socket.gethostname() | |
57 self.pid = os.getpid() | |
58 self.id = '%s:%s' % (self.host, self.pid) | |
59 while not self.held: | |
60 try: | |
61 util.makelock(self.id, self.f) | |
62 self.held = 1 | |
63 except (OSError, IOError), why: | |
64 if why.errno == errno.EEXIST: | |
65 locker = self.testlock() | |
66 if locker: | |
67 raise LockHeld(locker) | |
68 else: | |
69 raise LockUnavailable(why) | |
70 | |
71 def testlock(self): | |
72 '''return id of locker if lock is valid, else None.''' | |
73 # if old-style lock, we cannot tell what machine locker is on. | |
74 # with new-style lock, if locker is on this machine, we can | |
75 # see if locker is alive. if locker is on this machine but | |
76 # not alive, we can safely break lock. | |
77 locker = util.readlock(self.f) | |
78 c = locker.find(':') | |
79 if c == -1: | |
80 return locker | |
81 host = locker[:c] | |
82 if host != self.host: | |
83 return locker | |
45 try: | 84 try: |
46 util.makelock(str(pid), self.f) | 85 pid = int(locker[c+1:]) |
47 self.held = 1 | 86 except: |
48 except (OSError, IOError), why: | 87 return locker |
49 if why.errno == errno.EEXIST: | 88 if util.testpid(pid): |
50 raise LockHeld(util.readlock(self.f)) | 89 return locker |
51 else: | 90 # if locker dead, break lock. must do this with another lock |
52 raise LockUnavailable(why) | 91 # held, or can race and break valid lock. |
92 try: | |
93 l = lock(self.f + '.break') | |
94 l.trylock() | |
95 os.unlink(self.f) | |
96 l.release() | |
97 except (LockHeld, LockUnavailable): | |
98 return locker | |
53 | 99 |
54 def release(self): | 100 def release(self): |
55 if self.held: | 101 if self.held: |
56 self.held = 0 | 102 self.held = 0 |
57 if self.releasefn: | 103 if self.releasefn: |