Commit 1a418d12 authored by Jason Madden's avatar Jason Madden

Make libev follow libuv and not update timers by default.

Make Timeout._start_new_or_dummy stop forcing the issue.

The failing test_telnetlib was fixed by avoiding a zero-duration timer. On libuv zero duration timers can block the event loop.
parent 0774d4c1
......@@ -197,6 +197,15 @@ libuv
libev has also been changed to follow this behaviour.
- Timers of zero duration do not necessarily cause the event loop to
cycle, as they do in libev. Instead, they may be called
immediately. If zero duration timers are added from other zero
duration timer callbacks, this can lead the loop to appear to
hang, as no IO will actually be done.
In the future, zero duration timers may automatically be changed
to check or prepare watchers.
Again, this is extremely experimental and all of it is subject to
change.
......
......@@ -589,6 +589,14 @@ class timer(_base.TimerMixin, watcher):
# 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.
# Also, newly-added timers of 0 duration can *also* stall the loop, because
# they'll be seen to be expired immediately. Updating the time can prevent that,
# *if* there was already a timer for a longer duration scheduled.
# XXX: Have our loop implementation turn 0 duration timers into prepare or
# check watchers instead?
update_loop_time_on_start = False
def _update_now(self):
......
......@@ -150,9 +150,16 @@ def select(rlist, wlist, xlist, timeout=None): # pylint:disable=unused-argument
# Ignore interrupted syscalls
raise
if sel_results[0] or sel_results[1] or sel_results[2]:
if sel_results[0] or sel_results[1] or sel_results[2] or (timeout is not None and timeout == 0):
# If we actually had stuff ready, go ahead and return it. No need
# to go through the trouble of doing our own stuff.
# Likewise, if the timeout is 0, we already did a 0 timeout
# select and we don't need to do it again. Note that in libuv,
# zero duration timers may be called immediately, without
# cycling the event loop at all. 2.7/test_telnetlib.py "hangs"
# calling zero-duration timers if we go to the loop here.
# However, because this is typically a place where scheduling switches
# can occur, we need to make sure that's still the case; otherwise a single
# consumer could monopolize the thread. (shows up in test_ftplib.)
......
......@@ -13,7 +13,7 @@ module add timeouts to arbitrary code.
If a blocking function is called or an intense calculation is ongoing during
which no switches occur, :class:`Timeout` is powerless.
"""
from __future__ import absolute_import, print_function, division
from gevent._compat import string_types
from gevent.hub import getcurrent, _NONE, get_hub
......@@ -152,7 +152,7 @@ class Timeout(BaseException):
self.timer.start(getcurrent().throw, self.exception)
@classmethod
def start_new(cls, timeout=None, exception=None, ref=True, _update=None):
def start_new(cls, timeout=None, exception=None, ref=True):
"""Create a started :class:`Timeout`.
This is a shortcut, the exact action depends on *timeout*'s type:
......@@ -169,8 +169,6 @@ class Timeout(BaseException):
timeout.start()
return timeout
timeout = cls(timeout, exception, ref=ref)
if _update:
get_hub().loop.update()
timeout.start()
return timeout
......@@ -189,10 +187,7 @@ class Timeout(BaseException):
# under PyPy in synthetic benchmarks it makes no difference.
if timeout is None:
return _FakeTimer
# If we don't update the time here (and the timer watcher doesn't),
# as under libuv, then certain tests hang, notably the monkey-patched test_telnetlib
# in test_read_eager_A. libev does not demonstrate this behaviour.
return Timeout.start_new(timeout, exception, _update=True)
return Timeout.start_new(timeout, exception)
@property
def pending(self):
......
......@@ -578,12 +578,10 @@ class TestCase(TestCaseMetaClass("NewBase", (BaseTestCase,), {})):
econtext, ekind, evalue = error
if kind is not None:
self.assertIsInstance(kind, type)
try:
assert issubclass(ekind, kind), error
except TypeError as e:
# Seen on PyPy on Windows
print("TYPE ERROR", e, ekind, kind, type(kind))
raise
self.assertIsNotNone(
ekind,
"Error must not be none %r" % (error,))
assert issubclass(ekind, kind), error
if value is not None:
if isinstance(value, str):
self.assertEqual(str(evalue), value)
......
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