1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
|
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Interface to epoll I/O event notification facility.
"""
# NOTE: The version of Pyrex you are using probably _does not work_ with
# Python 2.5. If you need to recompile this file, _make sure you are using
# a version of Pyrex which works with Python 2.5_. I am using 0.9.4.1 from
# <http://codespeak.net/svn/lxml/pyrex/>. -exarkun
cdef extern from "stdio.h":
cdef extern void *malloc(int)
cdef extern void free(void *)
cdef extern int close(int)
cdef extern from "errno.h":
cdef extern int errno
cdef extern char *strerror(int)
cdef extern from "string.h":
cdef extern void *memset(void* s, int c, int n)
cdef extern from "stdint.h":
ctypedef unsigned long uint32_t
ctypedef unsigned long long uint64_t
cdef extern from "sys/epoll.h":
cdef enum:
EPOLL_CTL_ADD = 1
EPOLL_CTL_DEL = 2
EPOLL_CTL_MOD = 3
cdef enum EPOLL_EVENTS:
c_EPOLLIN "EPOLLIN" = 0x001
c_EPOLLPRI "EPOLLPRI" = 0x002
c_EPOLLOUT "EPOLLOUT" = 0x004
c_EPOLLRDNORM "EPOLLRDNORM" = 0x040
c_EPOLLRDBAND "EPOLLRDBAND" = 0x080
c_EPOLLWRNORM "EPOLLWRNORM" = 0x100
c_EPOLLWRBAND "EPOLLWRBAND" = 0x200
c_EPOLLMSG "EPOLLMSG" = 0x400
c_EPOLLERR "EPOLLERR" = 0x008
c_EPOLLHUP "EPOLLHUP" = 0x010
c_EPOLLET "EPOLLET" = (1 << 31)
ctypedef union epoll_data_t:
void *ptr
int fd
uint32_t u32
uint64_t u64
cdef struct epoll_event:
uint32_t events
epoll_data_t data
int epoll_create(int size)
int epoll_ctl(int epfd, int op, int fd, epoll_event *event)
int epoll_wait(int epfd, epoll_event *events, int maxevents, int timeout)
cdef extern from "Python.h":
ctypedef struct PyThreadState
cdef extern PyThreadState *PyEval_SaveThread()
cdef extern void PyEval_RestoreThread(PyThreadState*)
cdef call_epoll_wait(int fd, unsigned int maxevents, int timeout_msec):
"""
Wait for an I/O event, wrap epoll_wait(2).
@type fd: C{int}
@param fd: The epoll file descriptor number.
@type maxevents: C{int}
@param maxevents: Maximum number of events returned.
@type timeout_msec: C{int}
@param timeout_msec: Maximum time in milliseconds waiting for events. 0
makes it return immediately whereas -1 makes it wait indefinitely.
@raise IOError: Raised if the underlying epoll_wait() call fails.
"""
cdef epoll_event *events
cdef int result
cdef int nbytes
cdef PyThreadState *_save
nbytes = sizeof(epoll_event) * maxevents
events = <epoll_event*>malloc(nbytes)
memset(events, 0, nbytes)
try:
_save = PyEval_SaveThread()
result = epoll_wait(fd, events, maxevents, timeout_msec)
PyEval_RestoreThread(_save)
if result == -1:
raise IOError(errno, strerror(errno))
results = []
for i from 0 <= i < result:
results.append((events[i].data.fd, <int>events[i].events))
return results
finally:
free(events)
cdef class epoll:
"""
Represent a set of file descriptors being monitored for events.
"""
cdef int fd
cdef int initialized
def __init__(self, int size=1023):
"""
The constructor arguments are compatible with select.poll.__init__.
"""
self.fd = epoll_create(size)
if self.fd == -1:
raise IOError(errno, strerror(errno))
self.initialized = 1
def __dealloc__(self):
if self.initialized:
close(self.fd)
self.initialized = 0
def close(self):
"""
Close the epoll file descriptor.
"""
if self.initialized:
if close(self.fd) == -1:
raise IOError(errno, strerror(errno))
self.initialized = 0
def fileno(self):
"""
Return the epoll file descriptor number.
"""
return self.fd
def register(self, int fd, int events):
"""
Add (register) a file descriptor to be monitored by self.
This method is compatible with select.epoll.register in Python 2.6.
Wrap epoll_ctl(2).
@type fd: C{int}
@param fd: File descriptor to modify
@type events: C{int}
@param events: A bit set of IN, OUT, PRI, ERR, HUP, and ET.
@raise IOError: Raised if the underlying epoll_ctl() call fails.
"""
cdef int result
cdef epoll_event evt
evt.events = events
evt.data.fd = fd
result = epoll_ctl(self.fd, CTL_ADD, fd, &evt)
if result == -1:
raise IOError(errno, strerror(errno))
def unregister(self, int fd):
"""
Remove (unregister) a file descriptor monitored by self.
This method is compatible with select.epoll.unregister in Python 2.6.
Wrap epoll_ctl(2).
@type fd: C{int}
@param fd: File descriptor to modify
@raise IOError: Raised if the underlying epoll_ctl() call fails.
"""
cdef int result
cdef epoll_event evt
# We don't have to fill evt.events for CTL_DEL.
evt.data.fd = fd
result = epoll_ctl(self.fd, CTL_DEL, fd, &evt)
if result == -1:
raise IOError(errno, strerror(errno))
def modify(self, int fd, int events):
"""
Modify the modified state of a file descriptor monitored by self.
This method is compatible with select.epoll.modify in Python 2.6.
Wrap epoll_ctl(2).
@type fd: C{int}
@param fd: File descriptor to modify
@type events: C{int}
@param events: A bit set of IN, OUT, PRI, ERR, HUP, and ET.
@raise IOError: Raised if the underlying epoll_ctl() call fails.
"""
cdef int result
cdef epoll_event evt
evt.events = events
evt.data.fd = fd
result = epoll_ctl(self.fd, CTL_MOD, fd, &evt)
if result == -1:
raise IOError(errno, strerror(errno))
def _control(self, int op, int fd, int events):
"""
Modify the monitored state of a particular file descriptor.
Wrap epoll_ctl(2).
@type op: C{int}
@param op: One of CTL_ADD, CTL_DEL, or CTL_MOD
@type fd: C{int}
@param fd: File descriptor to modify
@type events: C{int}
@param events: A bit set of IN, OUT, PRI, ERR, HUP, and ET.
@raise IOError: Raised if the underlying epoll_ctl() call fails.
"""
cdef int result
cdef epoll_event evt
evt.events = events
evt.data.fd = fd
result = epoll_ctl(self.fd, op, fd, &evt)
if result == -1:
raise IOError(errno, strerror(errno))
def wait(self, unsigned int maxevents, int timeout):
"""
Wait for an I/O event, wrap epoll_wait(2).
@type maxevents: C{int}
@param maxevents: Maximum number of events returned.
@type timeout: C{int}
@param timeout: Maximum time in milliseconds waiting for events. 0
makes it return immediately whereas -1 makes it wait indefinitely.
@raise IOError: Raised if the underlying epoll_wait() call fails.
"""
return call_epoll_wait(self.fd, maxevents, timeout)
def poll(self, float timeout=-1, unsigned int maxevents=1024):
"""
Wait for an I/O event, wrap epoll_wait(2).
This method is compatible with select.epoll.poll in Python 2.6.
@type maxevents: C{int}
@param maxevents: Maximum number of events returned.
@type timeout: C{int}
@param timeout: Maximum time waiting for events. 0 makes it return
immediately whereas -1 makes it wait indefinitely.
@raise IOError: Raised if the underlying epoll_wait() call fails.
"""
return call_epoll_wait(self.fd, maxevents, <int>(timeout * 1000.0))
CTL_ADD = EPOLL_CTL_ADD
CTL_DEL = EPOLL_CTL_DEL
CTL_MOD = EPOLL_CTL_MOD
IN = EPOLLIN = c_EPOLLIN
OUT = EPOLLOUT = c_EPOLLOUT
PRI = EPOLLPRI = c_EPOLLPRI
ERR = EPOLLERR = c_EPOLLERR
HUP = EPOLLHUP = c_EPOLLHUP
ET = EPOLLET = c_EPOLLET
RDNORM = EPOLLRDNORM = c_EPOLLRDNORM
RDBAND = EPOLLRDBAND = c_EPOLLRDBAND
WRNORM = EPOLLWRNORM = c_EPOLLWRNORM
WRBAND = EPOLLWRBAND = c_EPOLLWRBAND
MSG = EPOLLMSG = c_EPOLLMSG
|