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
|