Merge "Fix race condition on handling signals"

This commit is contained in:
Jenkins 2016-02-01 07:09:39 +00:00 committed by Gerrit Code Review
commit 62f5462288
2 changed files with 22 additions and 4 deletions

View File

@ -153,9 +153,25 @@ class SignalHandler(object):
return
signo = self._signals_by_name[sig]
self._signal_handlers[signo].add(handler)
signal.signal(signo, self._handle_signals)
signal.signal(signo, self._handle_signal)
def _handle_signals(self, signo, frame):
def _handle_signal(self, signo, frame):
# This method can be called anytime, even between two Python
# instructions. It's scheduled by the C signal handler of Python using
# Py_AddPendingCall().
#
# We only do one thing: schedule a call to _handle_signal_cb() later.
# eventlet.spawn() is not signal-safe: _handle_signal() can be called
# during a call to eventlet.spawn(). This case is supported, it is
# ok to schedule multiple calls to _handle_signal() with the same
# signal number.
#
# To call to _handle_signal_cb() is delayed to avoid reentrant calls to
# _handle_signal_cb(). It avoids race conditions like reentrant call to
# clear(): clear() is not reentrant (bug #1538204).
eventlet.spawn(self._handle_signal_cb, signo, frame)
def _handle_signal_cb(self, signo, frame):
for handler in self._signal_handlers[signo]:
handler(signo, frame)

View File

@ -402,7 +402,7 @@ class ProcessLauncherTest(base.ServiceBaseTestCase):
mock_kill.mock_calls)
mock_service_stop.assert_called_once_with()
def test__handle_signals(self):
def test__handle_signal(self):
signal_handler = service.SignalHandler()
signal_handler.clear()
self.assertEqual(0,
@ -412,7 +412,9 @@ class ProcessLauncherTest(base.ServiceBaseTestCase):
signal_handler.add_handler('SIGTERM', call_2)
self.assertEqual(2,
len(signal_handler._signal_handlers[signal.SIGTERM]))
signal_handler._handle_signals(signal.SIGTERM, 'test')
signal_handler._handle_signal(signal.SIGTERM, 'test')
# execute pending eventlet callbacks
time.sleep(0)
for m in signal_handler._signal_handlers[signal.SIGTERM]:
m.assert_called_once_with(signal.SIGTERM, 'test')
signal_handler.clear()