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 |