##// END OF EJS Templates
lock.release: don't call postrelease functions for inherited locks...
Siddharth Agarwal -
r26474:431094a3 default
parent child Browse files
Show More
@@ -1,230 +1,233
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 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import contextlib
10 import contextlib
11 import errno
11 import errno
12 import os
12 import os
13 import socket
13 import socket
14 import time
14 import time
15 import warnings
15 import warnings
16
16
17 from . import (
17 from . import (
18 error,
18 error,
19 util,
19 util,
20 )
20 )
21
21
22 class lock(object):
22 class lock(object):
23 '''An advisory lock held by one process to control access to a set
23 '''An advisory lock held by one process to control access to a set
24 of files. Non-cooperating processes or incorrectly written scripts
24 of files. Non-cooperating processes or incorrectly written scripts
25 can ignore Mercurial's locking scheme and stomp all over the
25 can ignore Mercurial's locking scheme and stomp all over the
26 repository, so don't do that.
26 repository, so don't do that.
27
27
28 Typically used via localrepository.lock() to lock the repository
28 Typically used via localrepository.lock() to lock the repository
29 store (.hg/store/) or localrepository.wlock() to lock everything
29 store (.hg/store/) or localrepository.wlock() to lock everything
30 else under .hg/.'''
30 else under .hg/.'''
31
31
32 # lock is symlink on platforms that support it, file on others.
32 # lock is symlink on platforms that support it, file on others.
33
33
34 # symlink is used because create of directory entry and contents
34 # symlink is used because create of directory entry and contents
35 # are atomic even over nfs.
35 # are atomic even over nfs.
36
36
37 # old-style lock: symlink to pid
37 # old-style lock: symlink to pid
38 # new-style lock: symlink to hostname:pid
38 # new-style lock: symlink to hostname:pid
39
39
40 _host = None
40 _host = None
41
41
42 def __init__(self, vfs, file, timeout=-1, releasefn=None, acquirefn=None,
42 def __init__(self, vfs, file, timeout=-1, releasefn=None, acquirefn=None,
43 desc=None, parentlock=None):
43 desc=None, parentlock=None):
44 self.vfs = vfs
44 self.vfs = vfs
45 self.f = file
45 self.f = file
46 self.held = 0
46 self.held = 0
47 self.timeout = timeout
47 self.timeout = timeout
48 self.releasefn = releasefn
48 self.releasefn = releasefn
49 self.acquirefn = acquirefn
49 self.acquirefn = acquirefn
50 self.desc = desc
50 self.desc = desc
51 self.parentlock = parentlock
51 self.parentlock = parentlock
52 self._parentheld = False
52 self._parentheld = False
53 self._inherited = False
53 self._inherited = False
54 self.postrelease = []
54 self.postrelease = []
55 self.pid = self._getpid()
55 self.pid = self._getpid()
56 self.delay = self.lock()
56 self.delay = self.lock()
57 if self.acquirefn:
57 if self.acquirefn:
58 self.acquirefn()
58 self.acquirefn()
59
59
60 def __del__(self):
60 def __del__(self):
61 if self.held:
61 if self.held:
62 warnings.warn("use lock.release instead of del lock",
62 warnings.warn("use lock.release instead of del lock",
63 category=DeprecationWarning,
63 category=DeprecationWarning,
64 stacklevel=2)
64 stacklevel=2)
65
65
66 # ensure the lock will be removed
66 # ensure the lock will be removed
67 # even if recursive locking did occur
67 # even if recursive locking did occur
68 self.held = 1
68 self.held = 1
69
69
70 self.release()
70 self.release()
71
71
72 def _getpid(self):
72 def _getpid(self):
73 # wrapper around os.getpid() to make testing easier
73 # wrapper around os.getpid() to make testing easier
74 return os.getpid()
74 return os.getpid()
75
75
76 def lock(self):
76 def lock(self):
77 timeout = self.timeout
77 timeout = self.timeout
78 while True:
78 while True:
79 try:
79 try:
80 self._trylock()
80 self._trylock()
81 return self.timeout - timeout
81 return self.timeout - timeout
82 except error.LockHeld as inst:
82 except error.LockHeld as inst:
83 if timeout != 0:
83 if timeout != 0:
84 time.sleep(1)
84 time.sleep(1)
85 if timeout > 0:
85 if timeout > 0:
86 timeout -= 1
86 timeout -= 1
87 continue
87 continue
88 raise error.LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
88 raise error.LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
89 inst.locker)
89 inst.locker)
90
90
91 def _trylock(self):
91 def _trylock(self):
92 if self.held:
92 if self.held:
93 self.held += 1
93 self.held += 1
94 return
94 return
95 if lock._host is None:
95 if lock._host is None:
96 lock._host = socket.gethostname()
96 lock._host = socket.gethostname()
97 lockname = '%s:%s' % (lock._host, self.pid)
97 lockname = '%s:%s' % (lock._host, self.pid)
98 retry = 5
98 retry = 5
99 while not self.held and retry:
99 while not self.held and retry:
100 retry -= 1
100 retry -= 1
101 try:
101 try:
102 self.vfs.makelock(lockname, self.f)
102 self.vfs.makelock(lockname, self.f)
103 self.held = 1
103 self.held = 1
104 except (OSError, IOError) as why:
104 except (OSError, IOError) as why:
105 if why.errno == errno.EEXIST:
105 if why.errno == errno.EEXIST:
106 locker = self._readlock()
106 locker = self._readlock()
107 # special case where a parent process holds the lock -- this
107 # special case where a parent process holds the lock -- this
108 # is different from the pid being different because we do
108 # is different from the pid being different because we do
109 # want the unlock and postrelease functions to be called,
109 # want the unlock and postrelease functions to be called,
110 # but the lockfile to not be removed.
110 # but the lockfile to not be removed.
111 if locker == self.parentlock:
111 if locker == self.parentlock:
112 self._parentheld = True
112 self._parentheld = True
113 self.held = 1
113 self.held = 1
114 return
114 return
115 locker = self._testlock(locker)
115 locker = self._testlock(locker)
116 if locker is not None:
116 if locker is not None:
117 raise error.LockHeld(errno.EAGAIN,
117 raise error.LockHeld(errno.EAGAIN,
118 self.vfs.join(self.f), self.desc,
118 self.vfs.join(self.f), self.desc,
119 locker)
119 locker)
120 else:
120 else:
121 raise error.LockUnavailable(why.errno, why.strerror,
121 raise error.LockUnavailable(why.errno, why.strerror,
122 why.filename, self.desc)
122 why.filename, self.desc)
123
123
124 def _readlock(self):
124 def _readlock(self):
125 """read lock and return its value
125 """read lock and return its value
126
126
127 Returns None if no lock exists, pid for old-style locks, and host:pid
127 Returns None if no lock exists, pid for old-style locks, and host:pid
128 for new-style locks.
128 for new-style locks.
129 """
129 """
130 try:
130 try:
131 return self.vfs.readlock(self.f)
131 return self.vfs.readlock(self.f)
132 except (OSError, IOError) as why:
132 except (OSError, IOError) as why:
133 if why.errno == errno.ENOENT:
133 if why.errno == errno.ENOENT:
134 return None
134 return None
135 raise
135 raise
136
136
137 def _testlock(self, locker):
137 def _testlock(self, locker):
138 if locker is None:
138 if locker is None:
139 return None
139 return None
140 try:
140 try:
141 host, pid = locker.split(":", 1)
141 host, pid = locker.split(":", 1)
142 except ValueError:
142 except ValueError:
143 return locker
143 return locker
144 if host != lock._host:
144 if host != lock._host:
145 return locker
145 return locker
146 try:
146 try:
147 pid = int(pid)
147 pid = int(pid)
148 except ValueError:
148 except ValueError:
149 return locker
149 return locker
150 if util.testpid(pid):
150 if util.testpid(pid):
151 return locker
151 return locker
152 # if locker dead, break lock. must do this with another lock
152 # if locker dead, break lock. must do this with another lock
153 # held, or can race and break valid lock.
153 # held, or can race and break valid lock.
154 try:
154 try:
155 l = lock(self.vfs, self.f + '.break', timeout=0)
155 l = lock(self.vfs, self.f + '.break', timeout=0)
156 self.vfs.unlink(self.f)
156 self.vfs.unlink(self.f)
157 l.release()
157 l.release()
158 except error.LockError:
158 except error.LockError:
159 return locker
159 return locker
160
160
161 def testlock(self):
161 def testlock(self):
162 """return id of locker if lock is valid, else None.
162 """return id of locker if lock is valid, else None.
163
163
164 If old-style lock, we cannot tell what machine locker is on.
164 If old-style lock, we cannot tell what machine locker is on.
165 with new-style lock, if locker is on this machine, we can
165 with new-style lock, if locker is on this machine, we can
166 see if locker is alive. If locker is on this machine but
166 see if locker is alive. If locker is on this machine but
167 not alive, we can safely break lock.
167 not alive, we can safely break lock.
168
168
169 The lock file is only deleted when None is returned.
169 The lock file is only deleted when None is returned.
170
170
171 """
171 """
172 locker = self._readlock()
172 locker = self._readlock()
173 return self._testlock(locker)
173 return self._testlock(locker)
174
174
175 @contextlib.contextmanager
175 @contextlib.contextmanager
176 def inherit(self):
176 def inherit(self):
177 """context for the lock to be inherited by a Mercurial subprocess.
177 """context for the lock to be inherited by a Mercurial subprocess.
178
178
179 Yields a string that will be recognized by the lock in the subprocess.
179 Yields a string that will be recognized by the lock in the subprocess.
180 Communicating this string to the subprocess needs to be done separately
180 Communicating this string to the subprocess needs to be done separately
181 -- typically by an environment variable.
181 -- typically by an environment variable.
182 """
182 """
183 if not self.held:
183 if not self.held:
184 raise error.LockInheritanceContractViolation(
184 raise error.LockInheritanceContractViolation(
185 'inherit can only be called while lock is held')
185 'inherit can only be called while lock is held')
186 if self._inherited:
186 if self._inherited:
187 raise error.LockInheritanceContractViolation(
187 raise error.LockInheritanceContractViolation(
188 'inherit cannot be called while lock is already inherited')
188 'inherit cannot be called while lock is already inherited')
189 if self.releasefn:
189 if self.releasefn:
190 self.releasefn()
190 self.releasefn()
191 if self._parentheld:
191 if self._parentheld:
192 lockname = self.parentlock
192 lockname = self.parentlock
193 else:
193 else:
194 lockname = '%s:%s' % (lock._host, self.pid)
194 lockname = '%s:%s' % (lock._host, self.pid)
195 self._inherited = True
195 self._inherited = True
196 try:
196 try:
197 yield lockname
197 yield lockname
198 finally:
198 finally:
199 if self.acquirefn:
199 if self.acquirefn:
200 self.acquirefn()
200 self.acquirefn()
201 self._inherited = False
201 self._inherited = False
202
202
203 def release(self):
203 def release(self):
204 """release the lock and execute callback function if any
204 """release the lock and execute callback function if any
205
205
206 If the lock has been acquired multiple times, the actual release is
206 If the lock has been acquired multiple times, the actual release is
207 delayed to the last release call."""
207 delayed to the last release call."""
208 if self.held > 1:
208 if self.held > 1:
209 self.held -= 1
209 self.held -= 1
210 elif self.held == 1:
210 elif self.held == 1:
211 self.held = 0
211 self.held = 0
212 if self._getpid() != self.pid:
212 if self._getpid() != self.pid:
213 # we forked, and are not the parent
213 # we forked, and are not the parent
214 return
214 return
215 try:
215 try:
216 if self.releasefn:
216 if self.releasefn:
217 self.releasefn()
217 self.releasefn()
218 finally:
218 finally:
219 if not self._parentheld:
219 if not self._parentheld:
220 try:
220 try:
221 self.vfs.unlink(self.f)
221 self.vfs.unlink(self.f)
222 except OSError:
222 except OSError:
223 pass
223 pass
224 for callback in self.postrelease:
224 # The postrelease functions typically assume the lock is not held
225 callback()
225 # at all.
226 if not self._parentheld:
227 for callback in self.postrelease:
228 callback()
226
229
227 def release(*locks):
230 def release(*locks):
228 for lock in locks:
231 for lock in locks:
229 if lock is not None:
232 if lock is not None:
230 lock.release()
233 lock.release()
@@ -1,254 +1,254
1 from __future__ import absolute_import
1 from __future__ import absolute_import
2
2
3 import copy
3 import copy
4 import os
4 import os
5 import silenttestrunner
5 import silenttestrunner
6 import tempfile
6 import tempfile
7 import types
7 import types
8 import unittest
8 import unittest
9
9
10 from mercurial import (
10 from mercurial import (
11 lock,
11 lock,
12 scmutil,
12 scmutil,
13 )
13 )
14
14
15 testlockname = 'testlock'
15 testlockname = 'testlock'
16
16
17 # work around http://bugs.python.org/issue1515
17 # work around http://bugs.python.org/issue1515
18 if types.MethodType not in copy._deepcopy_dispatch:
18 if types.MethodType not in copy._deepcopy_dispatch:
19 def _deepcopy_method(x, memo):
19 def _deepcopy_method(x, memo):
20 return type(x)(x.im_func, copy.deepcopy(x.im_self, memo), x.im_class)
20 return type(x)(x.im_func, copy.deepcopy(x.im_self, memo), x.im_class)
21 copy._deepcopy_dispatch[types.MethodType] = _deepcopy_method
21 copy._deepcopy_dispatch[types.MethodType] = _deepcopy_method
22
22
23 class lockwrapper(lock.lock):
23 class lockwrapper(lock.lock):
24 def __init__(self, pidoffset, *args, **kwargs):
24 def __init__(self, pidoffset, *args, **kwargs):
25 # lock.lock.__init__() calls lock(), so the pidoffset assignment needs
25 # lock.lock.__init__() calls lock(), so the pidoffset assignment needs
26 # to be earlier
26 # to be earlier
27 self._pidoffset = pidoffset
27 self._pidoffset = pidoffset
28 super(lockwrapper, self).__init__(*args, **kwargs)
28 super(lockwrapper, self).__init__(*args, **kwargs)
29 def _getpid(self):
29 def _getpid(self):
30 return os.getpid() + self._pidoffset
30 return os.getpid() + self._pidoffset
31
31
32 class teststate(object):
32 class teststate(object):
33 def __init__(self, testcase, dir, pidoffset=0):
33 def __init__(self, testcase, dir, pidoffset=0):
34 self._testcase = testcase
34 self._testcase = testcase
35 self._acquirecalled = False
35 self._acquirecalled = False
36 self._releasecalled = False
36 self._releasecalled = False
37 self._postreleasecalled = False
37 self._postreleasecalled = False
38 self.vfs = scmutil.vfs(dir, audit=False)
38 self.vfs = scmutil.vfs(dir, audit=False)
39 self._pidoffset = pidoffset
39 self._pidoffset = pidoffset
40
40
41 def makelock(self, *args, **kwargs):
41 def makelock(self, *args, **kwargs):
42 l = lockwrapper(self._pidoffset, self.vfs, testlockname,
42 l = lockwrapper(self._pidoffset, self.vfs, testlockname,
43 releasefn=self.releasefn, acquirefn=self.acquirefn,
43 releasefn=self.releasefn, acquirefn=self.acquirefn,
44 *args, **kwargs)
44 *args, **kwargs)
45 l.postrelease.append(self.postreleasefn)
45 l.postrelease.append(self.postreleasefn)
46 return l
46 return l
47
47
48 def acquirefn(self):
48 def acquirefn(self):
49 self._acquirecalled = True
49 self._acquirecalled = True
50
50
51 def releasefn(self):
51 def releasefn(self):
52 self._releasecalled = True
52 self._releasecalled = True
53
53
54 def postreleasefn(self):
54 def postreleasefn(self):
55 self._postreleasecalled = True
55 self._postreleasecalled = True
56
56
57 def assertacquirecalled(self, called):
57 def assertacquirecalled(self, called):
58 self._testcase.assertEqual(
58 self._testcase.assertEqual(
59 self._acquirecalled, called,
59 self._acquirecalled, called,
60 'expected acquire to be %s but was actually %s' % (
60 'expected acquire to be %s but was actually %s' % (
61 self._tocalled(called),
61 self._tocalled(called),
62 self._tocalled(self._acquirecalled),
62 self._tocalled(self._acquirecalled),
63 ))
63 ))
64
64
65 def resetacquirefn(self):
65 def resetacquirefn(self):
66 self._acquirecalled = False
66 self._acquirecalled = False
67
67
68 def assertreleasecalled(self, called):
68 def assertreleasecalled(self, called):
69 self._testcase.assertEqual(
69 self._testcase.assertEqual(
70 self._releasecalled, called,
70 self._releasecalled, called,
71 'expected release to be %s but was actually %s' % (
71 'expected release to be %s but was actually %s' % (
72 self._tocalled(called),
72 self._tocalled(called),
73 self._tocalled(self._releasecalled),
73 self._tocalled(self._releasecalled),
74 ))
74 ))
75
75
76 def assertpostreleasecalled(self, called):
76 def assertpostreleasecalled(self, called):
77 self._testcase.assertEqual(
77 self._testcase.assertEqual(
78 self._postreleasecalled, called,
78 self._postreleasecalled, called,
79 'expected postrelease to be %s but was actually %s' % (
79 'expected postrelease to be %s but was actually %s' % (
80 self._tocalled(called),
80 self._tocalled(called),
81 self._tocalled(self._postreleasecalled),
81 self._tocalled(self._postreleasecalled),
82 ))
82 ))
83
83
84 def assertlockexists(self, exists):
84 def assertlockexists(self, exists):
85 actual = self.vfs.lexists(testlockname)
85 actual = self.vfs.lexists(testlockname)
86 self._testcase.assertEqual(
86 self._testcase.assertEqual(
87 actual, exists,
87 actual, exists,
88 'expected lock to %s but actually did %s' % (
88 'expected lock to %s but actually did %s' % (
89 self._toexists(exists),
89 self._toexists(exists),
90 self._toexists(actual),
90 self._toexists(actual),
91 ))
91 ))
92
92
93 def _tocalled(self, called):
93 def _tocalled(self, called):
94 if called:
94 if called:
95 return 'called'
95 return 'called'
96 else:
96 else:
97 return 'not called'
97 return 'not called'
98
98
99 def _toexists(self, exists):
99 def _toexists(self, exists):
100 if exists:
100 if exists:
101 return 'exist'
101 return 'exist'
102 else:
102 else:
103 return 'not exist'
103 return 'not exist'
104
104
105 class testlock(unittest.TestCase):
105 class testlock(unittest.TestCase):
106 def testlock(self):
106 def testlock(self):
107 state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
107 state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
108 lock = state.makelock()
108 lock = state.makelock()
109 state.assertacquirecalled(True)
109 state.assertacquirecalled(True)
110 lock.release()
110 lock.release()
111 state.assertreleasecalled(True)
111 state.assertreleasecalled(True)
112 state.assertpostreleasecalled(True)
112 state.assertpostreleasecalled(True)
113 state.assertlockexists(False)
113 state.assertlockexists(False)
114
114
115 def testrecursivelock(self):
115 def testrecursivelock(self):
116 state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
116 state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
117 lock = state.makelock()
117 lock = state.makelock()
118 state.assertacquirecalled(True)
118 state.assertacquirecalled(True)
119
119
120 state.resetacquirefn()
120 state.resetacquirefn()
121 lock.lock()
121 lock.lock()
122 # recursive lock should not call acquirefn again
122 # recursive lock should not call acquirefn again
123 state.assertacquirecalled(False)
123 state.assertacquirecalled(False)
124
124
125 lock.release() # brings lock refcount down from 2 to 1
125 lock.release() # brings lock refcount down from 2 to 1
126 state.assertreleasecalled(False)
126 state.assertreleasecalled(False)
127 state.assertpostreleasecalled(False)
127 state.assertpostreleasecalled(False)
128 state.assertlockexists(True)
128 state.assertlockexists(True)
129
129
130 lock.release() # releases the lock
130 lock.release() # releases the lock
131 state.assertreleasecalled(True)
131 state.assertreleasecalled(True)
132 state.assertpostreleasecalled(True)
132 state.assertpostreleasecalled(True)
133 state.assertlockexists(False)
133 state.assertlockexists(False)
134
134
135 def testlockfork(self):
135 def testlockfork(self):
136 state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
136 state = teststate(self, tempfile.mkdtemp(dir=os.getcwd()))
137 lock = state.makelock()
137 lock = state.makelock()
138 state.assertacquirecalled(True)
138 state.assertacquirecalled(True)
139
139
140 # fake a fork
140 # fake a fork
141 forklock = copy.deepcopy(lock)
141 forklock = copy.deepcopy(lock)
142 forklock._pidoffset = 1
142 forklock._pidoffset = 1
143 forklock.release()
143 forklock.release()
144 state.assertreleasecalled(False)
144 state.assertreleasecalled(False)
145 state.assertpostreleasecalled(False)
145 state.assertpostreleasecalled(False)
146 state.assertlockexists(True)
146 state.assertlockexists(True)
147
147
148 # release the actual lock
148 # release the actual lock
149 lock.release()
149 lock.release()
150 state.assertreleasecalled(True)
150 state.assertreleasecalled(True)
151 state.assertpostreleasecalled(True)
151 state.assertpostreleasecalled(True)
152 state.assertlockexists(False)
152 state.assertlockexists(False)
153
153
154 def testinheritlock(self):
154 def testinheritlock(self):
155 d = tempfile.mkdtemp(dir=os.getcwd())
155 d = tempfile.mkdtemp(dir=os.getcwd())
156 parentstate = teststate(self, d)
156 parentstate = teststate(self, d)
157 parentlock = parentstate.makelock()
157 parentlock = parentstate.makelock()
158 parentstate.assertacquirecalled(True)
158 parentstate.assertacquirecalled(True)
159
159
160 # set up lock inheritance
160 # set up lock inheritance
161 with parentlock.inherit() as lockname:
161 with parentlock.inherit() as lockname:
162 parentstate.assertreleasecalled(True)
162 parentstate.assertreleasecalled(True)
163 parentstate.assertpostreleasecalled(False)
163 parentstate.assertpostreleasecalled(False)
164 parentstate.assertlockexists(True)
164 parentstate.assertlockexists(True)
165
165
166 childstate = teststate(self, d, pidoffset=1)
166 childstate = teststate(self, d, pidoffset=1)
167 childlock = childstate.makelock(parentlock=lockname)
167 childlock = childstate.makelock(parentlock=lockname)
168 childstate.assertacquirecalled(True)
168 childstate.assertacquirecalled(True)
169
169
170 childlock.release()
170 childlock.release()
171 childstate.assertreleasecalled(True)
171 childstate.assertreleasecalled(True)
172 childstate.assertpostreleasecalled(True)
172 childstate.assertpostreleasecalled(False)
173 childstate.assertlockexists(True)
173 childstate.assertlockexists(True)
174
174
175 parentstate.resetacquirefn()
175 parentstate.resetacquirefn()
176
176
177 parentstate.assertacquirecalled(True)
177 parentstate.assertacquirecalled(True)
178
178
179 parentlock.release()
179 parentlock.release()
180 parentstate.assertreleasecalled(True)
180 parentstate.assertreleasecalled(True)
181 parentstate.assertpostreleasecalled(True)
181 parentstate.assertpostreleasecalled(True)
182 parentstate.assertlockexists(False)
182 parentstate.assertlockexists(False)
183
183
184 def testmultilock(self):
184 def testmultilock(self):
185 d = tempfile.mkdtemp(dir=os.getcwd())
185 d = tempfile.mkdtemp(dir=os.getcwd())
186 state0 = teststate(self, d)
186 state0 = teststate(self, d)
187 lock0 = state0.makelock()
187 lock0 = state0.makelock()
188 state0.assertacquirecalled(True)
188 state0.assertacquirecalled(True)
189
189
190 with lock0.inherit() as lock0name:
190 with lock0.inherit() as lock0name:
191 state0.assertreleasecalled(True)
191 state0.assertreleasecalled(True)
192 state0.assertpostreleasecalled(False)
192 state0.assertpostreleasecalled(False)
193 state0.assertlockexists(True)
193 state0.assertlockexists(True)
194
194
195 state1 = teststate(self, d, pidoffset=1)
195 state1 = teststate(self, d, pidoffset=1)
196 lock1 = state1.makelock(parentlock=lock0name)
196 lock1 = state1.makelock(parentlock=lock0name)
197 state1.assertacquirecalled(True)
197 state1.assertacquirecalled(True)
198
198
199 # from within lock1, acquire another lock
199 # from within lock1, acquire another lock
200 with lock1.inherit() as lock1name:
200 with lock1.inherit() as lock1name:
201 # since the file on disk is lock0's this should have the same
201 # since the file on disk is lock0's this should have the same
202 # name
202 # name
203 self.assertEqual(lock0name, lock1name)
203 self.assertEqual(lock0name, lock1name)
204
204
205 state2 = teststate(self, d, pidoffset=2)
205 state2 = teststate(self, d, pidoffset=2)
206 lock2 = state2.makelock(parentlock=lock1name)
206 lock2 = state2.makelock(parentlock=lock1name)
207 state2.assertacquirecalled(True)
207 state2.assertacquirecalled(True)
208
208
209 lock2.release()
209 lock2.release()
210 state2.assertreleasecalled(True)
210 state2.assertreleasecalled(True)
211 state2.assertpostreleasecalled(True)
211 state2.assertpostreleasecalled(False)
212 state2.assertlockexists(True)
212 state2.assertlockexists(True)
213
213
214 state1.resetacquirefn()
214 state1.resetacquirefn()
215
215
216 state1.assertacquirecalled(True)
216 state1.assertacquirecalled(True)
217
217
218 lock1.release()
218 lock1.release()
219 state1.assertreleasecalled(True)
219 state1.assertreleasecalled(True)
220 state1.assertpostreleasecalled(True)
220 state1.assertpostreleasecalled(False)
221 state1.assertlockexists(True)
221 state1.assertlockexists(True)
222
222
223 lock0.release()
223 lock0.release()
224
224
225 def testinheritlockfork(self):
225 def testinheritlockfork(self):
226 d = tempfile.mkdtemp(dir=os.getcwd())
226 d = tempfile.mkdtemp(dir=os.getcwd())
227 parentstate = teststate(self, d)
227 parentstate = teststate(self, d)
228 parentlock = parentstate.makelock()
228 parentlock = parentstate.makelock()
229 parentstate.assertacquirecalled(True)
229 parentstate.assertacquirecalled(True)
230
230
231 # set up lock inheritance
231 # set up lock inheritance
232 with parentlock.inherit() as lockname:
232 with parentlock.inherit() as lockname:
233 childstate = teststate(self, d, pidoffset=1)
233 childstate = teststate(self, d, pidoffset=1)
234 childlock = childstate.makelock(parentlock=lockname)
234 childlock = childstate.makelock(parentlock=lockname)
235 childstate.assertacquirecalled(True)
235 childstate.assertacquirecalled(True)
236
236
237 # fork the child lock
237 # fork the child lock
238 forkchildlock = copy.deepcopy(childlock)
238 forkchildlock = copy.deepcopy(childlock)
239 forkchildlock._pidoffset += 1
239 forkchildlock._pidoffset += 1
240 forkchildlock.release()
240 forkchildlock.release()
241 childstate.assertreleasecalled(False)
241 childstate.assertreleasecalled(False)
242 childstate.assertpostreleasecalled(False)
242 childstate.assertpostreleasecalled(False)
243 childstate.assertlockexists(True)
243 childstate.assertlockexists(True)
244
244
245 # release the child lock
245 # release the child lock
246 childlock.release()
246 childlock.release()
247 childstate.assertreleasecalled(True)
247 childstate.assertreleasecalled(True)
248 childstate.assertpostreleasecalled(True)
248 childstate.assertpostreleasecalled(False)
249 childstate.assertlockexists(True)
249 childstate.assertlockexists(True)
250
250
251 parentlock.release()
251 parentlock.release()
252
252
253 if __name__ == '__main__':
253 if __name__ == '__main__':
254 silenttestrunner.main(__name__)
254 silenttestrunner.main(__name__)
General Comments 0
You need to be logged in to leave comments. Login now