qmsk/net/lib/event2/event.pyx
changeset 39 075eaafa80a7
child 41 02f7c0539843
child 46 64b4ffb44754
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/net/lib/event2/event.pyx	Fri Aug 28 21:58:47 2009 +0300
@@ -0,0 +1,136 @@
+from qmsk.net.lib.event2.event cimport *
+from qmsk.net.lib.event2.base cimport event_base
+
+cdef lib.timeval* build_timeout (lib.timeval *tv, object timeout = None) except? <lib.timeval *>-1 :
+    """
+        If a timeout is given, it is treated as a float and stored in *tv, which is returned. Otherwise, simply returns
+        NULL.
+    """
+
+    cdef double t
+
+    if timeout :
+        t = timeout
+
+        tv.tv_sec = <int>(t)
+        tv.tv_usec = <int>((t - <int> t) * 100000)
+        
+        return tv
+
+    else :
+        return NULL
+
+
+# our shared callback handler
+# this must aquire the GIL before invoking the python object
+# this means that the libevent loop *must* be run with the GIL released!
+cdef void ev_callback (lib.evutil_socket_t fd, short mask, void *arg) with gil :
+    """
+        Proxy event back to event object passed as arg
+    """
+    
+    # unpack opaque arg
+    cdef event ev = <event>arg
+
+    # invoke
+    # XXX: logging?
+    ev(fd, mask)
+
+
+cdef class event :
+
+    def __init__ (self, event_base base, lib.evutil_socket_t fd, short events) :
+
+        # construct event using our internal callback, with self as the arg
+        self.ev = lib.event_new(base.ev_base, fd, events, ev_callback, <void *>self)
+
+        if self.ev == NULL :
+            raise Exception("event_new")
+    
+
+    def assign (self, event_base base, lib.evutil_socket_t fd, short events) :
+        """
+            Re-assign our event parameters to the given ones.
+
+            It is illegal to call this method after calling .add() but before del()/callback!
+        """
+        
+        # interesting... no error return code
+        lib.event_assign(self.ev, base.ev_base, fd, events, ev_callback, <void *>self)
+
+
+    def add (self, timeout = None) :
+        """
+            Add this event to our event_base's set of monitored events.
+
+            This event will be executed if any of the events specified in 'events' occurs, or the given timeout
+            expires. If no timeout is given, no timeout will occur.
+
+            If this event already has a shceduled timeout, the old timeout will be replaced by the new one.
+
+                timeout     - a float representing the timeout length
+
+            XXX: "The event in the ev argument must be already initialized by event_set() and may not be used in calls to event_set() until it has timed out or been removed with event_del()" -- enforce
+        """
+
+        cdef lib.timeval tv
+
+        if lib.event_add(self.ev, build_timeout(&tv, timeout)) < 0 :
+            raise Exception("event_new")
+
+
+    def delete (self) :
+        """
+            Remove this event from our event_base's set of monitored events.
+
+            If this event has already executed or has never been added this call with have no effect.
+        """
+
+        if lib.event_del(self.ev) < 0 :
+            raise Exception("event_del")
+
+
+    def activate (self, int fd, short mask) :
+        """
+            'Fake' this event as active, triggering the callback.
+        """
+
+        lib.event_active(self.ev, fd, mask)
+
+
+    def pending (self, short mask) :
+        """
+            Returns a bool indicating if this event is pending (that is, has been .add()'d).
+
+            XXX: support returning timeout value?
+        """
+
+        return bool(lib.event_pending(self.ev, mask, NULL))
+
+
+    property fd :
+        def __get__ (self) :
+            """
+                Get the OS file descriptor associated with this event.
+            """
+
+            return lib.event_get_fd(self.ev)
+
+
+    def __call__ (self, lib.evutil_socket_t fd, short mask) :
+        """
+            The method invoked by the internal libevent callback.
+        """
+
+        pass
+
+
+    def __dealloc__ (self) :
+        """
+            Release the event object usign event_free. This should be completely safe as regards out event_base.
+
+            XXX: what happens if event_base's __dealloc__ is triggered, but there are still event objects alive?
+        """
+
+        lib.event_free(self.ev)
+