self._cancellations += 1
def add_callback(self, callback, *args, **kwargs):
+ # The check doesn't need to be guarded by the callback lock,
+ # since the GIL makes all access to it atomic, and it can
+ # only ever transition to True
+ if self._closing:
+ raise RuntimeError("IOLoop is closing")
if thread.get_ident() != self._thread_ident:
+ # If we're not on the IOLoop's thread, we need to synchronize
+ # with other threads, or waking logic will induce a race.
with self._callback_lock:
- if self._closing:
- raise RuntimeError("IOLoop is closing")
list_empty = not self._callbacks
self._callbacks.append(functools.partial(
stack_context.wrap(callback), *args, **kwargs))
if list_empty:
- # 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.
+ # If we're not in the IOLoop's thread, 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()
else:
# If we're on the IOLoop's thread, we don't need the lock,