Use time.monotonic() which is available in Python 3.3 and later, and otherwise emulate it by using an offset to counteract any backward movements.
Bug: https://bugs.gentoo.org/591760 --- pym/portage/util/_eventloop/EventLoop.py | 19 ++++++++++++++----- pym/portage/util/monotonic.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 pym/portage/util/monotonic.py diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py index 89ac2a3b3..f472a3dae 100644 --- a/pym/portage/util/_eventloop/EventLoop.py +++ b/pym/portage/util/_eventloop/EventLoop.py @@ -1,4 +1,4 @@ -# Copyright 1999-2016 Gentoo Foundation +# Copyright 1999-2018 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import division @@ -9,7 +9,6 @@ import os import select import signal import sys -import time try: import fcntl @@ -29,6 +28,7 @@ portage.proxy.lazyimport.lazyimport(globals(), from portage import OrderedDict from portage.util import writemsg_level +from portage.util.monotonic import monotonic from ..SlotObject import SlotObject from .PollConstants import PollConstants from .PollSelectAdapter import PollSelectAdapter @@ -515,7 +515,7 @@ class EventLoop(object): self._timeout_handlers[source_id] = \ self._timeout_handler_class( interval=interval, function=function, args=args, - source_id=source_id, timestamp=time.time()) + source_id=source_id, timestamp=self.time()) if self._timeout_interval is None or \ self._timeout_interval > interval: self._timeout_interval = interval @@ -538,7 +538,7 @@ class EventLoop(object): return bool(calls) ready_timeouts = [] - current_time = time.time() + current_time = self.time() for x in self._timeout_handlers.values(): elapsed_seconds = current_time - x.timestamp # elapsed_seconds < 0 means the system clock has been adjusted @@ -558,7 +558,7 @@ class EventLoop(object): calls += 1 x.calling = True try: - x.timestamp = time.time() + x.timestamp = self.time() if not x.function(*x.args): self.source_remove(x.source_id) finally: @@ -684,6 +684,15 @@ class EventLoop(object): # The call_soon method inherits thread safety from the idle_add method. call_soon_threadsafe = call_soon + def time(self): + """Return the time according to the event loop's clock. + + This is a float expressed in seconds since an epoch, but the + epoch, precision, accuracy and drift are unspecified and may + differ per event loop. + """ + return monotonic() + def call_later(self, delay, callback, *args): """ Arrange for the callback to be called after the given delay seconds diff --git a/pym/portage/util/monotonic.py b/pym/portage/util/monotonic.py new file mode 100644 index 000000000..da1cce1c5 --- /dev/null +++ b/pym/portage/util/monotonic.py @@ -0,0 +1,32 @@ +# Copyright 2018 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +__all__ = ['monotonic'] + +import time +try: + import threading +except ImportError: + import dummy_threading as threading + +monotonic = getattr(time, 'monotonic', None) + +if monotonic is None: + def monotonic(): + """ + Emulate time.monotonic() which is available in Python 3.3 and later. + + @return: A float expressed in seconds since an epoch. + """ + with monotonic._lock: + current = time.time() + delta = current - monotonic._previous + if delta < 0: + monotonic._offset -= delta + monotonic._previous = current + monotonic._offset + return monotonic._previous + + # offset is used to counteract any backward movements + monotonic._offset = 0 + monotonic._previous = time.time() + monotonic._lock = threading.Lock() -- 2.13.6