Skip to content

Commit 0c66c0c

Browse files
committed
Handle race where process exits immediately in signal()
1 parent d0026ae commit 0c66c0c

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

supervisor/process.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,18 @@ def signal(self, sig):
512512
ProcessStates.STOPPING)
513513

514514
try:
515-
options.kill(self.pid, sig)
515+
try:
516+
options.kill(self.pid, sig)
517+
except OSError as exc:
518+
if exc.errno == errno.ESRCH:
519+
msg = ("unable to signal %s (pid %s), it probably just now exited "
520+
"on its own: %s" % (processname, self.pid,
521+
str(exc)))
522+
options.logger.debug(msg)
523+
# we could change the state here but we intentionally do
524+
# not. we will do it during normal SIGCHLD processing.
525+
return None
526+
raise
516527
except:
517528
tb = traceback.format_exc()
518529
msg = 'unknown problem sending sig %s (%s):%s' % (

supervisor/tests/test_process.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,29 @@ def test_signal_from_running(self):
10041004
self.assertTrue(instance.pid in options.kills)
10051005
self.assertEqual(options.kills[instance.pid], signal.SIGWINCH)
10061006

1007+
def test_signal_from_running_error_ESRCH(self):
1008+
options = DummyOptions()
1009+
config = DummyPConfig(options, 'test', '/test')
1010+
options.kill_exception = OSError(errno.ESRCH,
1011+
os.strerror(errno.ESRCH))
1012+
instance = self._makeOne(config)
1013+
L = []
1014+
from supervisor.states import ProcessStates
1015+
from supervisor import events
1016+
events.subscribe(events.ProcessStateEvent, lambda x: L.append(x))
1017+
instance.pid = 11
1018+
instance.state = ProcessStates.RUNNING
1019+
instance.signal(signal.SIGWINCH)
1020+
self.assertEqual(options.logger.data[0],
1021+
'sending test (pid 11) sig SIGWINCH')
1022+
self.assertEqual(options.logger.data[1], 'unable to signal test (pid 11), '
1023+
'it probably just now exited on its own: %s' %
1024+
str(options.kill_exception))
1025+
self.assertFalse(instance.killing)
1026+
self.assertEqual(instance.state, ProcessStates.RUNNING) # unchanged
1027+
self.assertEqual(instance.pid, 11) # unchanged
1028+
self.assertEqual(len(L), 0)
1029+
10071030
def test_signal_from_running_error(self):
10081031
options = DummyOptions()
10091032
config = DummyPConfig(options, 'test', '/test')

0 commit comments

Comments
 (0)