Commit 08d12491 authored by Jason Madden's avatar Jason Madden

Make ThreadPool.join respect the loop.min_sleep_time value.

Setting the size was already doing so. By not using the hardcoded value, this prevents a UserWarning from the libuv backend.

Also rename the attribute to  and implement it for all loops.
Add this to the ILoop interface, and test that all loop implementations correctly implement it with zope.interface.verify.verifyObject.

Fixes #1321.
parent 58fcd584
......@@ -63,6 +63,10 @@
if the socket was already in use. Now the correct socket.error
should be raised.
- Fix :meth:`gevent.threadpool.ThreadPool.join` raising a
`UserWarning` when using the libuv backend. Reported in
:issue:`1321` by ZeroNet.
1.3.7 (2018-10-12)
==================
......
......@@ -46,6 +46,13 @@ class ILoop(Interface):
default = Attribute("Boolean indicating whether this is the default loop")
approx_timer_resolution = Attribute(
"Floating point number of seconds giving (approximately) the minimum "
"resolution of a timer (and hence the minimun value the sleep can sleep for). "
"On libuv, this is fixed by the library, but on libev it is just a guess "
"and the actual value is system dependent."
)
def run(nowait=False, once=False):
"""
Run the event loop.
......
......@@ -392,6 +392,7 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]:
# the libev internal pointer to 0, and ev_is_default_loop will
# no longer work.
cdef bint _default
cdef readonly double approx_timer_resolution
def __cinit__(self, object flags=None, object default=None, libev.intptr_t ptr=0):
self.starting_timer_may_update_loop_time = 0
......@@ -440,6 +441,8 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]:
def __init__(self, object flags=None, object default=None, libev.intptr_t ptr=0):
self._callbacks = CallbackFIFO()
# See libev.corecffi for this attribute.
self.approx_timer_resolution = 0.00001
cdef _run_callbacks(self):
cdef callback cb
......@@ -745,6 +748,15 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]:
# Explicitly not EV_USE_SIGNALFD
raise AttributeError("sigfd")
try:
from zope.interface import classImplements
except ImportError:
pass
else:
# XXX: This invokes the side-table lookup, we would
# prefer to have it stored directly on the class.
from gevent._interfaces import ILoop
classImplements(loop, ILoop)
# about readonly _flags attribute:
# bit #1 set if object owns Python reference to itself (Py_INCREF was
......
......@@ -208,6 +208,17 @@ _events_to_str = _watchers._events_to_str # exported
class loop(AbstractLoop):
# pylint:disable=too-many-public-methods
# libuv parameters simply won't accept anything lower than 1ms
# (0.001s), but libev takes fractional seconds. In practice, on
# one machine, libev can sleep for very small periods of time:
#
# sleep(0.00001) -> 0.000024
# sleep(0.0001) -> 0.000156
# sleep(0.001) -> 0.00136 (which is comparable to libuv)
approx_timer_resolution = 0.00001
error_handler = None
_CHECK_POINTER = 'struct ev_check *'
......
......@@ -79,9 +79,10 @@ def supported_backends():
@implementer(ILoop)
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
# libuv parameters simply won't accept anything lower than 1ms. In
# practice, looping on gevent.sleep(0.001) takes about 0.00138 s
# (+- 0.000036s)
approx_timer_resolution = 0.001 # 1ms
error_handler = None
......
......@@ -21,6 +21,7 @@
import re
import time
import unittest
import gevent.testing as greentest
import gevent.testing.timing
......@@ -316,5 +317,16 @@ class TestPeriodicMonitoringThread(greentest.TestCase):
self.assertIn('PeriodicMonitoringThread', data)
class TestLoopInterface(unittest.TestCase):
def test_implemensts_ILoop(self):
from zope.interface import verify
from gevent._interfaces import ILoop
loop = get_hub().loop
verify.verifyObject(ILoop, loop)
if __name__ == '__main__':
greentest.main()
......@@ -114,7 +114,7 @@ class ThreadPool(GroupMappingMixin):
self.manager.kill()
while self._size < size:
self._add_thread()
delay = getattr(self.hub.loop, 'min_sleep_time', 0.0001) # For libuv
delay = self.hub.loop.approx_timer_resolution
while self._size > size:
while self._size - size > self.task_queue.unfinished_tasks:
self.task_queue.put(None)
......@@ -150,7 +150,7 @@ class ThreadPool(GroupMappingMixin):
def join(self):
"""Waits until all outstanding tasks have been completed."""
delay = 0.0005
delay = max(0.0005, self.hub.loop.approx_timer_resolution)
while self.task_queue.unfinished_tasks > 0:
sleep(delay)
delay = min(delay * 2, .05)
......
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