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