qmsk/net/lib/event2/base.pyx
changeset 51 c6b4abfc21da
parent 41 02f7c0539843
child 56 07ed878c847b
equal deleted inserted replaced
50:da394bb715af 51:c6b4abfc21da
     1 from qmsk.net.lib.event2.base cimport *
     1 from qmsk.net.lib.event2.event cimport build_timeout, event
     2 from qmsk.net.lib.event2.event cimport build_timeout
     2 from qmsk.net.socket.platform cimport AF_UNIX, SOCK_STREAM
       
     3 cimport qmsk.net.py as py
     3 
     4 
     4 class EventError (Exception) :
     5 class EventError (Exception) :
     5     """
     6     """
     6         Base class of errors raised by lib.event2 code
     7         Base class of errors raised by lib.event2 code
     7     """
     8     """
    17         super(EventCallError, self).__init__(func)
    18         super(EventCallError, self).__init__(func)
    18 
    19 
    19         self.func = func
    20         self.func = func
    20         self.msg = msg
    21         self.msg = msg
    21 
    22 
       
    23 cdef lib.event* build_pysignal_ev (lib.event_base *ev_base, lib._ev_callback_t cb_func, void *cb_arg) except NULL :
       
    24     """
       
    25         This constructs an returns an event* which will activate the given callback when a Python signal handler runs.
       
    26 
       
    27         The event is constructed as a persistent internal event, which means that it *must* be removed before trying to
       
    28         release the event_base.
       
    29     """
       
    30 
       
    31     cdef lib.evutil_socket_t fds[2]
       
    32 
       
    33     # construct the pipe/sockpair
       
    34     if lib.evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0 :
       
    35         raise EventCallError("evutil_socketpair(signals)")
       
    36 
       
    37     # prep: nonblocking
       
    38     # XXX: close-on-exec?
       
    39     lib.evutil_make_socket_nonblocking(fds[0])
       
    40     lib.evutil_make_socket_nonblocking(fds[1])
       
    41 
       
    42     # build event for one end
       
    43     cdef lib.event *ev = lib.event_new(ev_base, fds[1], lib.EV_READ | lib.EV_PERSIST, cb_func, cb_arg)
       
    44     
       
    45     # set magic flag to not count as active
       
    46     ev.ev_flags |= lib.EVLIST_INTERNAL
       
    47 
       
    48     if ev == NULL :
       
    49         raise EventCallError("event_new(signals)")
       
    50 
       
    51     # register wakeup fd
       
    52     py.PySignal_SetWakeupFd(fds[0])
       
    53 
       
    54     return ev
       
    55 
       
    56 
       
    57 cdef void on_pysignal_wakeup (int sig, short what, void *arg) :
       
    58     """
       
    59         Python's signal handler trapped a signal, we need to run the signal handlers.
       
    60 
       
    61         This is executed by libevent's event_base_loop as a normal event (using the wakeup fd built using
       
    62         build_pysignal_ev), and hence the signal handlers will be run from "inside" of event_base.loop(). Exceptions
       
    63         raised by the signal handlers should be propagated "out" of event_base.loop().
       
    64     """
       
    65 
       
    66     cdef event_base ev_base = <event_base> arg
       
    67 
       
    68     try :
       
    69         # run the real signal handlers for any pending signals
       
    70         py.PyErr_CheckSignals()
       
    71 
       
    72     except :
       
    73         # TODO: implement propagate-to-event_base.loop()
       
    74         # drop out of the event loop
       
    75         ev_base.loopbreak()
       
    76 
       
    77         # Cython will log it
       
    78         raise
       
    79         
       
    80 
    22 cdef class event_base :
    81 cdef class event_base :
    23 
    82 
    24     def __init__ (self) :
    83     def __init__ (self) :
    25 
    84 
    26         # construct
    85         # construct
    27         self.ev_base = lib.event_base_new()
    86         self.ev_base = lib.event_base_new()
    28 
    87 
    29         if self.ev_base == NULL :
    88         if self.ev_base == NULL :
    30             raise EventCallError("event_base_new")
    89             raise EventCallError("event_base_new")
       
    90         
       
    91         # internal signal wakeup handler
       
    92         # XXX: only the main event_base really needs one...
       
    93         self.ev_pysignal = build_pysignal_ev(self.ev_base, on_pysignal_wakeup, <void *> self)
       
    94             
       
    95         # XXX: this will keep the loop active?
       
    96         if lib.event_add(self.ev_pysignal, NULL) < 0 :
       
    97             raise EventCallError("event_add(signals)")
       
    98 
    31 
    99 
    32     def reinit (self) :
   100     def reinit (self) :
    33         """
   101         """
    34             Re-initialize the event_base following a fork(). This is required for some event mechanisms.
   102             Re-initialize the event_base following a fork(). This is required for some event mechanisms.
    35 
   103 
    37         """
   105         """
    38 
   106 
    39         if lib.event_reinit(self.ev_base) < 0 :
   107         if lib.event_reinit(self.ev_base) < 0 :
    40             raise EventCallError("event_reinit", "could not re-add all events")
   108             raise EventCallError("event_reinit", "could not re-add all events")
    41 
   109 
       
   110 
    42     property method :
   111     property method :
    43         def __get__ (self) :
   112         def __get__ (self) :
    44             """
   113             """
    45                 Query the underlying method used by this event_base as a string ("kqueue", "epoll", etc.)
   114                 Query the underlying method used by this event_base as a string ("kqueue", "epoll", etc.)
    46             """
   115             """
    47 
   116 
    48             return lib.event_base_get_method(self.ev_base)
   117             return lib.event_base_get_method(self.ev_base)
       
   118 
    49 
   119 
    50     def loop (self, once = False, nonblock = False) :
   120     def loop (self, once = False, nonblock = False) :
    51         """
   121         """
    52             Run the event loop.
   122             Run the event loop.
    53 
   123 
    63         if once :
   133         if once :
    64             flags |= lib.EVLOOP_ONCE
   134             flags |= lib.EVLOOP_ONCE
    65 
   135 
    66         if nonblock :
   136         if nonblock :
    67             flags |= lib.EVLOOP_NONBLOCK
   137             flags |= lib.EVLOOP_NONBLOCK
       
   138         
    68 
   139 
    69         # event_base_loop()
   140         # event_base_loop()
    70         ret = lib.event_base_loop(self.ev_base, flags)
   141         ret = lib.event_base_loop(self.ev_base, flags)
    71 
   142 
       
   143         
       
   144         # check return status
    72         if ret < 0 :
   145         if ret < 0 :
    73             raise EventCallError("event_base_loop")
   146             raise EventCallError("event_base_loop")
    74 
   147 
    75         elif ret > 0 :
   148         elif ret > 0 :
    76             return False
   149             return False
    77 
   150 
    78         else :
   151         else :
    79             return True
   152             return True
       
   153 
    80 
   154 
    81     def loopexit (self, timeout = None) :
   155     def loopexit (self, timeout = None) :
    82         """
   156         """
    83             Exit the event loop normally.
   157             Exit the event loop normally.
    84 
   158 
    89         cdef lib.timeval tv
   163         cdef lib.timeval tv
    90 
   164 
    91         if lib.event_base_loopexit(self.ev_base, build_timeout(&tv, timeout)) < 0 :
   165         if lib.event_base_loopexit(self.ev_base, build_timeout(&tv, timeout)) < 0 :
    92             raise EventCallError("event_base_loopexit")
   166             raise EventCallError("event_base_loopexit")
    93 
   167 
       
   168 
    94     def loopbreak (self) :
   169     def loopbreak (self) :
    95         """
   170         """
    96             Abort the event immediately.
   171             Abort the event immediately.
    97 
   172 
    98             The current event loop will end and .loop() return as soon as the currently running event has been
   173             The current event loop will end and .loop() return as soon as the currently running event has been
   100         """
   175         """
   101 
   176 
   102         if lib.event_base_loopbreak(self.ev_base) < 0 :
   177         if lib.event_base_loopbreak(self.ev_base) < 0 :
   103             raise EventCallError("event_base_loopbreak")
   178             raise EventCallError("event_base_loopbreak")
   104 
   179 
       
   180 
   105     def __dealloc__ (self) :
   181     def __dealloc__ (self) :
   106         """
   182         """
   107             Release the event_base's resources.
   183             Release the event_base's resources.
   108 
   184 
   109             XXX: is this entirely safe re our events?
   185             XXX: is this entirely safe re our events?
   110         """
   186         """
       
   187         
       
   188         if self.ev_pysignal :
       
   189             # remove the internal signal wakeup event
       
   190             lib.event_free(self.ev_pysignal)
   111 
   191 
   112         lib.event_base_free(self.ev_base)
   192         lib.event_base_free(self.ev_base)
   113 
   193