Show More
@@ -6,7 +6,7 b'' | |||
|
6 | 6 | # of the GNU General Public License, incorporated herein by reference. |
|
7 | 7 | |
|
8 | 8 | from demandload import * |
|
9 | demandload(globals(), 'errno os time util') | |
|
9 | demandload(globals(), 'errno os socket time util') | |
|
10 | 10 | |
|
11 | 11 | class LockException(Exception): |
|
12 | 12 | pass |
@@ -16,11 +16,22 b' class LockUnavailable(LockException):' | |||
|
16 | 16 | pass |
|
17 | 17 | |
|
18 | 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 | 27 | def __init__(self, file, timeout=-1, releasefn=None): |
|
20 | 28 | self.f = file |
|
21 | 29 | self.held = 0 |
|
22 | 30 | self.timeout = timeout |
|
23 | 31 | self.releasefn = releasefn |
|
32 | self.id = None | |
|
33 | self.host = None | |
|
34 | self.pid = None | |
|
24 | 35 | self.lock() |
|
25 | 36 | |
|
26 | 37 | def __del__(self): |
@@ -41,15 +52,50 b' class lock(object):' | |||
|
41 | 52 | raise inst |
|
42 | 53 | |
|
43 | 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 | 84 | try: |
|
46 | util.makelock(str(pid), self.f) | |
|
47 | self.held = 1 | |
|
48 | except (OSError, IOError), why: | |
|
49 | if why.errno == errno.EEXIST: | |
|
50 | raise LockHeld(util.readlock(self.f)) | |
|
51 | else: | |
|
52 | raise LockUnavailable(why) | |
|
85 | pid = int(locker[c+1:]) | |
|
86 | except: | |
|
87 | return locker | |
|
88 | if util.testpid(pid): | |
|
89 | return locker | |
|
90 | # if locker dead, break lock. must do this with another lock | |
|
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 | 100 | def release(self): |
|
55 | 101 | if self.held: |
@@ -499,7 +499,7 b" if os.name == 'nt':" | |||
|
499 | 499 | return pf |
|
500 | 500 | |
|
501 | 501 | try: # ActivePython can create hard links using win32file module |
|
502 | import win32file | |
|
502 | import win32api, win32con, win32file | |
|
503 | 503 | |
|
504 | 504 | def os_link(src, dst): # NB will only succeed on NTFS |
|
505 | 505 | win32file.CreateHardLink(dst, src) |
@@ -516,8 +516,18 b" if os.name == 'nt':" | |||
|
516 | 516 | except: |
|
517 | 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 | 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 | 532 | def is_exec(f, last): |
|
523 | 533 | return last |
@@ -614,6 +624,14 b' else:' | |||
|
614 | 624 | else: |
|
615 | 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 | 635 | def explain_exit(code): |
|
618 | 636 | """return a 2-tuple (desc, code) describing a process's status""" |
|
619 | 637 | if os.WIFEXITED(code): |
General Comments 0
You need to be logged in to leave comments.
Login now