Commit debb8d83 authored by Jason Madden's avatar Jason Madden

Fixes #1057 by adjusting timers for libuv

But see the comments in the code and the comments in the issue. libuv
has very different timer behaviour than libev.
parent 62e3b300
......@@ -113,8 +113,9 @@
suffers a number of limitations compared to libev, notably:
- Timers (such as ``gevent.sleep`` and ``gevent.Timeout``) only
support a resolution of 1ms. Attempting to use something smaller
will automatically increase it to 1ms and issue a warning.
support a resolution of 1ms (in practice, it's closer to 1.5ms).
Attempting to use something smaller will automatically increase it
to 1ms and issue a warning.
- Using negative timeouts may behave differently from libev.
......@@ -161,7 +162,8 @@
- The order in which timers and other callbacks are invoked may be
different than in libev. In particular, timers and IO callbacks
happen in a different order.
happen in a different order, and timers may easily be off by up to
half of the supposed 1ms resolution. See :issue:`1057`.
Again, this is extremely experimental and all of it is subject to
change.
......
......@@ -78,6 +78,10 @@ else:
class loop(AbstractLoop):
# XXX: Undocumented. Maybe better named 'timer_resolution'? We can't
# know this in general on libev
min_sleep_time = 0.001 # 1ms
DEFAULT_LOOP_REGENERATES = True
error_handler = None
......@@ -219,6 +223,24 @@ class loop(AbstractLoop):
# does. This could also lead to test__systemerror:TestCallback
# appearing to be flaky.
# As yet another final note, if we are currently running a
# timer callback, meaning we're inside uv__run_timers() in C,
# and the Python starts a new timer, if the Python code then
# update's the loop's time, it's possible that timer will
# expire *and be run in the same iteration of the loop*. This
# is trivial to do: In sequential code, anything after
# `gevent.sleep(0.1)` is running in a timer callback. Starting
# a new timer---e.g., another gevent.sleep() call---will
# update the time, *before* uv__run_timers exits, meaning
# other timers get a chance to run before our check or prepare
# watcher callbacks do. Therefore, we do indeed have to have a 0
# timer to run callbacks---it gets inserted before any other user
# timers---ideally, this should be especially careful about how much time
# it runs for.
# 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.
libuv.uv_check_start(self._timer0, libuv.python_prepare_callback)
......
......@@ -67,7 +67,7 @@ class ThreadPool(GroupMappingMixin):
self.manager.kill()
while self._size < size:
self._add_thread()
delay = 0.0001
delay = getattr(self.hub.loop, 'min_sleep_time', 0.0001) # For libuv
while self._size > size:
while self._size - size > self.task_queue.unfinished_tasks:
self.task_queue.put(None)
......
......@@ -666,6 +666,12 @@ class TestCase(TestCaseMetaClass("NewBase", (BaseTestCase,), {})):
self.assertEqual(sig.keywords, gevent_sig.keywords, func_name)
self.assertEqual(sig.defaults, gevent_sig.defaults, func_name)
def assertEqualFlakyRaceCondition(self, a, b):
try:
self.assertEqual(a, b)
except AssertionError:
reraiseFlakyTestRaceCondition()
if not hasattr(TestCase, 'assertRaisesRegex'):
TestCase.assertRaisesRegex = TestCase.assertRaisesRegexp
......
......@@ -341,16 +341,16 @@ class TestMaxsize(TestCase):
def test_inc(self):
self.pool = ThreadPool(0)
done = []
# Try to be careful not to tick over the libuv timer.
# See libuv/loop.py:_start_callback_timer
gevent.spawn(self.pool.spawn, done.append, 1)
gevent.spawn_later(0.001, self.pool.spawn, done.append, 2)
gevent.sleep(0.01)
gevent.spawn_later(0.01, self.pool.spawn, done.append, 2)
gevent.sleep(0.02)
self.assertEqual(done, [])
self.pool.maxsize = 1
gevent.sleep(0.01)
try:
self.assertEqual(done, [1, 2])
except AssertionError:
greentest.reraiseFlakyTestRaceConditionLibuv()
gevent.sleep(0.02)
self.assertEqualFlakyRaceCondition(done, [1, 2])
def test_setzero(self):
pool = self.pool = ThreadPool(3)
......
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