Permalink
Browse files

Forget abou some timers (specially, about timeouts)

Some docs
  • Loading branch information...
1 parent fcd38cc commit 7aa94eb3bc410bfcb0719f0d71a27f4509e6406c Alvaro committed Nov 12, 2012
Showing with 104 additions and 77 deletions.
  1. +1 −1 .idea/evy.iml
  2. +1 −1 .idea/misc.xml
  3. +32 −15 evy/hubs/hub.py
  4. +21 −12 evy/hubs/timer.py
  5. +16 −11 evy/timeout.py
  6. +10 −5 evy/uv/watchers.py
  7. +0 −14 tests/__init__.py
  8. +23 −18 tests/test_hub.py
View
@@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
- <orderEntry type="jdk" jdkName="Python 2.7.2 (/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python)" jdkType="Python SDK" />
+ <orderEntry type="jdk" jdkName="Python 2.7.2+ (/usr/bin/python2.7)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
View
@@ -3,6 +3,6 @@
<component name="ProjectResources">
<default-html-doctype>$APPLICATION_HOME_DIR$/lib/pycharm.jar!/resources/html5-schema/html5.rnc</default-html-doctype>
</component>
- <component name="ProjectRootManager" version="2" project-jdk-name="Python 2.7.2 (/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python)" project-jdk-type="Python SDK" />
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 2.7.2+ (/usr/bin/python2.7)" project-jdk-type="Python SDK" />
</project>
View
@@ -204,6 +204,9 @@ def add (self, evtype, fileno, cb):
The *cb* argument is the callback which will be called when the file
is ready for reading/writing.
"""
+ if fileno < 0:
+ raise ValueError('invalid file descriptor: %d' % (fileno))
+
listener = self.lclass(evtype, fileno, cb)
bucket = self.listeners[evtype]
if fileno in bucket:
@@ -317,7 +320,7 @@ def wait (self, seconds = None):
:param seconds: the amount of seconds to wait
:type seconds: integer
"""
- timer = Timer(self, seconds)
+ timer = Timer(self, seconds * 1000)
timer.start(lambda x: None)
try:
@@ -448,11 +451,18 @@ def squelch_timer_exception (self, timer, exc_info):
##
def add_timer (self, timer):
+ """
+ Add a timer in the hub
+
+ :param timer:
+ :param unreferenced: if True, we unreference the timer, so the loop does not wait until it is triggered
+ :return:
+ """
# scheduled_time = self.clock() + timer.seconds
# self.next_timers.append((scheduled_time, timer))
# return scheduled_time
# store the pyevent timer object so that we can cancel later
- eventtimer = Timer(self, timer.seconds)
+ eventtimer = Timer(self, timer.seconds * 1000)
timer.impltimer = eventtimer
eventtimer.start(self.timer_finished, timer)
@@ -465,19 +475,26 @@ def timer_canceled (self, timer):
# self.next_timers = [t for t in self.next_timers if not t[1].called]
# heapq.heapify(self.timers)
- #timer.impltimer.stop()
- del timer.impltimer
- #try:
- # timer.impltimer.stop()
- # del timer.impltimer
- #except (AttributeError, TypeError):
- # pass
- #finally:
- # super(Hub, self).timer_canceled(timer)
+ try:
+ #timer.impltimer.stop()
+ del timer.impltimer
+ except (AttributeError, TypeError):
+ pass
def timer_finished (self, timer):
pass
+ def forget_timer(self, timer):
+ """
+ Let the hub forget about a timer, so we do not keep the loop running forever until
+ this timer triggers...
+ """
+ try:
+ self.unref(timer.impltimer.handle)
+ except (AttributeError, TypeError):
+ pass
+
+
def prepare_timers (self):
heappush = heapq.heappush
t = self.timers
@@ -629,7 +646,7 @@ def default_loop(self):
## references
##
- def ref(self):
+ def ref(self, handle):
"""
The event loop only runs as long as there are active watchers. This system works by having
every watcher increase the reference count of the event loop when it is started and decreasing
@@ -638,9 +655,9 @@ def ref(self):
:return: None
"""
- libuv.uv_ref(self._uv_ptr)
+ libuv.uv_ref(handle)
- def unref(self):
+ def unref(self, handle):
"""
This method can be used with interval timers. You might have a garbage collector which runs
every X seconds, or your network service might send a heartbeat to others periodically, but
@@ -651,7 +668,7 @@ def unref(self):
:return: None
"""
- libuv.uv_unref(self._uv_ptr)
+ libuv.uv_unref(handle)
def now(self):
View
@@ -27,6 +27,8 @@
# THE SOFTWARE.
#
+from functools import partial
+
from evy.support import greenlets as greenlet
from evy.hubs import get_hub
@@ -54,8 +56,11 @@ def __init__(self, seconds, cb, *args, **kw):
calling timer.schedule() or runloop.add_timer(timer).
"""
self.seconds = seconds
- self.tpl = cb, args, kw
+
+ self.callback = kw.pop('callback', partial(cb, *args, **kw))
+
self.called = False
+
if _g_debug:
import traceback, cStringIO
self.traceback = cStringIO.StringIO()
@@ -67,33 +72,38 @@ def pending(self):
def __repr__(self):
secs = getattr(self, 'seconds', None)
- cb, args, kw = getattr(self, 'tpl', (None, None, None))
- retval = "Timer(%s, %s, *%s, **%s)" % (secs, cb, args, kw)
+ cb = getattr(self, 'callback', None)
+ retval = "Timer(%s, %s)" % (secs, cb)
if _g_debug and hasattr(self, 'traceback'):
retval += '\n' + self.traceback.getvalue()
return retval
def copy(self):
- cb, args, kw = self.tpl
- return self.__class__(self.seconds, cb, *args, **kw)
+ return self.__class__(self.seconds, callback = self.callback)
def schedule(self):
"""
- Schedule this timer to run in the current runloop.
+ Schedule this timer to run in the current loop.
"""
self.called = False
self.scheduled_time = get_hub().add_timer(self)
return self
+ def forget(self):
+ """
+ Let the hub forget about this timer, so we do not keep the loop running forever until
+ the timer triggers.
+ """
+ get_hub().forget_timer(self)
+
def __call__(self, *args):
if not self.called:
self.called = True
- cb, args, kw = self.tpl
try:
- cb(*args, **kw)
+ self.callback()
finally:
try:
- del self.tpl
+ del self.callback
except AttributeError:
pass
@@ -106,7 +116,7 @@ def cancel(self):
self.called = True
get_hub().timer_canceled(self)
try:
- del self.tpl
+ del self.callback
except AttributeError:
pass
@@ -134,8 +144,7 @@ def __call__(self, *args):
self.called = True
if self.greenlet is not None and self.greenlet.dead:
return
- cb, args, kw = self.tpl
- cb(*args, **kw)
+ self.callback()
def cancel(self):
"""
View
@@ -58,19 +58,24 @@ def __init__ (self, seconds = None, exception = None):
self.start()
def start (self):
- """Schedule the timeout. This is called on construction, so
+ """
+ Schedule the timeout. This is called on construction, so
it should not be called explicitly, unless the timer has been
- canceled."""
- assert not self.pending,\
- '%r is already started; to restart it, cancel it first' % self
- if self.seconds is None: # "fake" timeout (never expires)
+ canceled.
+ """
+ assert not self.pending, '%r is already started; to restart it, cancel it first' % self
+
+ if self.seconds is None:
+ # "fake" timeout (never expires)
self.timer = None
- elif self.exception is None or isinstance(self.exception, bool): # timeout that raises self
- self.timer = get_hub().schedule_call_global(
- self.seconds, greenlet.getcurrent().throw, self)
- else: # regular timeout with user-provided exception
- self.timer = get_hub().schedule_call_global(
- self.seconds, greenlet.getcurrent().throw, self.exception)
+ else:
+ hub = get_hub()
+ if self.exception is None or isinstance(self.exception, bool): # timeout that raises self
+ self.timer = hub.schedule_call_global(self.seconds, greenlet.getcurrent().throw, self)
+ else: # regular timeout with user-provided exception
+ self.timer = hub.schedule_call_global(self.seconds, greenlet.getcurrent().throw, self.exception)
+ self.timer.forget()
+
return self
@property
View
@@ -32,7 +32,7 @@
from functools import partial
-from evy.uv.interface import libuv, ffi, handle_is_active
+from evy.uv.interface import libuv, ffi, handle_is_active, cast_to_handle
@@ -68,7 +68,6 @@ def __init__(self, _hub, ref = True):
## .. and another one for stopping it
if self.libuv_stop_this_watcher:
- assert self._uv_handle
self._stop_func = partial(self.libuv_stop_this_watcher, self._uv_handle)
def _run_callback(self, handle, *args):
@@ -177,9 +176,16 @@ def active(self):
return handle_is_active(self._uv_handle)
##
- ## handles (internal)
+ ## handles
##
+ @property
+ def handle(self):
+ """
+ Return the uv_handle for this watcher
+ """
+ return cast_to_handle(self._uv_handle)
+
def _new_libuv_handle(self):
"""
Return a new libuv C handle for this watcher
@@ -343,8 +349,7 @@ def start(self, callback, *args, **kwargs):
self._libuv_unref()
- if update:
- libuv.uv_update_time(self.hub._uv_ptr)
+ if update: libuv.uv_update_time(self.hub._uv_ptr)
libuv.uv_timer_start(self._uv_handle, self._cb, self._after, self._repeat)
View
@@ -83,20 +83,6 @@ def wrapped (*a, **kw):
return skipped_wrapper
-def requires_twisted (func):
- """ Decorator that skips a test if Twisted is not present."""
-
- def requirement (_f):
- from evy.hubs import get_hub
-
- try:
- return 'Twisted' in type(get_hub()).__name__
- except Exception:
- return False
-
- return skip_unless(requirement)(func)
-
-
def using_pyevent (_f):
from evy.hubs import get_hub
Oops, something went wrong.

0 comments on commit 7aa94eb

Please sign in to comment.