diff options
author | Sergey Shepelev <temotor@gmail.com> | 2017-02-15 21:14:35 +0300 |
---|---|---|
committer | Sergey Shepelev <temotor@gmail.com> | 2017-02-16 12:46:24 +0300 |
commit | f92b49f77e073dad40a71858ef9149b543e07d67 (patch) | |
tree | 0ca02a8a6b632c518d238af792d6f34fa15cacab | |
parent | 503aed1310b5e4a2fad9f5a0909663636a4e8752 (diff) | |
download | eventlet-f92b49f77e073dad40a71858ef9149b543e07d67.tar.gz |
hubs: use monotonic clock by default (bundled package); Thanks to Roman Podoliaka and Victor Stinner
eventlet/support/monotonic.py is copied by curl from specific version on Github.
Change and run bin/pull-monotonic script to update to newer version.
https://github.com/eventlet/eventlet/pull/388
https://github.com/eventlet/eventlet/pull/303
-rw-r--r-- | AUTHORS | 1 | ||||
-rwxr-xr-x | bin/pull-monotonic | 8 | ||||
-rw-r--r-- | eventlet/hubs/epolls.py | 3 | ||||
-rw-r--r-- | eventlet/hubs/hub.py | 9 | ||||
-rw-r--r-- | eventlet/hubs/kqueue.py | 5 | ||||
-rw-r--r-- | eventlet/hubs/poll.py | 5 | ||||
-rw-r--r-- | eventlet/support/monotonic.py | 167 | ||||
-rw-r--r-- | tox.ini | 4 |
8 files changed, 188 insertions, 14 deletions
@@ -144,3 +144,4 @@ Thanks To * Matt Yule-Bennett * Artur Stawiarski * Tal Wrii +* Roman Podoliaka diff --git a/bin/pull-monotonic b/bin/pull-monotonic new file mode 100755 index 0000000..0796415 --- /dev/null +++ b/bin/pull-monotonic @@ -0,0 +1,8 @@ +#!/bin/bash +set -eux +cd "$( dirname "${BASH_SOURCE[0]}" )/.." +version=${1-"8447153178046158fe10d927cd358e0088587bca"} +path="./eventlet/support/monotonic.py" +url="https://raw.githubusercontent.com/atdt/monotonic/${version}/monotonic.py" +rm -f ${path} +curl --fail --location --silent --show-error -o${path} ${url} diff --git a/eventlet/hubs/epolls.py b/eventlet/hubs/epolls.py index 803b39d..8814248 100644 --- a/eventlet/hubs/epolls.py +++ b/eventlet/hubs/epolls.py @@ -1,7 +1,6 @@ import errno from eventlet.support import get_errno from eventlet import patcher -time = patcher.original('time') select = patcher.original("select") if hasattr(select, 'epoll'): epoll = select.epoll @@ -34,7 +33,7 @@ from eventlet.hubs.poll import READ, WRITE class Hub(poll.Hub): - def __init__(self, clock=time.time): + def __init__(self, clock=None): BaseHub.__init__(self, clock) self.poll = epoll() try: diff --git a/eventlet/hubs/hub.py b/eventlet/hubs/hub.py index 56cffb8..2834b3b 100644 --- a/eventlet/hubs/hub.py +++ b/eventlet/hubs/hub.py @@ -19,10 +19,8 @@ else: signal.alarm(math.ceil(seconds)) arm_alarm = alarm_signal -from eventlet import patcher from eventlet.hubs import timer, IOClosed -from eventlet.support import greenlets as greenlet, clear_sys_exc_info, six -time = patcher.original('time') +from eventlet.support import greenlets as greenlet, clear_sys_exc_info, monotonic, six g_prevent_multiple_readers = True @@ -113,12 +111,15 @@ class BaseHub(object): READ = READ WRITE = WRITE - def __init__(self, clock=time.time): + def __init__(self, clock=None): self.listeners = {READ: {}, WRITE: {}} self.secondaries = {READ: {}, WRITE: {}} self.closed = [] + if clock is None: + clock = monotonic.monotonic self.clock = clock + self.greenlet = greenlet.greenlet(self.run) self.stopping = False self.running = False diff --git a/eventlet/hubs/kqueue.py b/eventlet/hubs/kqueue.py index 05a961b..c5c5901 100644 --- a/eventlet/hubs/kqueue.py +++ b/eventlet/hubs/kqueue.py @@ -4,7 +4,6 @@ from eventlet import patcher, support from eventlet.support import six select = patcher.original('select') time = patcher.original('time') -sleep = time.sleep from eventlet.hubs.hub import BaseHub, READ, WRITE, noop @@ -20,7 +19,7 @@ FILTERS = {READ: select.KQ_FILTER_READ, class Hub(BaseHub): MAX_EVENTS = 100 - def __init__(self, clock=time.time): + def __init__(self, clock=None): super(Hub, self).__init__(clock) self._events = {} self._init_kqueue() @@ -96,7 +95,7 @@ class Hub(BaseHub): if not readers and not writers: if seconds: - sleep(seconds) + time.sleep(seconds) return result = self._control([], self.MAX_EVENTS, seconds) SYSTEM_EXCEPTIONS = self.SYSTEM_EXCEPTIONS diff --git a/eventlet/hubs/poll.py b/eventlet/hubs/poll.py index 536044c..54e242c 100644 --- a/eventlet/hubs/poll.py +++ b/eventlet/hubs/poll.py @@ -4,7 +4,6 @@ import sys from eventlet import patcher select = patcher.original('select') time = patcher.original('time') -sleep = time.sleep from eventlet.hubs.hub import BaseHub, READ, WRITE, noop from eventlet.support import get_errno, clear_sys_exc_info @@ -15,7 +14,7 @@ WRITE_MASK = select.POLLOUT class Hub(BaseHub): - def __init__(self, clock=time.time): + def __init__(self, clock=None): super(Hub, self).__init__(clock) self.poll = select.poll() # poll.modify is new to 2.6 @@ -79,7 +78,7 @@ class Hub(BaseHub): if not readers and not writers: if seconds: - sleep(seconds) + time.sleep(seconds) return try: presult = self.do_poll(seconds) diff --git a/eventlet/support/monotonic.py b/eventlet/support/monotonic.py new file mode 100644 index 0000000..d843d82 --- /dev/null +++ b/eventlet/support/monotonic.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +""" + monotonic + ~~~~~~~~~ + + This module provides a ``monotonic()`` function which returns the + value (in fractional seconds) of a clock which never goes backwards. + + On Python 3.3 or newer, ``monotonic`` will be an alias of + ``time.monotonic`` from the standard library. On older versions, + it will fall back to an equivalent implementation: + + +-------------+----------------------------------------+ + | Linux, BSD | ``clock_gettime(3)`` | + +-------------+----------------------------------------+ + | Windows | ``GetTickCount`` or ``GetTickCount64`` | + +-------------+----------------------------------------+ + | OS X | ``mach_absolute_time`` | + +-------------+----------------------------------------+ + + If no suitable implementation exists for the current platform, + attempting to import this module (or to import from it) will + cause a ``RuntimeError`` exception to be raised. + + + Copyright 2014, 2015, 2016 Ori Livneh <ori@wikimedia.org> + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +""" +import ctypes +import ctypes.util +import os +import sys +import threading +import time + + +__all__ = ('monotonic',) + + +try: + monotonic = time.monotonic +except AttributeError: + try: + if sys.platform == 'darwin': # OS X, iOS + # See Technical Q&A QA1398 of the Mac Developer Library: + # <https://developer.apple.com/library/mac/qa/qa1398/> + libc = ctypes.CDLL('/usr/lib/libc.dylib', use_errno=True) + + class mach_timebase_info_data_t(ctypes.Structure): + """System timebase info. Defined in <mach/mach_time.h>.""" + _fields_ = (('numer', ctypes.c_uint32), + ('denom', ctypes.c_uint32)) + + mach_absolute_time = libc.mach_absolute_time + mach_absolute_time.restype = ctypes.c_uint64 + + timebase = mach_timebase_info_data_t() + libc.mach_timebase_info(ctypes.byref(timebase)) + ticks_per_second = timebase.numer / timebase.denom * 1.0e9 + + def monotonic(): + """Monotonic clock, cannot go backward.""" + return mach_absolute_time() / ticks_per_second + + elif sys.platform.startswith('win32') or sys.platform.startswith('cygwin'): + if sys.platform.startswith('cygwin'): + # Note: cygwin implements clock_gettime (CLOCK_MONOTONIC = 4) since + # version 1.7.6. Using raw WinAPI for maximum version compatibility. + + # Ugly hack using the wrong calling convention (in 32-bit mode) + # because ctypes has no windll under cygwin (and it also seems that + # the code letting you select stdcall in _ctypes doesn't exist under + # the preprocessor definitions relevant to cygwin). + # This is 'safe' because: + # 1. The ABI of GetTickCount and GetTickCount64 is identical for + # both calling conventions because they both have no parameters. + # 2. libffi masks the problem because after making the call it doesn't + # touch anything through esp and epilogue code restores a correct + # esp from ebp afterwards. + try: + kernel32 = ctypes.cdll.kernel32 + except OSError: # 'No such file or directory' + kernel32 = ctypes.cdll.LoadLibrary('kernel32.dll') + else: + kernel32 = ctypes.windll.kernel32 + + GetTickCount64 = getattr(kernel32, 'GetTickCount64', None) + if GetTickCount64: + # Windows Vista / Windows Server 2008 or newer. + GetTickCount64.restype = ctypes.c_ulonglong + + def monotonic(): + """Monotonic clock, cannot go backward.""" + return GetTickCount64() / 1000.0 + + else: + # Before Windows Vista. + GetTickCount = kernel32.GetTickCount + GetTickCount.restype = ctypes.c_uint32 + + get_tick_count_lock = threading.Lock() + get_tick_count_last_sample = 0 + get_tick_count_wraparounds = 0 + + def monotonic(): + """Monotonic clock, cannot go backward.""" + global get_tick_count_last_sample + global get_tick_count_wraparounds + + with get_tick_count_lock: + current_sample = GetTickCount() + if current_sample < get_tick_count_last_sample: + get_tick_count_wraparounds += 1 + get_tick_count_last_sample = current_sample + + final_milliseconds = get_tick_count_wraparounds << 32 + final_milliseconds += get_tick_count_last_sample + return final_milliseconds / 1000.0 + + else: + try: + clock_gettime = ctypes.CDLL(ctypes.util.find_library('c'), + use_errno=True).clock_gettime + except AttributeError: + clock_gettime = ctypes.CDLL(ctypes.util.find_library('rt'), + use_errno=True).clock_gettime + + class timespec(ctypes.Structure): + """Time specification, as described in clock_gettime(3).""" + _fields_ = (('tv_sec', ctypes.c_long), + ('tv_nsec', ctypes.c_long)) + + if sys.platform.startswith('linux'): + CLOCK_MONOTONIC = 1 + elif sys.platform.startswith('freebsd'): + CLOCK_MONOTONIC = 4 + elif sys.platform.startswith('sunos5'): + CLOCK_MONOTONIC = 4 + elif 'bsd' in sys.platform: + CLOCK_MONOTONIC = 3 + + def monotonic(): + """Monotonic clock, cannot go backward.""" + ts = timespec() + if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(ts)): + errno = ctypes.get_errno() + raise OSError(errno, os.strerror(errno)) + return ts.tv_sec + ts.tv_nsec / 1.0e9 + + # Perform a sanity-check. + if monotonic() - monotonic() > 0: + raise ValueError('monotonic() is not monotonic!') + + except Exception: + raise RuntimeError('no suitable implementation for this system') @@ -1,13 +1,13 @@ # The flake8 and pep8 sections just contain configuration for corresponding tools. # Checkers are not run implicitly. [flake8] -exclude = *.egg*,.env,.git,.hg,.tox,_*,build*,dist*,venv*,six.py,mock.py,eventlet/green/http/*,eventlet/support/dns/* +exclude = *.egg*,.env,.git,.hg,.tox,_*,build*,dist*,venv*,six.py,mock.py,eventlet/green/http/*,eventlet/support/dns/*,eventlet/support/monotonic.py ignore = E261,W503 max-line-length = 101 [pep8] count = 1 -exclude = *.egg*,.env,.git,.hg,.tox,_*,build*,dist*,venv*,six.py,mock.py,eventlet/green/http/*,eventlet/support/dns/* +exclude = *.egg*,.env,.git,.hg,.tox,_*,build*,dist*,venv*,six.py,mock.py,eventlet/green/http/*,eventlet/support/dns/*,eventlet/support/monotonic.py ignore = E261,W503 max-line-length = 101 show-source = 1 |