qmsk/net/lib/event2/base.pxd
authorTero Marttila <terom@fixme.fi>
Sat, 26 Sep 2009 21:46:36 +0300
changeset 51 c6b4abfc21da
parent 50 da394bb715af
child 52 722fc70a197a
qmsk/net/lib/event2/base.pxd
qmsk/net/lib/event2/base.pyx
qmsk/net/lib/event2/lib.pxd
qmsk/net/py.pxd
--- 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