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