Commit a01382a6 authored by Jason Madden's avatar Jason Madden

libuv: do not update the loop time when timers are started by default. See...

libuv: do not update the loop time when timers are started by default. See #1057. libev might follow suite in the future.
parent debb8d83
...@@ -107,20 +107,45 @@ ...@@ -107,20 +107,45 @@
- Update c-ares to 1.13.0. See :issue:`990`. - Update c-ares to 1.13.0. See :issue:`990`.
- gevent is now tested on Python 3.6.4. This includes the following
fixes and changes:
- Errors raised from :mod:`gevent.subprocess` will have a
``filename`` attribute set.
- The :class:`threading.Timer` class is now monkey-patched and can
be joined.
- :meth:`gevent.ssl.SSLSocket.unwrap` behaves more like the standard
library, including returning a SSLSocket and allowing certain
timeout-related SSL errors to propagate. The added standard
library tests ``test_ftplib.py`` now passes.
- :class:`gevent.subprocess.Popen` accepts a "path-like object" for
the *cwd* parameter on all platforms. Previously this only worked
on POSIX platforms under Python 3.6. Now it also works on Windows under
Python 3.6 (as expected) and is backported to all previous versions.
- gevent now uses cffi's "extern 'Python'" callbacks. These should be
faster and more stable. This requires at least cffi 1.4.0. See :issue:`1049`.
libuv
-----
- Add initial *experimental* support for using libuv as a backend - Add initial *experimental* support for using libuv as a backend
instead of libev, controlled by setting the environment variable instead of libev, controlled by setting the environment variable
``GEVENT_CORE_CFFI_ONLY=libuv`` before importing gevent. This only ``GEVENT_CORE_CFFI_ONLY=libuv`` before importing gevent. This
suffers a number of limitations compared to libev, notably: suffers a number of limitations compared to libev, notably:
- Timers (such as ``gevent.sleep`` and ``gevent.Timeout``) only - Timers (such as ``gevent.sleep`` and ``gevent.Timeout``) only
support a resolution of 1ms (in practice, it's closer to 1.5ms). support a resolution of 1ms (in practice, it's closer to 1.5ms).
Attempting to use something smaller will automatically increase it Attempting to use something smaller will automatically increase it
to 1ms and issue a warning. to 1ms and issue a warning. Because libuv only supports
millisecond resolution by rounding a higher-precision clock to an
integer number of milliseconds, timers apparently suffer from more
jitter.
- Using negative timeouts may behave differently from libev. - Using negative timeouts may behave differently from libev.
- Timers seem to suffer from more jitter than libev.
- libuv blocks delivery of all signals, so signals are handled using - libuv blocks delivery of all signals, so signals are handled using
an (arbitrary) 1 second timer. This means that signal handling an (arbitrary) 1 second timer. This means that signal handling
will be delayed by up to that amount, and that the longest the will be delayed by up to that amount, and that the longest the
...@@ -165,30 +190,18 @@ ...@@ -165,30 +190,18 @@
happen in a different order, and timers may easily be off by up to happen in a different order, and timers may easily be off by up to
half of the supposed 1ms resolution. See :issue:`1057`. half of the supposed 1ms resolution. See :issue:`1057`.
- Starting timers does not update the loop's time by default. This
is because, unlike libev, a timer callback can cause other timer
callbacks to be run if they expire because the loop's time
updated, without cycling the event loop. See :issue:`1057`.
.. note:: libev might be changed to follow this behaviour.
Again, this is extremely experimental and all of it is subject to Again, this is extremely experimental and all of it is subject to
change. change.
See :issue:`790` for history and more in-depth discussion. See :issue:`790` for history and more in-depth discussion.
- gevent is now tested on Python 3.6.4. This includes the following
fixes and changes:
- Errors raised from :mod:`gevent.subprocess` will have a
``filename`` attribute set.
- The :class:`threading.Timer` class is now monkey-patched and can
be joined.
- :meth:`gevent.ssl.SSLSocket.unwrap` behaves more like the standard
library, including returning a SSLSocket and allowing certain
timeout-related SSL errors to propagate. The added standard
library tests ``test_ftplib.py`` now passes.
- :class:`gevent.subprocess.Popen` accepts a "path-like object" for
the *cwd* parameter on all platforms. Previously this only worked
on POSIX platforms under Python 3.6. Now it also works on Windows under
Python 3.6 (as expected) and is backported to all previous versions.
- gevent now uses cffi's "extern 'Python'" callbacks. These should be
faster and more stable. This requires at least cffi 1.4.0. See :issue:`1049`.
1.2.2 (2017-06-05) 1.2.2 (2017-06-05)
================== ==================
...@@ -2110,3 +2123,9 @@ Besides having less bugs and less code to care about the goals of the fork are: ...@@ -2110,3 +2123,9 @@ Besides having less bugs and less code to care about the goals of the fork are:
* Use the interfaces and conventions from the standard Python library where possible. * Use the interfaces and conventions from the standard Python library where possible.
.. _eventlet: http://bitbucket.org/denis/eventlet .. _eventlet: http://bitbucket.org/denis/eventlet
..
Local Variables:
flycheck-mode: nil
End:
...@@ -149,6 +149,7 @@ html_short_title = 'Documentation' ...@@ -149,6 +149,7 @@ html_short_title = 'Documentation'
# If true, SmartyPants will be used to convert quotes and dashes to # If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities. # typographically correct entities.
# This is true by default in sphinx 1.6
html_use_smartypants = True html_use_smartypants = True
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
......
...@@ -423,6 +423,8 @@ class IoMixin(object): ...@@ -423,6 +423,8 @@ class IoMixin(object):
class TimerMixin(object): class TimerMixin(object):
_watcher_type = 'timer' _watcher_type = 'timer'
update_loop_time_on_start = True
def __init__(self, loop, after=0.0, repeat=0.0, ref=True, priority=None): def __init__(self, loop, after=0.0, repeat=0.0, ref=True, priority=None):
if repeat < 0.0: if repeat < 0.0:
raise ValueError("repeat must be positive or zero: %r" % repeat) raise ValueError("repeat must be positive or zero: %r" % repeat)
...@@ -431,7 +433,7 @@ class TimerMixin(object): ...@@ -431,7 +433,7 @@ class TimerMixin(object):
super(TimerMixin, self).__init__(loop, ref=ref, priority=priority, args=(after, repeat)) super(TimerMixin, self).__init__(loop, ref=ref, priority=priority, args=(after, repeat))
def start(self, callback, *args, **kw): def start(self, callback, *args, **kw):
update = kw.get("update", True) update = kw.get("update", self.update_loop_time_on_start)
if update: if update:
# Quoth the libev doc: "This is a costly operation and is # Quoth the libev doc: "This is a costly operation and is
# usually done automatically within ev_run(). This # usually done automatically within ev_run(). This
......
...@@ -241,6 +241,9 @@ class loop(AbstractLoop): ...@@ -241,6 +241,9 @@ class loop(AbstractLoop):
# AND YET: We can't actually do that. We get timeouts that I haven't fully # AND YET: We can't actually do that. We get timeouts that I haven't fully
# investigated if we do. Probably stuck in a timer loop. # investigated if we do. Probably stuck in a timer loop.
# As a partial remedy to this, unlike libev, our timer watcher
# class doesn't update the loop time by default.
libuv.uv_check_start(self._timer0, libuv.python_prepare_callback) libuv.uv_check_start(self._timer0, libuv.python_prepare_callback)
......
...@@ -582,6 +582,15 @@ locals()['async'] = async_ ...@@ -582,6 +582,15 @@ locals()['async'] = async_
class timer(_base.TimerMixin, watcher): class timer(_base.TimerMixin, watcher):
# In libuv, timer callbacks continue running while any timer is
# expired, including newly added timers. Newly added non-zero
# timers (especially of small duration) can be seen to be expired
# if the loop time is updated while we are in a timer callback.
# This can lead to us being stuck running timers for a terribly
# long time, which is not good. So default to not updating the
# time.
update_loop_time_on_start = False
def _update_now(self): def _update_now(self):
self.loop.update() self.loop.update()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment