summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Shepelev <temotor@gmail.com>2017-02-15 21:14:35 +0300
committerSergey Shepelev <temotor@gmail.com>2017-02-16 12:46:24 +0300
commitf92b49f77e073dad40a71858ef9149b543e07d67 (patch)
tree0ca02a8a6b632c518d238af792d6f34fa15cacab
parent503aed1310b5e4a2fad9f5a0909663636a4e8752 (diff)
downloadeventlet-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--AUTHORS1
-rwxr-xr-xbin/pull-monotonic8
-rw-r--r--eventlet/hubs/epolls.py3
-rw-r--r--eventlet/hubs/hub.py9
-rw-r--r--eventlet/hubs/kqueue.py5
-rw-r--r--eventlet/hubs/poll.py5
-rw-r--r--eventlet/support/monotonic.py167
-rw-r--r--tox.ini4
8 files changed, 188 insertions, 14 deletions
diff --git a/AUTHORS b/AUTHORS
index 70f27da..6272011 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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')
diff --git a/tox.ini b/tox.ini
index f6dfd66..9f4dbb6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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