diff options
Diffstat (limited to 'python/gevent/libev/corecext.pyx')
-rw-r--r-- | python/gevent/libev/corecext.pyx | 2186 |
1 files changed, 708 insertions, 1478 deletions
diff --git a/python/gevent/libev/corecext.pyx b/python/gevent/libev/corecext.pyx index a7fb888..793b002 100644 --- a/python/gevent/libev/corecext.pyx +++ b/python/gevent/libev/corecext.pyx @@ -1,14 +1,37 @@ -# Generated by cythonpp.py on 2017-06-05 11:30:19 # Copyright (c) 2009-2012 Denis Bilenko. See LICENSE for details. -# This directive, supported in Cython 0.24+, causes sources files to be -# much smaller and thus cythonpp.py to be slightly faster. But it does make -# debugging more difficult. -# cython: emit_code_comments=False + +# This first directive, supported in Cython 0.24+, causes sources +# files to be *much* smaller when it's false (139,027 LOC vs 35,000 +# LOC) and thus cythonpp.py (and probably the compiler; also Visual C +# has limits on source file sizes) to be faster (73s vs 46s). But it does +# make debugging more difficult. Auto-pickling was added in 0.26, and +# that's a new feature that we don't need or want to allow in a gevent +# point release. + +# cython: emit_code_comments=False, auto_pickle=False + +# NOTE: We generally cannot use the Cython IF directive as documented +# at +# http://cython.readthedocs.io/en/latest/src/userguide/language_basics.html#conditional-compilation +# (e.g., IF UNAME_SYSNAME == "Windows") because when Cython says +# "compilation", it means when *Cython* compiles, not when the C +# compiler compiles. We distribute an sdist with a single pre-compiled +# C file for all platforms so that end users that don't use a binary +# wheel don't have to sit through cythonpp and other steps the Makefile does. +# See https://github.com/gevent/gevent/issues/1076 + cimport cython cimport libev -# Note this is not the standard cython 'cpython' (which has a backwards compat alias of 'python') -# it's our custom def. If it's not on the include path, we get warned. -from python cimport * + +from cpython.ref cimport Py_INCREF +from cpython.ref cimport Py_DECREF +from cpython.mem cimport PyMem_Malloc +from cpython.mem cimport PyMem_Free +from libc.errno cimport errno + +cdef extern from "Python.h": + int Py_ReprEnter(object) + void Py_ReprLeave(object) # Work around lack of absolute_import in Cython # Note for PY3: not doing so will leave reference to locals() on import @@ -17,6 +40,7 @@ sys = __import__('sys', level=0) os = __import__('os', level=0) traceback = __import__('traceback', level=0) signalmodule = __import__('signal', level=0) +getswitchinterval = __import__('gevent', level=0).getswitchinterval __all__ = ['get_version', @@ -51,9 +75,6 @@ cdef extern from "callbacks.h": void gevent_call(loop, callback) void gevent_noop(libev.ev_loop, void*, int) -cdef extern from *: - int errno - cdef extern from "stathelper.c": object _pystat_fromstructstat(void*) @@ -232,37 +253,163 @@ def embeddable_backends(): def time(): return libev.ev_time() +cdef bint _check_loop(loop loop) except -1: + if not loop._ptr: + raise ValueError('operation on destroyed loop') + return 1 -cdef bint _default_loop_destroyed = False +cdef public class callback [object PyGeventCallbackObject, type PyGeventCallback_Type]: + cdef public object callback + cdef public tuple args + cdef callback next + def __init__(self, callback, args): + self.callback = callback + self.args = args + def stop(self): + self.callback = None + self.args = None + close = stop + # Note, that __nonzero__ and pending are different + # nonzero is used in contexts where we need to know whether to schedule another callback, + # so it's true if it's pending or currently running + # 'pending' has the same meaning as libev watchers: it is cleared before entering callback + def __nonzero__(self): + # it's nonzero if it's pending or currently executing + return self.args is not None + + @property + def pending(self): + return self.callback is not None + + def __repr__(self): + if Py_ReprEnter(self) != 0: + return "<...>" + try: + format = self._format() + result = "<%s at 0x%x%s" % (self.__class__.__name__, id(self), format) + if self.pending: + result += " pending" + if self.callback is not None: + result += " callback=%r" % (self.callback, ) + if self.args is not None: + result += " args=%r" % (self.args, ) + if self.callback is None and self.args is None: + result += " stopped" + return result + ">" + finally: + Py_ReprLeave(self) + + def _format(self): + return '' + +DEF CALLBACK_CHECK_COUNT = 50 + +@cython.final +@cython.internal +cdef class CallbackFIFO(object): + cdef callback head + cdef callback tail + + def __init__(self): + self.head = None + self.tail = None + + cdef inline callback popleft(self): + cdef callback head = self.head + self.head = head.next + if self.head is self.tail or self.head is None: + self.tail = None + head.next = None + return head + + + cdef inline append(self, callback new_tail): + assert not new_tail.next + if self.tail is None: + if self.head is None: + # Completely empty, so this + # is now our head + self.head = new_tail + return + self.tail = self.head + + + assert self.head is not None + old_tail = self.tail + old_tail.next = new_tail + self.tail = new_tail + + def __nonzero__(self): + return self.head is not None + + def __len__(self): + cdef Py_ssize_t count = 0 + head = self.head + while head is not None: + count += 1 + head = head.next + return count + + def __iter__(self): + cdef list objects = [] + head = self.head + while head is not None: + objects.append(head) + head = head.next + return iter(objects) + + cdef bint has_callbacks(self): + return self.head + + def __repr__(self): + return "<callbacks@%r len=%d head=%r tail=%r>" % (id(self), len(self), self.head, self.tail) cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]: - cdef libev.ev_loop* _ptr - cdef public object error_handler + ## embedded struct members cdef libev.ev_prepare _prepare - cdef public list _callbacks cdef libev.ev_timer _timer0 -#ifdef _WIN32 + # We'll only actually start this timer if we're on Windows, + # but it doesn't hurt to compile it in on all platforms. cdef libev.ev_timer _periodic_signal_checker -#endif - def __init__(self, object flags=None, object default=None, size_t ptr=0): + ## pointer members + cdef public object error_handler + cdef libev.ev_loop* _ptr + cdef public CallbackFIFO _callbacks + + ## data members + cdef bint starting_timer_may_update_loop_time + # We must capture the 'default' state at initialiaztion + # time. Destroying the default loop in libev sets + # the libev internal pointer to 0, and ev_is_default_loop will + # no longer work. + cdef bint _default + + def __cinit__(self, object flags=None, object default=None, libev.intptr_t ptr=0): + self.starting_timer_may_update_loop_time = 0 + self._default = 0 + libev.ev_prepare_init(&self._prepare, + <void*>gevent_run_callbacks) + libev.ev_timer_init(&self._periodic_signal_checker, + <void*>gevent_periodic_signal_check, + 0.3, 0.3) + libev.ev_timer_init(&self._timer0, + <void*>gevent_noop, + 0.0, 0.0) + cdef unsigned int c_flags cdef object old_handler = None - libev.ev_prepare_init(&self._prepare, <void*>gevent_run_callbacks) -#ifdef _WIN32 - libev.ev_timer_init(&self._periodic_signal_checker, <void*>gevent_periodic_signal_check, 0.3, 0.3) -#endif - libev.ev_timer_init(&self._timer0, <void*>gevent_noop, 0.0, 0.0) if ptr: self._ptr = <libev.ev_loop*>ptr + self._default = libev.ev_is_default_loop(self._ptr) else: c_flags = _flags_to_int(flags) _check_flags(c_flags) @@ -270,88 +417,121 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]: c_flags |= libev.EVFLAG_FORKCHECK if default is None: default = True - if _default_loop_destroyed: - default = False if default: + self._default = 1 self._ptr = libev.gevent_ev_default_loop(c_flags) if not self._ptr: raise SystemError("ev_default_loop(%s) failed" % (c_flags, )) -#ifdef _WIN32 - libev.ev_timer_start(self._ptr, &self._periodic_signal_checker) - libev.ev_unref(self._ptr) -#endif + if sys.platform == "win32": + libev.ev_timer_start(self._ptr, &self._periodic_signal_checker) + libev.ev_unref(self._ptr) else: self._ptr = libev.ev_loop_new(c_flags) if not self._ptr: raise SystemError("ev_loop_new(%s) failed" % (c_flags, )) if default or __SYSERR_CALLBACK is None: set_syserr_cb(self._handle_syserr) - libev.ev_prepare_start(self._ptr, &self._prepare) - libev.ev_unref(self._ptr) - self._callbacks = [] + + # Mark as not destroyed + libev.ev_set_userdata(self._ptr, self._ptr) + + libev.ev_prepare_start(self._ptr, &self._prepare) + libev.ev_unref(self._ptr) + + def __init__(self, object flags=None, object default=None, libev.intptr_t ptr=0): + self._callbacks = CallbackFIFO() cdef _run_callbacks(self): cdef callback cb cdef object callbacks - cdef int count = 1000 - libev.ev_timer_stop(self._ptr, &self._timer0) - while self._callbacks and count > 0: - callbacks = self._callbacks - self._callbacks = [] - for cb in callbacks: + cdef int count = CALLBACK_CHECK_COUNT + self.starting_timer_may_update_loop_time = True + cdef libev.ev_tstamp now = libev.ev_now(self._ptr) + cdef libev.ev_tstamp expiration = now + <libev.ev_tstamp>getswitchinterval() + + try: + libev.ev_timer_stop(self._ptr, &self._timer0) + while self._callbacks.head is not None: + cb = self._callbacks.popleft() + libev.ev_unref(self._ptr) - gevent_call(self, cb) + gevent_call(self, cb) # XXX: Why is this a C callback, not cython? count -= 1 - if self._callbacks: - libev.ev_timer_start(self._ptr, &self._timer0) - def _stop_watchers(self): + if count == 0 and self._callbacks.head is not None: + # We still have more to run but we've reached + # the end of one check group + count = CALLBACK_CHECK_COUNT + + libev.ev_now_update(self._ptr) + if libev.ev_now(self._ptr) >= expiration: + now = 0 + break + + if now != 0: + libev.ev_now_update(self._ptr) + if self._callbacks.head is not None: + libev.ev_timer_start(self._ptr, &self._timer0) + finally: + self.starting_timer_may_update_loop_time = False + + cdef _stop_watchers(self, libev.ev_loop* ptr): + if not ptr: + return + if libev.ev_is_active(&self._prepare): - libev.ev_ref(self._ptr) - libev.ev_prepare_stop(self._ptr, &self._prepare) -#ifdef _WIN32 + libev.ev_ref(ptr) + libev.ev_prepare_stop(ptr, &self._prepare) if libev.ev_is_active(&self._periodic_signal_checker): - libev.ev_ref(self._ptr) - libev.ev_timer_stop(self._ptr, &self._periodic_signal_checker) -#endif + libev.ev_ref(ptr) + libev.ev_timer_stop(ptr, &self._periodic_signal_checker) def destroy(self): - global _default_loop_destroyed - if self._ptr: - self._stop_watchers() + cdef libev.ev_loop* ptr = self._ptr + self._ptr = NULL + + if ptr: + if not libev.ev_userdata(ptr): + # Whoops! Program error. They destroyed the loop, + # using a different loop object. Our _ptr is still + # valid, but the libev loop is gone. Doing anything + # else with it will likely cause a crash. + return + # Mark as destroyed + libev.ev_set_userdata(ptr, NULL) + self._stop_watchers(ptr) if __SYSERR_CALLBACK == self._handle_syserr: set_syserr_cb(None) - if libev.ev_is_default_loop(self._ptr): - _default_loop_destroyed = True - libev.ev_loop_destroy(self._ptr) - self._ptr = NULL + libev.ev_loop_destroy(ptr) def __dealloc__(self): - if self._ptr: - self._stop_watchers() - if not libev.ev_is_default_loop(self._ptr): - libev.ev_loop_destroy(self._ptr) - self._ptr = NULL - - property ptr: - - def __get__(self): - return <size_t>self._ptr - - property WatcherType: - - def __get__(self): - return watcher + cdef libev.ev_loop* ptr = self._ptr + self._ptr = NULL + if ptr != NULL: + if not libev.ev_userdata(ptr): + # See destroy(). This is a bug in the caller. + return + self._stop_watchers(ptr) + if not self._default: + libev.ev_loop_destroy(ptr) + # Mark as destroyed + libev.ev_set_userdata(ptr, NULL) - property MAXPRI: + @property + def ptr(self): + return <size_t>self._ptr - def __get__(self): - return libev.EV_MAXPRI + @property + def WatcherType(self): + return watcher - property MINPRI: + @property + def MAXPRI(self): + return libev.EV_MAXPRI - def __get__(self): - return libev.EV_MINPRI + @property + def MINPRI(self): + return libev.EV_MINPRI def _handle_syserr(self, message, errno): if sys.version_info[0] >= 3: @@ -376,9 +556,7 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]: libev.ev_break(self._ptr, libev.EVBREAK_ONE) def run(self, nowait=False, once=False): - - if not self._ptr: - raise ValueError('operation on destroyed loop') + _check_loop(self) cdef unsigned int flags = 0 if nowait: flags |= libev.EVRUN_NOWAIT @@ -392,103 +570,71 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]: libev.ev_loop_fork(self._ptr) def ref(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') + _check_loop(self) libev.ev_ref(self._ptr) def unref(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') + _check_loop(self) libev.ev_unref(self._ptr) def break_(self, int how=libev.EVBREAK_ONE): - - if not self._ptr: - raise ValueError('operation on destroyed loop') + _check_loop(self) libev.ev_break(self._ptr, how) def verify(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') + _check_loop(self) libev.ev_verify(self._ptr) - def now(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') + cpdef libev.ev_tstamp now(self) except *: + _check_loop(self) return libev.ev_now(self._ptr) - def update(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') + cpdef void update_now(self) except *: + _check_loop(self) libev.ev_now_update(self._ptr) + update = update_now # Old name, deprecated. + def __repr__(self): return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), self._format()) - property default: - - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return True if libev.ev_is_default_loop(self._ptr) else False - - property iteration: - - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return libev.ev_iteration(self._ptr) - - property depth: - - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return libev.ev_depth(self._ptr) - - property backend_int: - - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return libev.ev_backend(self._ptr) - - property backend: + @property + def default(self): + # If we're destroyed, we are not the default loop anymore, + # as far as Python is concerned. + return self._default if self._ptr else False + + @property + def iteration(self): + _check_loop(self) + return libev.ev_iteration(self._ptr) + + @property + def depth(self): + _check_loop(self) + return libev.ev_depth(self._ptr) + + @property + def backend_int(self): + _check_loop(self) + return libev.ev_backend(self._ptr) + + @property + def backend(self): + _check_loop(self) + cdef unsigned int backend = libev.ev_backend(self._ptr) + for key, value in _flags: + if key == backend: + return value + return backend + + @property + def pendingcnt(self): + _check_loop(self) + return libev.ev_pending_count(self._ptr) - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - cdef unsigned int backend = libev.ev_backend(self._ptr) - for key, value in _flags: - if key == backend: - return value - return backend - - property pendingcnt: - - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return libev.ev_pending_count(self._ptr) - -#ifdef _WIN32 def io(self, libev.vfd_socket_t fd, int events, ref=True, priority=None): return io(self, fd, events, ref, priority) -#else - def io(self, int fd, int events, ref=True, priority=None): - return io(self, fd, events, ref, priority) -#endif def timer(self, double after, double repeat=0.0, ref=True, priority=None): return timer(self, after, repeat, ref, priority) @@ -508,13 +654,15 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]: def fork(self, ref=True, priority=None): return fork(self, ref, priority) - def async(self, ref=True, priority=None): - return async(self, ref, priority) + def async_(self, ref=True, priority=None): + return async_(self, ref, priority) -#ifdef _WIN32 -#else + # cython doesn't enforce async as a keyword + async = async_ def child(self, int pid, bint trace=0, ref=True): + if sys.platform == 'win32': + raise AttributeError("Child watchers are not supported on Windows") return child(self, pid, trace, ref) def install_sigchld(self): @@ -523,15 +671,11 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]: def reset_sigchld(self): libev.gevent_reset_sigchld_handler() -#endif - def stat(self, str path, float interval=0.0, ref=True, priority=None): return stat(self, path, interval, ref, priority) def run_callback(self, func, *args): - - if not self._ptr: - raise ValueError('operation on destroyed loop') + _check_loop(self) cdef callback cb = callback(func, args) self._callbacks.append(cb) libev.ev_ref(self._ptr) @@ -541,20 +685,15 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]: if not self._ptr: return 'destroyed' cdef object msg = self.backend - if self.default: + if self._default: msg += ' default' msg += ' pending=%s' % self.pendingcnt -#ifdef LIBEV_EMBED msg += self._format_details() -#endif return msg -#ifdef LIBEV_EMBED - def _format_details(self): cdef str msg = '' cdef object fileno = self.fileno() - cdef object sigfd = None cdef object activecnt = None try: sigfd = self.sigfd @@ -568,135 +707,241 @@ cdef public class loop [object PyGeventLoopObject, type PyGeventLoop_Type]: msg += ' ref=' + repr(activecnt) if fileno is not None: msg += ' fileno=' + repr(fileno) - if sigfd is not None and sigfd != -1: - msg += ' sigfd=' + repr(sigfd) return msg def fileno(self): cdef int fd if self._ptr: - fd = self._ptr.backend_fd + fd = libev.gevent_ev_loop_backend_fd(self._ptr) if fd >= 0: return fd - property activecnt: - - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return self._ptr.activecnt + @property + def activecnt(self): + _check_loop(self) + return libev.gevent_ev_loop_activecnt(self._ptr) - property sig_pending: + @property + def sig_pending(self): + _check_loop(self) + return libev.gevent_ev_loop_sig_pending(self._ptr) - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return self._ptr.sig_pending - -#if EV_USE_SIGNALFD - property sigfd: - - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return self._ptr.sigfd -#endif - - property origflags: - - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return _flags_to_list(self._ptr.origflags) - - property origflags_int: - - def __get__(self): - - if not self._ptr: - raise ValueError('operation on destroyed loop') - return self._ptr.origflags - -#endif - - -cdef public class callback [object PyGeventCallbackObject, type PyGeventCallback_Type]: - cdef public object callback - cdef public tuple args - - def __init__(self, callback, args): - self.callback = callback - self.args = args - - def stop(self): - self.callback = None - self.args = None - - # Note, that __nonzero__ and pending are different - # nonzero is used in contexts where we need to know whether to schedule another callback, - # so it's true if it's pending or currently running - # 'pending' has the same meaning as libev watchers: it is cleared before entering callback - - def __nonzero__(self): - # it's nonzero if it's pending or currently executing - return self.args is not None - - property pending: - - def __get__(self): - return self.callback is not None - - def __repr__(self): - if Py_ReprEnter(<PyObjectPtr>self) != 0: - return "<...>" - try: - format = self._format() - result = "<%s at 0x%x%s" % (self.__class__.__name__, id(self), format) - if self.pending: - result += " pending" - if self.callback is not None: - result += " callback=%r" % (self.callback, ) - if self.args is not None: - result += " args=%r" % (self.args, ) - if self.callback is None and self.args is None: - result += " stopped" - return result + ">" - finally: - Py_ReprLeave(<PyObjectPtr>self) + @property + def origflags(self): + return _flags_to_list(self.origflags_int) - def _format(self): - return '' + @property + def origflags_int(self): + _check_loop(self) + return libev.gevent_ev_loop_origflags(self._ptr) + @property + def sigfd(self): + _check_loop(self) + fd = libev.gevent_ev_loop_sigfd(self._ptr) + if fd >= 0: + return fd + # Explicitly not EV_USE_SIGNALFD + raise AttributeError("sigfd") # about readonly _flags attribute: -# bit #1 set if object owns Python reference to itself (Py_INCREF was called and we must call Py_DECREF later) +# bit #1 set if object owns Python reference to itself (Py_INCREF was +# called and we must call Py_DECREF later) +DEF FLAG_WATCHER_OWNS_PYREF = 1 << 0 # 0x1 # bit #2 set if ev_unref() was called and we must call ev_ref() later +DEF FLAG_WATCHER_NEEDS_EVREF = 1 << 1 # 0x2 # bit #3 set if user wants to call ev_unref() before start() +DEF FLAG_WATCHER_UNREF_BEFORE_START = 1 << 2 # 0x4 +# bits 2 and 3 are *both* set when we are active, but the user +# request us not to be ref'd anymore. We unref us (because going active will +# ref us) and then make a note of this in the future +DEF FLAG_WATCHER_MASK_UNREF_NEEDS_REF = 0x6 +cdef void _python_incref(watcher self): + if not self._flags & FLAG_WATCHER_OWNS_PYREF: + Py_INCREF(self) + self._flags |= FLAG_WATCHER_OWNS_PYREF +cdef void _python_decref(watcher self): + if self._flags & FLAG_WATCHER_OWNS_PYREF: + Py_DECREF(self) + self._flags &= ~FLAG_WATCHER_OWNS_PYREF +cdef void _libev_ref(watcher self): + if self._flags & FLAG_WATCHER_NEEDS_EVREF: + libev.ev_ref(self.loop._ptr) + self._flags &= ~FLAG_WATCHER_NEEDS_EVREF +cdef void _libev_unref(watcher self): + if self._flags & FLAG_WATCHER_MASK_UNREF_NEEDS_REF == FLAG_WATCHER_UNREF_BEFORE_START: + libev.ev_unref(self.loop._ptr) + self._flags |= FLAG_WATCHER_NEEDS_EVREF +ctypedef void (*start_stop_func)(libev.ev_loop*, void*) nogil +cdef struct start_and_stop: + start_stop_func start + start_stop_func stop +cdef start_and_stop make_ss(void* start, void* stop): + cdef start_and_stop result = start_and_stop(<start_stop_func>start, <start_stop_func>stop) + return result +cdef bint _watcher_start(watcher self, object callback, tuple args) except -1: + # This method should be called by subclasses of watcher, if they + # override the python-level `start` function: they've already paid + # for argument unpacking, and `start` cannot be cpdef since it + # uses varargs. + + # We keep this as a function, not a cdef method of watcher. + # If it's a cdef method, it could potentially be overridden + # by a subclass, which means that the watcher gains a pointer to a + # function table (vtable), making each object 8 bytes larger. + + _check_loop(self.loop) + if callback is None or not callable(callback): + raise TypeError("Expected callable, not %r" % (callback, )) + self._callback = callback + self.args = args + _libev_unref(self) + _python_incref(self) + self.__ss.start(self.loop._ptr, self.__watcher) + return 1 +cdef public class watcher [object PyGeventWatcherObject, type PyGeventWatcher_Type]: + """Abstract base class for all the watchers""" + ## pointer members + cdef public loop loop + cdef object _callback + cdef public tuple args + # By keeping a __watcher cached, the size of the io and timer + # structs becomes 152 bytes and child is 160 and stat is 512 (when + # the start_and_stop is inlined). On 64-bit macOS CPython 2.7. I + # hoped that using libev's data pointer and allocating the + # watchers directly and not as inline members would result in + # overall savings thanks to better padding, but it didn't. And it + # added lots of casts, making the code ugly. + + # Table: + # gevent ver | 1.2 | This | +data + # Watcher Kind | | | + # Timer | 120 | 152 | 160 + # IO | 120 | 152 | 160 + # Child | 128 | 160 | 168 + # Stat | 480 | 512 | 512 + cdef libev.ev_watcher* __watcher + + # By inlining the start_and_stop struct, instead of taking the address + # of a static struct or using the watcher's data pointer, we + # use an additional pointer of memory and incur an additional pointer copy + # on creation. + # But we use fewer pointer accesses for start/stop, and they have + # better cache locality. (Then again, we're bigger). + # Right now we're going for size, so we use the pointer. IO/Timer objects + # are then 144 bytes. + cdef start_and_stop* __ss + + ## Int members + + # Our subclasses will declare the ev_X struct + # as an inline member. This is good for locality, but + # probably bad for alignment, as it will get tacked on + # immediately after our data. + + # But all ev_watchers start with some ints, so maybe we can help that + # out by putting our ints here. + cdef readonly unsigned int _flags + + def __init__(self, loop loop, ref=True, priority=None): + if not self.__watcher or not self.__ss.start or not self.__ss.stop: + raise ValueError("Cannot construct a bare watcher") + self.loop = loop + self._flags = 0 if ref else FLAG_WATCHER_UNREF_BEFORE_START + if priority is not None: + libev.ev_set_priority(self.__watcher, priority) + @property + def ref(self): + return False if self._flags & 4 else True + + @ref.setter + def ref(self, object value): + _check_loop(self.loop) + if value: + # self.ref should be true after this. + if self.ref: + return # ref is already True + + if self._flags & FLAG_WATCHER_NEEDS_EVREF: # ev_unref was called, undo + libev.ev_ref(self.loop._ptr) + # do not want unref, no outstanding unref + self._flags &= ~FLAG_WATCHER_MASK_UNREF_NEEDS_REF + else: + # self.ref must be false after this + if not self.ref: + return # ref is already False + self._flags |= FLAG_WATCHER_UNREF_BEFORE_START + if not self._flags & FLAG_WATCHER_NEEDS_EVREF and libev.ev_is_active(self.__watcher): + libev.ev_unref(self.loop._ptr) + self._flags |= FLAG_WATCHER_NEEDS_EVREF + + @property + def callback(self): + return self._callback + + @callback.setter + def callback(self, object callback): + if callback is not None and not callable(callback): + raise TypeError("Expected callable, not %r" % (callback, )) + self._callback = callback + + @property + def priority(self): + return libev.ev_priority(self.__watcher) + + @priority.setter + def priority(self, int priority): + cdef libev.ev_watcher* w = self.__watcher + if libev.ev_is_active(w): + raise AttributeError("Cannot set priority of an active watcher") + libev.ev_set_priority(w, priority) + + @property + def active(self): + return True if libev.ev_is_active(self.__watcher) else False + + @property + def pending(self): + return True if libev.ev_is_pending(self.__watcher) else False -cdef public class watcher [object PyGeventWatcherObject, type PyGeventWatcher_Type]: - """Abstract base class for all the watchers""" + def start(self, object callback, *args): + _watcher_start(self, callback, args) + + def stop(self): + _check_loop(self.loop) + _libev_ref(self) + # The callback cannot possibly fire while we are executing, + # so this is safe. + self._callback = None + self.args = None + self.__ss.stop(self.loop._ptr, self.__watcher) + _python_decref(self) + + def feed(self, int revents, object callback, *args): + _check_loop(self.loop) + self.callback = callback + self.args = args + _libev_unref(self) + libev.ev_feed_event(self.loop._ptr, self.__watcher, revents) + _python_incref(self) def __repr__(self): - if Py_ReprEnter(<PyObjectPtr>self) != 0: + if Py_ReprEnter(self) != 0: return "<...>" try: format = self._format() @@ -711,450 +956,125 @@ cdef public class watcher [object PyGeventWatcherObject, type PyGeventWatcher_Ty result += " args=%r" % (self.args, ) return result + ">" finally: - Py_ReprLeave(<PyObjectPtr>self) + Py_ReprLeave(self) def _format(self): return '' + def close(self): + self.stop() -cdef public class io(watcher) [object PyGeventIOObject, type PyGeventIO_Type]: - - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags - cdef libev.ev_io _watcher - - property ref: - - def __get__(self): - return False if self._flags & 4 else True - - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - - property callback: - - def __get__(self): - return self._callback - - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_io_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 + def __enter__(self): + return self - property priority: + def __exit__(self, t, v, tb): + self.close() + return - def __get__(self): - return libev.ev_priority(&self._watcher) +cdef start_and_stop io_ss = make_ss(<void*>libev.ev_io_start, <void*>libev.ev_io_stop) - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) +cdef public class io(watcher) [object PyGeventIOObject, type PyGeventIO_Type]: - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 + cdef libev.ev_io _watcher def start(self, object callback, *args, pass_events=False): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback if pass_events: - self.args = (GEVENT_CORE_EVENTS, ) + args - else: - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_io_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - property active: - - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False - - - property pending: - - def __get__(self): - return True if libev.ev_is_pending(&self._watcher) else False - -#ifdef _WIN32 + args = (GEVENT_CORE_EVENTS, ) + args + _watcher_start(self, callback, args) def __init__(self, loop loop, libev.vfd_socket_t fd, int events, ref=True, priority=None): - if events & ~(libev.EV__IOFDSET | libev.EV_READ | libev.EV_WRITE): - raise ValueError('illegal event mask: %r' % events) - cdef int vfd = libev.vfd_open(fd) - libev.vfd_free(self._watcher.fd) - libev.ev_io_init(&self._watcher, <void *>gevent_callback_io, vfd, events) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) - -#else + watcher.__init__(self, loop, ref, priority) - def __init__(self, loop loop, int fd, int events, ref=True, priority=None): + def __cinit__(self, loop loop, libev.vfd_socket_t fd, int events, ref=True, priority=None): if fd < 0: raise ValueError('fd must be non-negative: %r' % fd) if events & ~(libev.EV__IOFDSET | libev.EV_READ | libev.EV_WRITE): raise ValueError('illegal event mask: %r' % events) - libev.ev_io_init(&self._watcher, <void *>gevent_callback_io, fd, events) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) - -#endif - - property fd: - - def __get__(self): - return libev.vfd_get(self._watcher.fd) + # All the vfd_functions are no-ops on POSIX + cdef int vfd = libev.vfd_open(fd) + libev.ev_io_init(&self._watcher, <void *>gevent_callback_io, vfd, events) + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &io_ss - def __set__(self, long fd): - if libev.ev_is_active(&self._watcher): - raise AttributeError("'io' watcher attribute 'fd' is read-only while watcher is active") - cdef int vfd = libev.vfd_open(fd) - libev.vfd_free(self._watcher.fd) - libev.ev_io_init(&self._watcher, <void *>gevent_callback_io, vfd, self._watcher.events) + def __dealloc__(self): + libev.vfd_free(self._watcher.fd) - property events: + @property + def fd(self): + return libev.vfd_get(self._watcher.fd) - def __get__(self): - return self._watcher.events + @fd.setter + def fd(self, long fd): + if libev.ev_is_active(&self._watcher): + raise AttributeError("'io' watcher attribute 'fd' is read-only while watcher is active") + cdef int vfd = libev.vfd_open(fd) + libev.vfd_free(self._watcher.fd) + libev.ev_io_init(&self._watcher, <void *>gevent_callback_io, vfd, self._watcher.events) - def __set__(self, int events): - if libev.ev_is_active(&self._watcher): - raise AttributeError("'io' watcher attribute 'events' is read-only while watcher is active") - libev.ev_io_init(&self._watcher, <void *>gevent_callback_io, self._watcher.fd, events) + @property + def events(self): + return self._watcher.events - property events_str: + @events.setter + def events(self, int events): + if libev.ev_is_active(&self._watcher): + raise AttributeError("'io' watcher attribute 'events' is read-only while watcher is active") + libev.ev_io_init(&self._watcher, <void *>gevent_callback_io, self._watcher.fd, events) - def __get__(self): - return _events_to_str(self._watcher.events) + @property + def events_str(self): + return _events_to_str(self._watcher.events) def _format(self): return ' fd=%s events=%s' % (self.fd, self.events_str) -#ifdef _WIN32 - - def __cinit__(self): - self._watcher.fd = -1; - - def __dealloc__(self): - libev.vfd_free(self._watcher.fd) - -#endif - +cdef start_and_stop timer_ss = make_ss(<void*>libev.ev_timer_start, <void*>libev.ev_timer_stop) cdef public class timer(watcher) [object PyGeventTimerObject, type PyGeventTimer_Type]: - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags cdef libev.ev_timer _watcher - property ref: - - def __get__(self): - return False if self._flags & 4 else True - - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - - property callback: - - def __get__(self): - return self._callback - - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_timer_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 - - property priority: - - def __get__(self): - return libev.ev_priority(&self._watcher) - - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) - - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - def start(self, object callback, *args, update=True): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - if update: - libev.ev_now_update(self.loop._ptr) - libev.ev_timer_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - property active: - - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False - - - property pending: - - def __get__(self): - return True if libev.ev_is_pending(&self._watcher) else False - - def __init__(self, loop loop, double after=0.0, double repeat=0.0, ref=True, priority=None): + def __cinit__(self, loop loop, double after=0.0, double repeat=0.0, ref=True, priority=None): if repeat < 0.0: raise ValueError("repeat must be positive or zero: %r" % repeat) libev.ev_timer_init(&self._watcher, <void *>gevent_callback_timer, after, repeat) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &timer_ss - property at: + def __init__(self, loop loop, double after=0.0, double repeat=0.0, ref=True, priority=None): + watcher.__init__(self, loop, ref, priority) - def __get__(self): - return self._watcher.at + def start(self, object callback, *args, update=None): + update = update if update is not None else self.loop.starting_timer_may_update_loop_time + if update: + self.loop.update_now() + _watcher_start(self, callback, args) + + @property + def at(self): + return self._watcher.at # QQQ: add 'after' and 'repeat' properties? def again(self, object callback, *args, update=True): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') + _check_loop(self.loop) self.callback = callback self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 + _libev_unref(self) if update: libev.ev_now_update(self.loop._ptr) libev.ev_timer_again(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - -cdef public class signal(watcher) [object PyGeventSignalObject, type PyGeventSignal_Type]: - - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags - cdef libev.ev_signal _watcher - - property ref: - - def __get__(self): - return False if self._flags & 4 else True - - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - - property callback: - - def __get__(self): - return self._callback + _python_incref(self) - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_signal_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 - - property priority: - - def __get__(self): - return libev.ev_priority(&self._watcher) - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) - - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 +cdef start_and_stop signal_ss = make_ss(<void*>libev.ev_signal_start, <void*>libev.ev_signal_stop) - def start(self, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_signal_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - property active: - - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False - - - property pending: +cdef public class signal(watcher) [object PyGeventSignalObject, type PyGeventSignal_Type]: - def __get__(self): - return True if libev.ev_is_pending(&self._watcher) else False + cdef libev.ev_signal _watcher - def __init__(self, loop loop, int signalnum, ref=True, priority=None): + def __cinit__(self, loop loop, int signalnum, ref=True, priority=None): if signalnum < 1 or signalnum >= signalmodule.NSIG: raise ValueError('illegal signal number: %r' % signalnum) # still possible to crash on one of libev's asserts: @@ -1163,872 +1083,140 @@ cdef public class signal(watcher) [object PyGeventSignalObject, type PyGeventSig # 2) "libev: a signal must not be attached to two different loops" # we probably could check that in LIBEV_EMBED mode, but not in general libev.ev_signal_init(&self._watcher, <void *>gevent_callback_signal, signalnum) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) - + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &signal_ss -cdef public class idle(watcher) [object PyGeventIdleObject, type PyGeventIdle_Type]: - - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags - cdef libev.ev_idle _watcher - - property ref: - - def __get__(self): - return False if self._flags & 4 else True - - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - - property callback: - - def __get__(self): - return self._callback - - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_idle_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 - - property priority: - - def __get__(self): - return libev.ev_priority(&self._watcher) + def __init__(self, loop loop, int signalnum, ref=True, priority=None): + watcher.__init__(self, loop, ref, priority) - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - def start(self, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_idle_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 +cdef start_and_stop idle_ss = make_ss(<void*>libev.ev_idle_start, <void*>libev.ev_idle_stop) - property active: +cdef public class idle(watcher) [object PyGeventIdleObject, type PyGeventIdle_Type]: - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False + cdef libev.ev_idle _watcher - - property pending: + def __cinit__(self, loop loop, ref=True, priority=None): + libev.ev_idle_init(&self._watcher, <void*>gevent_callback_idle) + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &idle_ss - def __get__(self): - return True if libev.ev_is_pending(&self._watcher) else False - - def __init__(self, loop loop , ref=True, priority=None): - libev.ev_idle_init(&self._watcher, <void *>gevent_callback_idle ) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) +cdef start_and_stop prepare_ss = make_ss(<void*>libev.ev_prepare_start, <void*>libev.ev_prepare_stop) cdef public class prepare(watcher) [object PyGeventPrepareObject, type PyGeventPrepare_Type]: - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags cdef libev.ev_prepare _watcher - property ref: - - def __get__(self): - return False if self._flags & 4 else True + def __cinit__(self, loop loop, ref=True, priority=None): + libev.ev_prepare_init(&self._watcher, <void*>gevent_callback_prepare) + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &prepare_ss - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - property callback: - - def __get__(self): - return self._callback - - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_prepare_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 - - property priority: - - def __get__(self): - return libev.ev_priority(&self._watcher) - - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) - - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - def start(self, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_prepare_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - property active: - - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False - - - property pending: - - def __get__(self): - return True if libev.ev_is_pending(&self._watcher) else False - - - def __init__(self, loop loop , ref=True, priority=None): - libev.ev_prepare_init(&self._watcher, <void *>gevent_callback_prepare ) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) +cdef start_and_stop check_ss = make_ss(<void*>libev.ev_check_start, <void*>libev.ev_check_stop) cdef public class check(watcher) [object PyGeventCheckObject, type PyGeventCheck_Type]: - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags cdef libev.ev_check _watcher - property ref: - - def __get__(self): - return False if self._flags & 4 else True - - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - - property callback: - - def __get__(self): - return self._callback + def __cinit__(self, loop loop, ref=True, priority=None): + libev.ev_check_init(&self._watcher, <void*>gevent_callback_check) + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &check_ss - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_check_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 - - property priority: - - def __get__(self): - return libev.ev_priority(&self._watcher) - - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) - - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - def start(self, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_check_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - property active: - - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False - - - property pending: - - def __get__(self): - return True if libev.ev_is_pending(&self._watcher) else False - - - def __init__(self, loop loop , ref=True, priority=None): - libev.ev_check_init(&self._watcher, <void *>gevent_callback_check ) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) +cdef start_and_stop fork_ss = make_ss(<void*>libev.ev_fork_start, <void*>libev.ev_fork_stop) cdef public class fork(watcher) [object PyGeventForkObject, type PyGeventFork_Type]: - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags cdef libev.ev_fork _watcher - property ref: - - def __get__(self): - return False if self._flags & 4 else True - - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - - property callback: - - def __get__(self): - return self._callback - - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_fork_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 - - property priority: - - def __get__(self): - return libev.ev_priority(&self._watcher) - - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) - - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - def start(self, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_fork_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - property active: + def __cinit__(self, loop loop, ref=True, priority=None): + libev.ev_fork_init(&self._watcher, <void*>gevent_callback_fork) + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &fork_ss - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False - - property pending: +cdef start_and_stop async_ss = make_ss(<void*>libev.ev_async_start, <void*>libev.ev_async_stop) - def __get__(self): - return True if libev.ev_is_pending(&self._watcher) else False - - - def __init__(self, loop loop , ref=True, priority=None): - libev.ev_fork_init(&self._watcher, <void *>gevent_callback_fork ) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) +cdef public class async_(watcher) [object PyGeventAsyncObject, type PyGeventAsync_Type]: - -cdef public class async(watcher) [object PyGeventAsyncObject, type PyGeventAsync_Type]: - - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags cdef libev.ev_async _watcher - property ref: - - def __get__(self): - return False if self._flags & 4 else True - - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - - property callback: - - def __get__(self): - return self._callback - - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_async_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 - - property priority: + @property + def pending(self): + # Note the use of ev_async_pending instead of ev_is_pending + return True if libev.ev_async_pending(&self._watcher) else False - def __get__(self): - return libev.ev_priority(&self._watcher) + def __cinit__(self, loop loop, ref=True, priority=None): + libev.ev_async_init(&self._watcher, <void*>gevent_callback_async) + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &async_ss - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) - - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - def start(self, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_async_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - property active: - - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False - - property pending: - - def __get__(self): - return True if libev.ev_async_pending(&self._watcher) else False - - - def __init__(self, loop loop , ref=True, priority=None): - libev.ev_async_init(&self._watcher, <void *>gevent_callback_async ) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) def send(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') + _check_loop(self.loop) libev.ev_async_send(self.loop._ptr, &self._watcher) -#ifdef _WIN32 -#else +async = async_ + +cdef start_and_stop child_ss = make_ss(<void*>libev.ev_child_start, <void*>libev.ev_child_stop) cdef public class child(watcher) [object PyGeventChildObject, type PyGeventChild_Type]: - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags cdef libev.ev_child _watcher - property ref: - - def __get__(self): - return False if self._flags & 4 else True - - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - - property callback: - - def __get__(self): - return self._callback - - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_child_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 - - property priority: - - def __get__(self): - return libev.ev_priority(&self._watcher) - - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) - - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - def start(self, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_child_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - property active: - - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False - - - property pending: - - def __get__(self): - return True if libev.ev_is_pending(&self._watcher) else False - - def __init__(self, loop loop, int pid, bint trace=0, ref=True): + def __cinit__(self, loop loop, int pid, bint trace=0, ref=True): + if sys.platform == 'win32': + raise AttributeError("Child watchers are not supported on Windows") if not loop.default: raise TypeError('child watchers are only available on the default loop') libev.gevent_install_sigchld_handler() libev.ev_child_init(&self._watcher, <void *>gevent_callback_child, pid, trace) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - - def _format(self): - return ' pid=%r rstatus=%r' % (self.pid, self.rstatus) - - property pid: + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &child_ss - def __get__(self): - return self._watcher.pid + def __init__(self, loop loop, int pid, bint trace=0, ref=True): + watcher.__init__(self, loop, ref, None) - property rpid: - def __get__(self): - return self._watcher.rpid + def _format(self): + return ' pid=%r rstatus=%r' % (self.pid, self.rstatus) - def __set__(self, int value): - self._watcher.rpid = value + @property + def pid(self): + return self._watcher.pid - property rstatus: + @property + def rpid(self): + return self._watcher.rpid - def __get__(self): - return self._watcher.rstatus + @rpid.setter + def rpid(self, int value): + self._watcher.rpid = value - def __set__(self, int value): - self._watcher.rstatus = value + @property + def rstatus(self): + return self._watcher.rstatus -#endif + @rstatus.setter + def rstatus(self, int value): + self._watcher.rstatus = value +cdef start_and_stop stat_ss = make_ss(<void*>libev.ev_stat_start, <void*>libev.ev_stat_stop) cdef public class stat(watcher) [object PyGeventStatObject, type PyGeventStat_Type]: - - cdef public loop loop - cdef object _callback - cdef public tuple args - cdef readonly int _flags cdef libev.ev_stat _watcher - - property ref: - - def __get__(self): - return False if self._flags & 4 else True - - def __set__(self, object value): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if value: - if not self._flags & 4: - return # ref is already True - if self._flags & 2: # ev_unref was called, undo - libev.ev_ref(self.loop._ptr) - self._flags &= ~6 # do not want unref, no outstanding unref - else: - if self._flags & 4: - return # ref is already False - self._flags |= 4 - if not self._flags & 2 and libev.ev_is_active(&self._watcher): - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - - property callback: - - def __get__(self): - return self._callback - - def __set__(self, object callback): - if not PyCallable_Check(<PyObjectPtr>callback) and callback is not None: - raise TypeError("Expected callable, not %r" % (callback, )) - self._callback = callback - - def stop(self): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if self._flags & 2: - libev.ev_ref(self.loop._ptr) - self._flags &= ~2 - libev.ev_stat_stop(self.loop._ptr, &self._watcher) - self._callback = None - self.args = None - if self._flags & 1: - Py_DECREF(<PyObjectPtr>self) - self._flags &= ~1 - - property priority: - - def __get__(self): - return libev.ev_priority(&self._watcher) - - def __set__(self, int priority): - if libev.ev_is_active(&self._watcher): - raise AttributeError("Cannot set priority of an active watcher") - libev.ev_set_priority(&self._watcher, priority) - - def feed(self, int revents, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_feed_event(self.loop._ptr, &self._watcher, revents) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - def start(self, object callback, *args): - - if not self.loop._ptr: - raise ValueError('operation on destroyed loop') - if callback is None: - raise TypeError('callback must be callable, not None') - self.callback = callback - self.args = args - if self._flags & 6 == 4: - libev.ev_unref(self.loop._ptr) - self._flags |= 2 - libev.ev_stat_start(self.loop._ptr, &self._watcher) - if not self._flags & 1: - Py_INCREF(<PyObjectPtr>self) - self._flags |= 1 - - property active: - - def __get__(self): - return True if libev.ev_is_active(&self._watcher) else False - - - property pending: - - def __get__(self): - return True if libev.ev_is_pending(&self._watcher) else False cdef readonly str path cdef readonly bytes _paths - def __init__(self, loop loop, str path, float interval=0.0, ref=True, priority=None): + def __cinit__(self, loop loop, str path, float interval=0.0, ref=True, priority=None): self.path = path cdef bytes paths if isinstance(path, unicode): @@ -2041,32 +1229,29 @@ cdef public class stat(watcher) [object PyGeventStatObject, type PyGeventStat_Ty paths = <bytes>path self._paths = paths libev.ev_stat_init(&self._watcher, <void *>gevent_callback_stat, <char*>paths, interval) - self.loop = loop - if ref: - self._flags = 0 - else: - self._flags = 4 - if priority is not None: - libev.ev_set_priority(&self._watcher, priority) + self.__watcher = <libev.ev_watcher*>&self._watcher + self.__ss = &stat_ss - property attr: + def __init__(self, loop loop, str path, float interval=0.0, ref=True, priority=None): + watcher.__init__(self, loop, ref, priority) - def __get__(self): - if not self._watcher.attr.st_nlink: - return - return _pystat_fromstructstat(&self._watcher.attr) - property prev: + @property + def attr(self): + if not self._watcher.attr.st_nlink: + return + return _pystat_fromstructstat(&self._watcher.attr) - def __get__(self): - if not self._watcher.prev.st_nlink: - return - return _pystat_fromstructstat(&self._watcher.prev) + @property + def prev(self): + if not self._watcher.prev.st_nlink: + return + return _pystat_fromstructstat(&self._watcher.prev) - property interval: + @property + def interval(self): + return self._watcher.interval - def __get__(self): - return self._watcher.interval __SYSERR_CALLBACK = None @@ -2094,8 +1279,8 @@ cpdef set_syserr_cb(callback): raise TypeError('Expected callable or None, got %r' % (callback, )) -#ifdef LIBEV_EMBED -LIBEV_EMBED = True + +LIBEV_EMBED = bool(libev.LIBEV_EMBED) EV_USE_FLOOR = libev.EV_USE_FLOOR EV_USE_CLOCK_SYSCALL = libev.EV_USE_CLOCK_SYSCALL EV_USE_REALTIME = libev.EV_USE_REALTIME @@ -2105,6 +1290,51 @@ EV_USE_INOTIFY = libev.EV_USE_INOTIFY EV_USE_SIGNALFD = libev.EV_USE_SIGNALFD EV_USE_EVENTFD = libev.EV_USE_EVENTFD EV_USE_4HEAP = libev.EV_USE_4HEAP -#else -LIBEV_EMBED = False -#endif + +# Things used in callbacks.c + +from cpython cimport PyErr_Fetch +from cpython cimport PyObject + +cdef public void gevent_handle_error(loop loop, object context): + cdef PyObject* typep + cdef PyObject* valuep + cdef PyObject* tracebackp + + cdef object type + cdef object value = None + cdef object traceback = None + cdef object result + + # If it was set, this will clear it, and we will own + # the references. + PyErr_Fetch(&typep, &valuep, &tracebackp) + # TODO: Should we call PyErr_Normalize? There's code in + # Hub.handle_error that works around what looks like an + # unnormalized exception. + + if not typep: + return + # This assignment will do a Py_INCREF + # on the value. We already own the reference + # returned from PyErr_Fetch, + # so we must decref immediately + type = <object>typep + Py_DECREF(type) + + if valuep: + value = <object>valuep + Py_DECREF(value) + if tracebackp: + traceback = <object>tracebackp + Py_DECREF(traceback) + + # If this method fails by raising an exception, + # cython will print it for us because we don't return a + # Python object and we don't declare an `except` clause. + loop.handle_error(context, type, value, traceback) + +cdef public tuple _empty_tuple = () + +cdef public object gevent_loop_run_callbacks(loop loop): + return loop._run_callbacks() |