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