##// END OF EJS Templates
Don't step into an endless loop when lock file is empty.
Thomas Arendsen Hein -
r3686:4308f4cd default
parent child Browse files
Show More
@@ -1,118 +1,123 b''
1 # lock.py - simple locking scheme for mercurial
1 # lock.py - simple 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
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import *
8 from demandload import *
9 demandload(globals(), 'errno os socket time util')
9 demandload(globals(), 'errno os socket time util')
10
10
11 class LockException(IOError):
11 class LockException(IOError):
12 def __init__(self, errno, strerror, filename, desc):
12 def __init__(self, errno, strerror, filename, desc):
13 IOError.__init__(self, errno, strerror, filename)
13 IOError.__init__(self, errno, strerror, filename)
14 self.desc = desc
14 self.desc = desc
15
15
16 class LockHeld(LockException):
16 class LockHeld(LockException):
17 def __init__(self, errno, filename, desc, locker):
17 def __init__(self, errno, filename, desc, locker):
18 LockException.__init__(self, errno, 'Lock held', filename, desc)
18 LockException.__init__(self, errno, 'Lock held', filename, desc)
19 self.locker = locker
19 self.locker = locker
20
20
21 class LockUnavailable(LockException):
21 class LockUnavailable(LockException):
22 pass
22 pass
23
23
24 class lock(object):
24 class lock(object):
25 # lock is symlink on platforms that support it, file on others.
25 # lock is symlink on platforms that support it, file on others.
26
26
27 # symlink is used because create of directory entry and contents
27 # symlink is used because create of directory entry and contents
28 # are atomic even over nfs.
28 # are atomic even over nfs.
29
29
30 # old-style lock: symlink to pid
30 # old-style lock: symlink to pid
31 # new-style lock: symlink to hostname:pid
31 # new-style lock: symlink to hostname:pid
32
32
33 def __init__(self, file, timeout=-1, releasefn=None, desc=None):
33 def __init__(self, file, timeout=-1, releasefn=None, desc=None):
34 self.f = file
34 self.f = file
35 self.held = 0
35 self.held = 0
36 self.timeout = timeout
36 self.timeout = timeout
37 self.releasefn = releasefn
37 self.releasefn = releasefn
38 self.id = None
38 self.id = None
39 self.host = None
39 self.host = None
40 self.pid = None
40 self.pid = None
41 self.desc = desc
41 self.desc = desc
42 self.lock()
42 self.lock()
43
43
44 def __del__(self):
44 def __del__(self):
45 self.release()
45 self.release()
46
46
47 def lock(self):
47 def lock(self):
48 timeout = self.timeout
48 timeout = self.timeout
49 while 1:
49 while 1:
50 try:
50 try:
51 self.trylock()
51 self.trylock()
52 return 1
52 return 1
53 except LockHeld, inst:
53 except LockHeld, inst:
54 if timeout != 0:
54 if timeout != 0:
55 time.sleep(1)
55 time.sleep(1)
56 if timeout > 0:
56 if timeout > 0:
57 timeout -= 1
57 timeout -= 1
58 continue
58 continue
59 raise LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
59 raise LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
60 inst.locker)
60 inst.locker)
61
61
62 def trylock(self):
62 def trylock(self):
63 if self.id is None:
63 if self.id is None:
64 self.host = socket.gethostname()
64 self.host = socket.gethostname()
65 self.pid = os.getpid()
65 self.pid = os.getpid()
66 self.id = '%s:%s' % (self.host, self.pid)
66 self.id = '%s:%s' % (self.host, self.pid)
67 while not self.held:
67 while not self.held:
68 try:
68 try:
69 util.makelock(self.id, self.f)
69 util.makelock(self.id, self.f)
70 self.held = 1
70 self.held = 1
71 except (OSError, IOError), why:
71 except (OSError, IOError), why:
72 if why.errno == errno.EEXIST:
72 if why.errno == errno.EEXIST:
73 locker = self.testlock()
73 locker = self.testlock()
74 if locker:
74 if locker is not None:
75 raise LockHeld(errno.EAGAIN, self.f, self.desc,
75 raise LockHeld(errno.EAGAIN, self.f, self.desc,
76 locker)
76 locker)
77 else:
77 else:
78 raise LockUnavailable(why.errno, why.strerror,
78 raise LockUnavailable(why.errno, why.strerror,
79 why.filename, self.desc)
79 why.filename, self.desc)
80
80
81 def testlock(self):
81 def testlock(self):
82 '''return id of locker if lock is valid, else None.'''
82 """return id of locker if lock is valid, else None.
83 # if old-style lock, we cannot tell what machine locker is on.
83
84 # with new-style lock, if locker is on this machine, we can
84 If old-style lock, we cannot tell what machine locker is on.
85 # see if locker is alive. if locker is on this machine but
85 with new-style lock, if locker is on this machine, we can
86 # not alive, we can safely break lock.
86 see if locker is alive. If locker is on this machine but
87 not alive, we can safely break lock.
88
89 The lock file is only deleted when None is returned.
90
91 """
87 locker = util.readlock(self.f)
92 locker = util.readlock(self.f)
88 try:
93 try:
89 host, pid = locker.split(":", 1)
94 host, pid = locker.split(":", 1)
90 except ValueError:
95 except ValueError:
91 return locker
96 return locker
92 if host != self.host:
97 if host != self.host:
93 return locker
98 return locker
94 try:
99 try:
95 pid = int(pid)
100 pid = int(pid)
96 except:
101 except:
97 return locker
102 return locker
98 if util.testpid(pid):
103 if util.testpid(pid):
99 return locker
104 return locker
100 # if locker dead, break lock. must do this with another lock
105 # if locker dead, break lock. must do this with another lock
101 # held, or can race and break valid lock.
106 # held, or can race and break valid lock.
102 try:
107 try:
103 l = lock(self.f + '.break')
108 l = lock(self.f + '.break')
104 l.trylock()
109 l.trylock()
105 os.unlink(self.f)
110 os.unlink(self.f)
106 l.release()
111 l.release()
107 except (LockHeld, LockUnavailable):
112 except (LockHeld, LockUnavailable):
108 return locker
113 return locker
109
114
110 def release(self):
115 def release(self):
111 if self.held:
116 if self.held:
112 self.held = 0
117 self.held = 0
113 if self.releasefn:
118 if self.releasefn:
114 self.releasefn()
119 self.releasefn()
115 try:
120 try:
116 os.unlink(self.f)
121 os.unlink(self.f)
117 except: pass
122 except: pass
118
123
General Comments 0
You need to be logged in to leave comments. Login now