From 5fc96e6fd4184828445a73a2a3befac04a2a53f4 Mon Sep 17 00:00:00 2001 From: drewbrew Date: Tue, 3 Jun 2014 13:22:08 -0500 Subject: [PATCH] ioloop.py: use itertools.count() as tiebreaker to preserve FIFO in case of tie Current implementation does not function as a FIFO. When adding multiple timeouts with the same deadline, order is currently consistently mangled in the event of a garbage collect. As suggested by Ben Darnell at https://groups.google.com/forum/#!topic/python-tornado/w8aKm6ZabUQ/discussion, we need to add a sequence number to the Timeout class to serve as tiebreaker. This code uses the model suggested by https://docs.python.org/2/library/heapq.html#priority-queue-implementation-notes, which uses itertools.count() to serve as our counter. --- tornado/ioloop.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tornado/ioloop.py b/tornado/ioloop.py index 86c4a9f22..cd59bfee0 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -32,6 +32,7 @@ import datetime import errno import functools import heapq +import itertools import logging import numbers import os @@ -585,7 +586,8 @@ class PollIOLoop(IOLoop): self._closing = False self._thread_ident = None self._blocking_signal_threshold = None - + self._timeout_counter = itertools.count() + # Create a pipe that we send bogus data to when we want to wake # the I/O loop when it is idle self._waker = Waker() @@ -835,7 +837,7 @@ class _Timeout(object): """An IOLoop timeout, a UNIX timestamp and a callback""" # Reduce memory overhead when there are lots of pending callbacks - __slots__ = ['deadline', 'callback'] + __slots__ = ['deadline', 'callback', 'tiebreaker'] def __init__(self, deadline, callback, io_loop): if isinstance(deadline, numbers.Real): @@ -849,6 +851,7 @@ class _Timeout(object): else: raise TypeError("Unsupported deadline %r" % deadline) self.callback = callback + self.tiebreaker = next(io_loop._timeout_counter) @staticmethod def timedelta_to_seconds(td): @@ -860,12 +863,12 @@ class _Timeout(object): # in python2.5, and __lt__ in 2.6+ (sort() and most other comparisons # use __lt__). def __lt__(self, other): - return ((self.deadline, id(self)) < - (other.deadline, id(other))) + return ((self.deadline, self.tiebreaker) < + (other.deadline, other.tiebreaker)) def __le__(self, other): - return ((self.deadline, id(self)) <= - (other.deadline, id(other))) + return ((self.deadline, self.tiebreaker) <= + (other.deadline, other.tiebreaker)) class PeriodicCallback(object): -- 2.47.2