51
|
1 |
from qmsk.net.lib.event2.event cimport build_timeout, event
|
|
2 |
from qmsk.net.socket.platform cimport AF_UNIX, SOCK_STREAM
|
|
3 |
cimport qmsk.net.py as py
|
39
|
4 |
|
41
|
5 |
class EventError (Exception) :
|
|
6 |
"""
|
|
7 |
Base class of errors raised by lib.event2 code
|
|
8 |
"""
|
|
9 |
|
|
10 |
pass
|
|
11 |
|
|
12 |
class EventCallError (EventError) :
|
|
13 |
"""
|
|
14 |
Some libevent function returned an error code.
|
|
15 |
"""
|
|
16 |
|
|
17 |
def __init__ (self, func, msg=None) :
|
|
18 |
super(EventCallError, self).__init__(func)
|
|
19 |
|
|
20 |
self.func = func
|
|
21 |
self.msg = msg
|
|
22 |
|
51
|
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 |
|
39
|
81 |
cdef class event_base :
|
|
82 |
|
|
83 |
def __init__ (self) :
|
|
84 |
|
|
85 |
# construct
|
|
86 |
self.ev_base = lib.event_base_new()
|
|
87 |
|
|
88 |
if self.ev_base == NULL :
|
41
|
89 |
raise EventCallError("event_base_new")
|
51
|
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 |
|
39
|
99 |
|
|
100 |
def reinit (self) :
|
|
101 |
"""
|
|
102 |
Re-initialize the event_base following a fork(). This is required for some event mechanisms.
|
|
103 |
|
|
104 |
Raises an error if some events could not be re-added.
|
|
105 |
"""
|
|
106 |
|
|
107 |
if lib.event_reinit(self.ev_base) < 0 :
|
41
|
108 |
raise EventCallError("event_reinit", "could not re-add all events")
|
39
|
109 |
|
51
|
110 |
|
39
|
111 |
property method :
|
|
112 |
def __get__ (self) :
|
|
113 |
"""
|
|
114 |
Query the underlying method used by this event_base as a string ("kqueue", "epoll", etc.)
|
|
115 |
"""
|
|
116 |
|
|
117 |
return lib.event_base_get_method(self.ev_base)
|
|
118 |
|
51
|
119 |
|
39
|
120 |
def loop (self, once = False, nonblock = False) :
|
|
121 |
"""
|
|
122 |
Run the event loop.
|
|
123 |
|
|
124 |
once - only run the event loop once, at most
|
|
125 |
nonblock - do not block waiting for events
|
41
|
126 |
|
|
127 |
Returns True if succesfull, False if no events were registered
|
39
|
128 |
"""
|
|
129 |
|
|
130 |
cdef int flags = 0
|
|
131 |
|
|
132 |
# build flags
|
|
133 |
if once :
|
|
134 |
flags |= lib.EVLOOP_ONCE
|
|
135 |
|
|
136 |
if nonblock :
|
|
137 |
flags |= lib.EVLOOP_NONBLOCK
|
51
|
138 |
|
39
|
139 |
|
|
140 |
# event_base_loop()
|
41
|
141 |
ret = lib.event_base_loop(self.ev_base, flags)
|
|
142 |
|
51
|
143 |
|
|
144 |
# check return status
|
41
|
145 |
if ret < 0 :
|
|
146 |
raise EventCallError("event_base_loop")
|
|
147 |
|
|
148 |
elif ret > 0 :
|
|
149 |
return False
|
|
150 |
|
|
151 |
else :
|
|
152 |
return True
|
39
|
153 |
|
51
|
154 |
|
39
|
155 |
def loopexit (self, timeout = None) :
|
|
156 |
"""
|
|
157 |
Exit the event loop normally.
|
|
158 |
|
|
159 |
This will optionally wait until the given timeout expires, and then allow the current event loop iteration
|
|
160 |
to complete normally, before causing .loop() to return.
|
|
161 |
"""
|
|
162 |
|
|
163 |
cdef lib.timeval tv
|
|
164 |
|
|
165 |
if lib.event_base_loopexit(self.ev_base, build_timeout(&tv, timeout)) < 0 :
|
41
|
166 |
raise EventCallError("event_base_loopexit")
|
39
|
167 |
|
51
|
168 |
|
39
|
169 |
def loopbreak (self) :
|
|
170 |
"""
|
|
171 |
Abort the event immediately.
|
|
172 |
|
|
173 |
The current event loop will end and .loop() return as soon as the currently running event has been
|
|
174 |
processed.
|
|
175 |
"""
|
|
176 |
|
|
177 |
if lib.event_base_loopbreak(self.ev_base) < 0 :
|
41
|
178 |
raise EventCallError("event_base_loopbreak")
|
39
|
179 |
|
51
|
180 |
|
39
|
181 |
def __dealloc__ (self) :
|
|
182 |
"""
|
|
183 |
Release the event_base's resources.
|
|
184 |
|
|
185 |
XXX: is this entirely safe re our events?
|
|
186 |
"""
|
51
|
187 |
|
|
188 |
if self.ev_pysignal :
|
|
189 |
# remove the internal signal wakeup event
|
|
190 |
lib.event_free(self.ev_pysignal)
|
39
|
191 |
|
|
192 |
lib.event_base_free(self.ev_base)
|
|
193 |
|