From 8e7effdaefaddc5b61ba0aa6ff8e94f1422b6092 Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Sun, 18 Aug 2013 23:54:59 -0400 Subject: [PATCH] In add_callback, hold the lock while writing to the waker pipe. This protects against a shutdown race condition seen occasionally in the ThreadedResolver unittests. This slightly increases contention on the callback lock in multi-threaded scenarios, but the cost is limited by the fact that we only write to the pipe once per IOLoop iteration. Closes #875. --- tornado/ioloop.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tornado/ioloop.py b/tornado/ioloop.py index c9f2e47c..a36ab7a5 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -722,14 +722,14 @@ class PollIOLoop(IOLoop): list_empty = not self._callbacks self._callbacks.append(functools.partial( stack_context.wrap(callback), *args, **kwargs)) - if list_empty and thread.get_ident() != self._thread_ident: - # If we're in the IOLoop's thread, we know it's not currently - # polling. If we're not, and we added the first callback to an - # empty list, we may need to wake it up (it may wake up on its - # own, but an occasional extra wake is harmless). Waking - # up a polling IOLoop is relatively expensive, so we try to - # avoid it when we can. - self._waker.wake() + if list_empty and thread.get_ident() != self._thread_ident: + # If we're in the IOLoop's thread, we know it's not currently + # polling. If we're not, and we added the first callback to an + # empty list, we may need to wake it up (it may wake up on its + # own, but an occasional extra wake is harmless). Waking + # up a polling IOLoop is relatively expensive, so we try to + # avoid it when we can. + self._waker.wake() def add_callback_from_signal(self, callback, *args, **kwargs): with stack_context.NullContext():