terom@39: from qmsk.net.lib.event2.event cimport * terom@39: from qmsk.net.lib.event2.base cimport event_base terom@39: terom@39: cdef lib.timeval* build_timeout (lib.timeval *tv, object timeout = None) except? -1 : terom@39: """ terom@39: If a timeout is given, it is treated as a float and stored in *tv, which is returned. Otherwise, simply returns terom@39: NULL. terom@39: """ terom@39: terom@39: cdef double t terom@39: terom@39: if timeout : terom@39: t = timeout terom@39: terom@39: tv.tv_sec = (t) terom@39: tv.tv_usec = ((t - t) * 100000) terom@39: terom@39: return tv terom@39: terom@39: else : terom@39: return NULL terom@39: terom@39: terom@39: # our shared callback handler terom@39: # this must aquire the GIL before invoking the python object terom@39: # this means that the libevent loop *must* be run with the GIL released! terom@39: cdef void ev_callback (lib.evutil_socket_t fd, short mask, void *arg) with gil : terom@39: """ terom@39: Proxy event back to event object passed as arg terom@39: """ terom@39: terom@39: # unpack opaque arg terom@39: cdef event ev = arg terom@39: terom@39: # invoke terom@39: # XXX: logging? terom@39: ev(fd, mask) terom@39: terom@39: terom@39: cdef class event : terom@39: terom@39: def __init__ (self, event_base base, lib.evutil_socket_t fd, short events) : terom@39: terom@39: # construct event using our internal callback, with self as the arg terom@39: self.ev = lib.event_new(base.ev_base, fd, events, ev_callback, self) terom@39: terom@39: if self.ev == NULL : terom@39: raise Exception("event_new") terom@39: terom@39: terom@39: def assign (self, event_base base, lib.evutil_socket_t fd, short events) : terom@39: """ terom@39: Re-assign our event parameters to the given ones. terom@39: terom@39: It is illegal to call this method after calling .add() but before del()/callback! terom@39: """ terom@39: terom@39: # interesting... no error return code terom@39: lib.event_assign(self.ev, base.ev_base, fd, events, ev_callback, self) terom@39: terom@39: terom@39: def add (self, timeout = None) : terom@39: """ terom@39: Add this event to our event_base's set of monitored events. terom@39: terom@39: This event will be executed if any of the events specified in 'events' occurs, or the given timeout terom@39: expires. If no timeout is given, no timeout will occur. terom@39: terom@39: If this event already has a shceduled timeout, the old timeout will be replaced by the new one. terom@39: terom@39: timeout - a float representing the timeout length terom@39: terom@39: 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 terom@39: """ terom@39: terom@39: cdef lib.timeval tv terom@39: terom@39: if lib.event_add(self.ev, build_timeout(&tv, timeout)) < 0 : terom@39: raise Exception("event_new") terom@39: terom@39: terom@39: def delete (self) : terom@39: """ terom@39: Remove this event from our event_base's set of monitored events. terom@39: terom@39: If this event has already executed or has never been added this call with have no effect. terom@39: """ terom@39: terom@39: if lib.event_del(self.ev) < 0 : terom@39: raise Exception("event_del") terom@39: terom@39: terom@39: def activate (self, int fd, short mask) : terom@39: """ terom@39: 'Fake' this event as active, triggering the callback. terom@39: """ terom@39: terom@39: lib.event_active(self.ev, fd, mask) terom@39: terom@39: terom@39: def pending (self, short mask) : terom@39: """ terom@39: Returns a bool indicating if this event is pending (that is, has been .add()'d). terom@39: terom@39: XXX: support returning timeout value? terom@39: """ terom@39: terom@39: return bool(lib.event_pending(self.ev, mask, NULL)) terom@39: terom@39: terom@39: property fd : terom@39: def __get__ (self) : terom@39: """ terom@39: Get the OS file descriptor associated with this event. terom@39: """ terom@39: terom@39: return lib.event_get_fd(self.ev) terom@39: terom@39: terom@39: def __call__ (self, lib.evutil_socket_t fd, short mask) : terom@39: """ terom@39: The method invoked by the internal libevent callback. terom@39: """ terom@39: terom@39: pass terom@39: terom@39: terom@39: def __dealloc__ (self) : terom@39: """ terom@39: Release the event object usign event_free. This should be completely safe as regards out event_base. terom@39: terom@39: XXX: what happens if event_base's __dealloc__ is triggered, but there are still event objects alive? terom@39: """ terom@39: terom@39: lib.event_free(self.ev) terom@39: terom@46: class CallbackEvent (event) : terom@46: """ terom@46: Extends the event type to take a callback and additional arguments to invoke from the __call__ method. terom@46: """ terom@46: terom@46: def __init__ (self, event_base base, lib.evutil_socket_t fd, short events, object callback, *args, **kwargs) : terom@46: # parent terom@46: super(CallbackEvent, self).__init__(base, fd, events) terom@46: terom@46: # store terom@46: self.callback = callback terom@46: self.args = args terom@46: self.kwargs = kwargs terom@46: terom@46: def __call__ (self, lib.evutil_socket_t fd, short mask) : terom@46: self.callback(fd, mask, *self.args, **self.kwargs) terom@46: