##// END OF EJS Templates
change lock format to let us detect and break stale locks....
Vadim Gelfer -
r1877:d314a89f default
parent child Browse files
Show More
@@ -6,7 +6,7 b''
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 time util')
9 demandload(globals(), 'errno os socket time util')
10
10
11 class LockException(Exception):
11 class LockException(Exception):
12 pass
12 pass
@@ -16,11 +16,22 b' class LockUnavailable(LockException):'
16 pass
16 pass
17
17
18 class lock(object):
18 class lock(object):
19 # lock is symlink on platforms that support it, file on others.
20
21 # symlink is used because create of directory entry and contents
22 # are atomic even over nfs.
23
24 # old-style lock: symlink to pid
25 # new-style lock: symlink to hostname:pid
26
19 def __init__(self, file, timeout=-1, releasefn=None):
27 def __init__(self, file, timeout=-1, releasefn=None):
20 self.f = file
28 self.f = file
21 self.held = 0
29 self.held = 0
22 self.timeout = timeout
30 self.timeout = timeout
23 self.releasefn = releasefn
31 self.releasefn = releasefn
32 self.id = None
33 self.host = None
34 self.pid = None
24 self.lock()
35 self.lock()
25
36
26 def __del__(self):
37 def __del__(self):
@@ -41,15 +52,50 b' class lock(object):'
41 raise inst
52 raise inst
42
53
43 def trylock(self):
54 def trylock(self):
44 pid = os.getpid()
55 if self.id is None:
56 self.host = socket.gethostname()
57 self.pid = os.getpid()
58 self.id = '%s:%s' % (self.host, self.pid)
59 while not self.held:
60 try:
61 util.makelock(self.id, self.f)
62 self.held = 1
63 except (OSError, IOError), why:
64 if why.errno == errno.EEXIST:
65 locker = self.testlock()
66 if locker:
67 raise LockHeld(locker)
68 else:
69 raise LockUnavailable(why)
70
71 def testlock(self):
72 '''return id of locker if lock is valid, else None.'''
73 # if old-style lock, we cannot tell what machine locker is on.
74 # with new-style lock, if locker is on this machine, we can
75 # see if locker is alive. if locker is on this machine but
76 # not alive, we can safely break lock.
77 locker = util.readlock(self.f)
78 c = locker.find(':')
79 if c == -1:
80 return locker
81 host = locker[:c]
82 if host != self.host:
83 return locker
45 try:
84 try:
46 util.makelock(str(pid), self.f)
85 pid = int(locker[c+1:])
47 self.held = 1
86 except:
48 except (OSError, IOError), why:
87 return locker
49 if why.errno == errno.EEXIST:
88 if util.testpid(pid):
50 raise LockHeld(util.readlock(self.f))
89 return locker
51 else:
90 # if locker dead, break lock. must do this with another lock
52 raise LockUnavailable(why)
91 # held, or can race and break valid lock.
92 try:
93 l = lock(self.f + '.break')
94 l.trylock()
95 os.unlink(self.f)
96 l.release()
97 except (LockHeld, LockUnavailable):
98 return locker
53
99
54 def release(self):
100 def release(self):
55 if self.held:
101 if self.held:
@@ -499,7 +499,7 b" if os.name == 'nt':"
499 return pf
499 return pf
500
500
501 try: # ActivePython can create hard links using win32file module
501 try: # ActivePython can create hard links using win32file module
502 import win32file
502 import win32api, win32con, win32file
503
503
504 def os_link(src, dst): # NB will only succeed on NTFS
504 def os_link(src, dst): # NB will only succeed on NTFS
505 win32file.CreateHardLink(dst, src)
505 win32file.CreateHardLink(dst, src)
@@ -516,8 +516,18 b" if os.name == 'nt':"
516 except:
516 except:
517 return os.stat(pathname).st_nlink
517 return os.stat(pathname).st_nlink
518
518
519 def testpid(pid):
520 '''return False if pid is dead, True if running or not known'''
521 try:
522 win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION,
523 False, pid)
524 except:
525 return True
526
519 except ImportError:
527 except ImportError:
520 pass
528 def testpid(pid):
529 '''return False if pid dead, True if running or not known'''
530 return True
521
531
522 def is_exec(f, last):
532 def is_exec(f, last):
523 return last
533 return last
@@ -614,6 +624,14 b' else:'
614 else:
624 else:
615 raise
625 raise
616
626
627 def testpid(pid):
628 '''return False if pid dead, True if running or not sure'''
629 try:
630 os.kill(pid, 0)
631 return True
632 except OSError, inst:
633 return inst.errno != errno.ESRCH
634
617 def explain_exit(code):
635 def explain_exit(code):
618 """return a 2-tuple (desc, code) describing a process's status"""
636 """return a 2-tuple (desc, code) describing a process's status"""
619 if os.WIFEXITED(code):
637 if os.WIFEXITED(code):
General Comments 0
You need to be logged in to leave comments. Login now