--- a/qmsk/net/lib/event2/base.pyx Sat Sep 26 16:39:20 2009 +0300
+++ b/qmsk/net/lib/event2/base.pyx Sat Sep 26 21:46:36 2009 +0300
@@ -1,5 +1,6 @@
-from qmsk.net.lib.event2.base cimport *
-from qmsk.net.lib.event2.event cimport build_timeout
+from qmsk.net.lib.event2.event cimport build_timeout, event
+from qmsk.net.socket.platform cimport AF_UNIX, SOCK_STREAM
+cimport qmsk.net.py as py
class EventError (Exception) :
"""
@@ -19,6 +20,64 @@
self.func = func
self.msg = msg
+cdef lib.event* build_pysignal_ev (lib.event_base *ev_base, lib._ev_callback_t cb_func, void *cb_arg) except NULL :
+ """
+ This constructs an returns an event* which will activate the given callback when a Python signal handler runs.
+
+ The event is constructed as a persistent internal event, which means that it *must* be removed before trying to
+ release the event_base.
+ """
+
+ cdef lib.evutil_socket_t fds[2]
+
+ # construct the pipe/sockpair
+ if lib.evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0 :
+ raise EventCallError("evutil_socketpair(signals)")
+
+ # prep: nonblocking
+ # XXX: close-on-exec?
+ lib.evutil_make_socket_nonblocking(fds[0])
+ lib.evutil_make_socket_nonblocking(fds[1])
+
+ # build event for one end
+ cdef lib.event *ev = lib.event_new(ev_base, fds[1], lib.EV_READ | lib.EV_PERSIST, cb_func, cb_arg)
+
+ # set magic flag to not count as active
+ ev.ev_flags |= lib.EVLIST_INTERNAL
+
+ if ev == NULL :
+ raise EventCallError("event_new(signals)")
+
+ # register wakeup fd
+ py.PySignal_SetWakeupFd(fds[0])
+
+ return ev
+
+
+cdef void on_pysignal_wakeup (int sig, short what, void *arg) :
+ """
+ Python's signal handler trapped a signal, we need to run the signal handlers.
+
+ This is executed by libevent's event_base_loop as a normal event (using the wakeup fd built using
+ build_pysignal_ev), and hence the signal handlers will be run from "inside" of event_base.loop(). Exceptions
+ raised by the signal handlers should be propagated "out" of event_base.loop().
+ """
+
+ cdef event_base ev_base = <event_base> arg
+
+ try :
+ # run the real signal handlers for any pending signals
+ py.PyErr_CheckSignals()
+
+ except :
+ # TODO: implement propagate-to-event_base.loop()
+ # drop out of the event loop
+ ev_base.loopbreak()
+
+ # Cython will log it
+ raise
+
+
cdef class event_base :
def __init__ (self) :
@@ -28,6 +87,15 @@
if self.ev_base == NULL :
raise EventCallError("event_base_new")
+
+ # internal signal wakeup handler
+ # XXX: only the main event_base really needs one...
+ self.ev_pysignal = build_pysignal_ev(self.ev_base, on_pysignal_wakeup, <void *> self)
+
+ # XXX: this will keep the loop active?
+ if lib.event_add(self.ev_pysignal, NULL) < 0 :
+ raise EventCallError("event_add(signals)")
+
def reinit (self) :
"""
@@ -39,6 +107,7 @@
if lib.event_reinit(self.ev_base) < 0 :
raise EventCallError("event_reinit", "could not re-add all events")
+
property method :
def __get__ (self) :
"""
@@ -47,6 +116,7 @@
return lib.event_base_get_method(self.ev_base)
+
def loop (self, once = False, nonblock = False) :
"""
Run the event loop.
@@ -65,10 +135,13 @@
if nonblock :
flags |= lib.EVLOOP_NONBLOCK
+
# event_base_loop()
ret = lib.event_base_loop(self.ev_base, flags)
+
+ # check return status
if ret < 0 :
raise EventCallError("event_base_loop")
@@ -78,6 +151,7 @@
else :
return True
+
def loopexit (self, timeout = None) :
"""
Exit the event loop normally.
@@ -91,6 +165,7 @@
if lib.event_base_loopexit(self.ev_base, build_timeout(&tv, timeout)) < 0 :
raise EventCallError("event_base_loopexit")
+
def loopbreak (self) :
"""
Abort the event immediately.
@@ -102,12 +177,17 @@
if lib.event_base_loopbreak(self.ev_base) < 0 :
raise EventCallError("event_base_loopbreak")
+
def __dealloc__ (self) :
"""
Release the event_base's resources.
XXX: is this entirely safe re our events?
"""
+
+ if self.ev_pysignal :
+ # remove the internal signal wakeup event
+ lib.event_free(self.ev_pysignal)
lib.event_base_free(self.ev_base)
--- a/qmsk/net/lib/event2/lib.pxd Sat Sep 26 16:39:20 2009 +0300
+++ b/qmsk/net/lib/event2/lib.pxd Sat Sep 26 21:46:36 2009 +0300
@@ -13,14 +13,30 @@
# XXX: WIN32 !?
ctypedef int evutil_socket_t
+ # utility methods needed for some internal behaviour
+ int evutil_socketpair (int domain, int type, int protocol, evutil_socket_t sv[2])
+ int evutil_make_socket_nonblocking (evutil_socket_t sock)
+
+
ctypedef void (*_ev_callback_t)(int, short, void *)
+cdef extern from "event2/event_struct.h" :
+ # bits of internal event struct
+ struct event :
+ # internal EVLIST_* flags
+ short ev_flags
+
+ # internal ev_flags, we shouldn't really be using these, but...
+ enum :
+ # do not count this event if the event queue is otherwise empty
+ EVLIST_INTERNAL
+
cdef extern from "event2/event.h" :
struct event_base :
pass
- struct event :
- pass
+# struct event :
+# pass
struct event_config :
pass
@@ -70,6 +86,7 @@
EV_SIGNAL
EV_PERSIST
EV_ET
+
# core event assign/new/free
void event_assign (event *, event_base *, evutil_socket_t, short, _ev_callback_t, void *)
@@ -109,3 +126,4 @@
int event_base_priority_set (event_base *, int)
int event_priority_set (event *, int)
+
--- a/qmsk/net/py.pxd Sat Sep 26 16:39:20 2009 +0300
+++ b/qmsk/net/py.pxd Sat Sep 26 21:46:36 2009 +0300
@@ -34,12 +34,16 @@
int _PyString_Resize (PyObject **string, ssize_t newsize) except -1
- ## except setting
+ ## exceptions
PyObject* PyErr_SetFromErrno (PyObject *type)
PyObject *PyExc_OSError
+
+ ## signal handling
+ int PySignal_SetWakeupFd (int fd)
+ int PyErr_CheckSignals () except -1
# raise OSError with errno
-# XXX: doesn't do anything with func
+# XXX: doesn't do anything with func, yet
cdef int raise_errno (char *func) except -1