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