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