##// END OF EJS Templates
Don't step into an endless loop when lock file is empty.
Thomas Arendsen Hein -
r3686:4308f4cd default
parent child Browse files
Show More
@@ -1,118 +1,123 b''
1 1 # lock.py - simple locking scheme for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from demandload import *
9 9 demandload(globals(), 'errno os socket time util')
10 10
11 11 class LockException(IOError):
12 12 def __init__(self, errno, strerror, filename, desc):
13 13 IOError.__init__(self, errno, strerror, filename)
14 14 self.desc = desc
15 15
16 16 class LockHeld(LockException):
17 17 def __init__(self, errno, filename, desc, locker):
18 18 LockException.__init__(self, errno, 'Lock held', filename, desc)
19 19 self.locker = locker
20 20
21 21 class LockUnavailable(LockException):
22 22 pass
23 23
24 24 class lock(object):
25 25 # lock is symlink on platforms that support it, file on others.
26 26
27 27 # symlink is used because create of directory entry and contents
28 28 # are atomic even over nfs.
29 29
30 30 # old-style lock: symlink to pid
31 31 # new-style lock: symlink to hostname:pid
32 32
33 33 def __init__(self, file, timeout=-1, releasefn=None, desc=None):
34 34 self.f = file
35 35 self.held = 0
36 36 self.timeout = timeout
37 37 self.releasefn = releasefn
38 38 self.id = None
39 39 self.host = None
40 40 self.pid = None
41 41 self.desc = desc
42 42 self.lock()
43 43
44 44 def __del__(self):
45 45 self.release()
46 46
47 47 def lock(self):
48 48 timeout = self.timeout
49 49 while 1:
50 50 try:
51 51 self.trylock()
52 52 return 1
53 53 except LockHeld, inst:
54 54 if timeout != 0:
55 55 time.sleep(1)
56 56 if timeout > 0:
57 57 timeout -= 1
58 58 continue
59 59 raise LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
60 60 inst.locker)
61 61
62 62 def trylock(self):
63 63 if self.id is None:
64 64 self.host = socket.gethostname()
65 65 self.pid = os.getpid()
66 66 self.id = '%s:%s' % (self.host, self.pid)
67 67 while not self.held:
68 68 try:
69 69 util.makelock(self.id, self.f)
70 70 self.held = 1
71 71 except (OSError, IOError), why:
72 72 if why.errno == errno.EEXIST:
73 73 locker = self.testlock()
74 if locker:
74 if locker is not None:
75 75 raise LockHeld(errno.EAGAIN, self.f, self.desc,
76 76 locker)
77 77 else:
78 78 raise LockUnavailable(why.errno, why.strerror,
79 79 why.filename, self.desc)
80 80
81 81 def testlock(self):
82 '''return id of locker if lock is valid, else None.'''
83 # if old-style lock, we cannot tell what machine locker is on.
84 # with new-style lock, if locker is on this machine, we can
85 # see if locker is alive. if locker is on this machine but
86 # not alive, we can safely break lock.
82 """return id of locker if lock is valid, else None.
83
84 If old-style lock, we cannot tell what machine locker is on.
85 with new-style lock, if locker is on this machine, we can
86 see if locker is alive. If locker is on this machine but
87 not alive, we can safely break lock.
88
89 The lock file is only deleted when None is returned.
90
91 """
87 92 locker = util.readlock(self.f)
88 93 try:
89 94 host, pid = locker.split(":", 1)
90 95 except ValueError:
91 96 return locker
92 97 if host != self.host:
93 98 return locker
94 99 try:
95 100 pid = int(pid)
96 101 except:
97 102 return locker
98 103 if util.testpid(pid):
99 104 return locker
100 105 # if locker dead, break lock. must do this with another lock
101 106 # held, or can race and break valid lock.
102 107 try:
103 108 l = lock(self.f + '.break')
104 109 l.trylock()
105 110 os.unlink(self.f)
106 111 l.release()
107 112 except (LockHeld, LockUnavailable):
108 113 return locker
109 114
110 115 def release(self):
111 116 if self.held:
112 117 self.held = 0
113 118 if self.releasefn:
114 119 self.releasefn()
115 120 try:
116 121 os.unlink(self.f)
117 122 except: pass
118 123
General Comments 0
You need to be logged in to leave comments. Login now