import logging log = logging.getLogger() MIN_DELAY = 0.0 MAX_DELAY = 0.01 DELAY_INCREMENT = 0.0001 QUEUE_SIZE_THRESHOLD = 100 class CallLaterManager(object): _callLater = None _pendingCallLaters = [] _delay = MIN_DELAY @classmethod def get_min_delay(cls): cls._pendingCallLaters = [cl for cl in cls._pendingCallLaters if cl.active()] queue_size = len(cls._pendingCallLaters) if queue_size > QUEUE_SIZE_THRESHOLD: cls._delay = min((cls._delay + DELAY_INCREMENT), MAX_DELAY) else: cls._delay = max((cls._delay - 2.0 * DELAY_INCREMENT), MIN_DELAY) return cls._delay @classmethod def _cancel(cls, call_later): """ :param call_later: DelayedCall :return: (callable) canceller function """ def cancel(reason=None): """ :param reason: reason for cancellation, this is returned after cancelling the DelayedCall :return: reason """ if call_later.active(): call_later.cancel() cls._pendingCallLaters.remove(call_later) return reason return cancel @classmethod def stop(cls): """ Cancel any callLaters that are still running """ from twisted.internet import defer while cls._pendingCallLaters: canceller = cls._cancel(cls._pendingCallLaters[0]) try: canceller() except (defer.CancelledError, defer.AlreadyCalledError): pass @classmethod def call_later(cls, when, what, *args, **kwargs): """ Schedule a call later and get a canceller callback function :param when: (float) delay in seconds :param what: (callable) :param args: (*tuple) args to be passed to the callable :param kwargs: (**dict) kwargs to be passed to the callable :return: (tuple) twisted.internet.base.DelayedCall object, canceller function """ call_later = cls._callLater(when, what, *args, **kwargs) canceller = cls._cancel(call_later) cls._pendingCallLaters.append(call_later) return call_later, canceller @classmethod def call_soon(cls, what, *args, **kwargs): delay = cls.get_min_delay() return cls.call_later(delay, what, *args, **kwargs) @classmethod def setup(cls, callLater): """ Setup the callLater function to use, supports the real reactor as well as task.Clock :param callLater: (IReactorTime.callLater) """ cls._callLater = callLater