@@ -44,20 +44,23 @@ def ensure_running(self):
4444 This can be run from any process. Usually a child process will use
4545 the semaphore created by its parent.'''
4646 with self ._lock :
47- if self ._pid is not None :
47+ if self ._fd is not None :
4848 # semaphore tracker was launched before, is it still running?
49+ if self ._check_alive ():
50+ # => still alive
51+ return
52+ # => dead, launch it again
53+ os .close (self ._fd )
54+
55+ # Clean-up to avoid dangling processes.
4956 try :
50- pid , _ = os .waitpid (self ._pid , os .WNOHANG )
57+ # _pid can be None if this process is a child from another
58+ # python process, which has started the semaphore_tracker.
59+ if self ._pid is not None :
60+ os .waitpid (self ._pid , 0 )
5161 except ChildProcessError :
52- # The process terminated
62+ # The semaphore_tracker has already been terminated.
5363 pass
54- else :
55- if not pid :
56- # => still alive
57- return
58-
59- # => dead, launch it again
60- os .close (self ._fd )
6164 self ._fd = None
6265 self ._pid = None
6366
@@ -99,6 +102,17 @@ def ensure_running(self):
99102 finally :
100103 os .close (r )
101104
105+ def _check_alive (self ):
106+ '''Check that the pipe has not been closed by sending a probe.'''
107+ try :
108+ # We cannot use send here as it calls ensure_running, creating
109+ # a cycle.
110+ os .write (self ._fd , b'PROBE:0\n ' )
111+ except OSError :
112+ return False
113+ else :
114+ return True
115+
102116 def register (self , name ):
103117 '''Register name of semaphore with semaphore tracker.'''
104118 self ._send ('REGISTER' , name )
@@ -150,6 +164,8 @@ def main(fd):
150164 cache .add (name )
151165 elif cmd == b'UNREGISTER' :
152166 cache .remove (name )
167+ elif cmd == b'PROBE' :
168+ pass
153169 else :
154170 raise RuntimeError ('unrecognized command %r' % cmd )
155171 except Exception :
0 commit comments