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
This commit is contained in:
1
AUTHORS
1
AUTHORS
@@ -144,3 +144,4 @@ Thanks To
|
|||||||
* Matt Yule-Bennett
|
* Matt Yule-Bennett
|
||||||
* Artur Stawiarski
|
* Artur Stawiarski
|
||||||
* Tal Wrii
|
* Tal Wrii
|
||||||
|
* Roman Podoliaka
|
||||||
|
8
bin/pull-monotonic
Executable file
8
bin/pull-monotonic
Executable file
@@ -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}
|
@@ -1,7 +1,6 @@
|
|||||||
import errno
|
import errno
|
||||||
from eventlet.support import get_errno
|
from eventlet.support import get_errno
|
||||||
from eventlet import patcher
|
from eventlet import patcher
|
||||||
time = patcher.original('time')
|
|
||||||
select = patcher.original("select")
|
select = patcher.original("select")
|
||||||
if hasattr(select, 'epoll'):
|
if hasattr(select, 'epoll'):
|
||||||
epoll = select.epoll
|
epoll = select.epoll
|
||||||
@@ -34,7 +33,7 @@ from eventlet.hubs.poll import READ, WRITE
|
|||||||
|
|
||||||
|
|
||||||
class Hub(poll.Hub):
|
class Hub(poll.Hub):
|
||||||
def __init__(self, clock=time.time):
|
def __init__(self, clock=None):
|
||||||
BaseHub.__init__(self, clock)
|
BaseHub.__init__(self, clock)
|
||||||
self.poll = epoll()
|
self.poll = epoll()
|
||||||
try:
|
try:
|
||||||
|
@@ -19,10 +19,8 @@ else:
|
|||||||
signal.alarm(math.ceil(seconds))
|
signal.alarm(math.ceil(seconds))
|
||||||
arm_alarm = alarm_signal
|
arm_alarm = alarm_signal
|
||||||
|
|
||||||
from eventlet import patcher
|
|
||||||
from eventlet.hubs import timer, IOClosed
|
from eventlet.hubs import timer, IOClosed
|
||||||
from eventlet.support import greenlets as greenlet, clear_sys_exc_info, six
|
from eventlet.support import greenlets as greenlet, clear_sys_exc_info, monotonic, six
|
||||||
time = patcher.original('time')
|
|
||||||
|
|
||||||
g_prevent_multiple_readers = True
|
g_prevent_multiple_readers = True
|
||||||
|
|
||||||
@@ -113,12 +111,15 @@ class BaseHub(object):
|
|||||||
READ = READ
|
READ = READ
|
||||||
WRITE = WRITE
|
WRITE = WRITE
|
||||||
|
|
||||||
def __init__(self, clock=time.time):
|
def __init__(self, clock=None):
|
||||||
self.listeners = {READ: {}, WRITE: {}}
|
self.listeners = {READ: {}, WRITE: {}}
|
||||||
self.secondaries = {READ: {}, WRITE: {}}
|
self.secondaries = {READ: {}, WRITE: {}}
|
||||||
self.closed = []
|
self.closed = []
|
||||||
|
|
||||||
|
if clock is None:
|
||||||
|
clock = monotonic.monotonic
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
|
|
||||||
self.greenlet = greenlet.greenlet(self.run)
|
self.greenlet = greenlet.greenlet(self.run)
|
||||||
self.stopping = False
|
self.stopping = False
|
||||||
self.running = False
|
self.running = False
|
||||||
|
@@ -4,7 +4,6 @@ from eventlet import patcher, support
|
|||||||
from eventlet.support import six
|
from eventlet.support import six
|
||||||
select = patcher.original('select')
|
select = patcher.original('select')
|
||||||
time = patcher.original('time')
|
time = patcher.original('time')
|
||||||
sleep = time.sleep
|
|
||||||
|
|
||||||
from eventlet.hubs.hub import BaseHub, READ, WRITE, noop
|
from eventlet.hubs.hub import BaseHub, READ, WRITE, noop
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ FILTERS = {READ: select.KQ_FILTER_READ,
|
|||||||
class Hub(BaseHub):
|
class Hub(BaseHub):
|
||||||
MAX_EVENTS = 100
|
MAX_EVENTS = 100
|
||||||
|
|
||||||
def __init__(self, clock=time.time):
|
def __init__(self, clock=None):
|
||||||
super(Hub, self).__init__(clock)
|
super(Hub, self).__init__(clock)
|
||||||
self._events = {}
|
self._events = {}
|
||||||
self._init_kqueue()
|
self._init_kqueue()
|
||||||
@@ -96,7 +95,7 @@ class Hub(BaseHub):
|
|||||||
|
|
||||||
if not readers and not writers:
|
if not readers and not writers:
|
||||||
if seconds:
|
if seconds:
|
||||||
sleep(seconds)
|
time.sleep(seconds)
|
||||||
return
|
return
|
||||||
result = self._control([], self.MAX_EVENTS, seconds)
|
result = self._control([], self.MAX_EVENTS, seconds)
|
||||||
SYSTEM_EXCEPTIONS = self.SYSTEM_EXCEPTIONS
|
SYSTEM_EXCEPTIONS = self.SYSTEM_EXCEPTIONS
|
||||||
|
@@ -4,7 +4,6 @@ import sys
|
|||||||
from eventlet import patcher
|
from eventlet import patcher
|
||||||
select = patcher.original('select')
|
select = patcher.original('select')
|
||||||
time = patcher.original('time')
|
time = patcher.original('time')
|
||||||
sleep = time.sleep
|
|
||||||
|
|
||||||
from eventlet.hubs.hub import BaseHub, READ, WRITE, noop
|
from eventlet.hubs.hub import BaseHub, READ, WRITE, noop
|
||||||
from eventlet.support import get_errno, clear_sys_exc_info
|
from eventlet.support import get_errno, clear_sys_exc_info
|
||||||
@@ -15,7 +14,7 @@ WRITE_MASK = select.POLLOUT
|
|||||||
|
|
||||||
|
|
||||||
class Hub(BaseHub):
|
class Hub(BaseHub):
|
||||||
def __init__(self, clock=time.time):
|
def __init__(self, clock=None):
|
||||||
super(Hub, self).__init__(clock)
|
super(Hub, self).__init__(clock)
|
||||||
self.poll = select.poll()
|
self.poll = select.poll()
|
||||||
# poll.modify is new to 2.6
|
# poll.modify is new to 2.6
|
||||||
@@ -79,7 +78,7 @@ class Hub(BaseHub):
|
|||||||
|
|
||||||
if not readers and not writers:
|
if not readers and not writers:
|
||||||
if seconds:
|
if seconds:
|
||||||
sleep(seconds)
|
time.sleep(seconds)
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
presult = self.do_poll(seconds)
|
presult = self.do_poll(seconds)
|
||||||
|
167
eventlet/support/monotonic.py
Normal file
167
eventlet/support/monotonic.py
Normal file
@@ -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')
|
4
tox.ini
4
tox.ini
@@ -1,13 +1,13 @@
|
|||||||
# The flake8 and pep8 sections just contain configuration for corresponding tools.
|
# The flake8 and pep8 sections just contain configuration for corresponding tools.
|
||||||
# Checkers are not run implicitly.
|
# Checkers are not run implicitly.
|
||||||
[flake8]
|
[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
|
ignore = E261,W503
|
||||||
max-line-length = 101
|
max-line-length = 101
|
||||||
|
|
||||||
[pep8]
|
[pep8]
|
||||||
count = 1
|
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
|
ignore = E261,W503
|
||||||
max-line-length = 101
|
max-line-length = 101
|
||||||
show-source = 1
|
show-source = 1
|
||||||
|
Reference in New Issue
Block a user