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