blob: 3a3b1cecac5d519ebdcd1c9d5b609538fa268f33 [file] [log] [blame]
# Copyright (c) 2001-2006 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:
EPOLLIN = 0x001
EPOLLPRI = 0x002
EPOLLOUT = 0x004
EPOLLRDNORM = 0x040
EPOLLRDBAND = 0x080
EPOLLWRNORM = 0x100
EPOLLWRBAND = 0x200
EPOLLMSG = 0x400
EPOLLERR = 0x008
EPOLLHUP = 0x010
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 class epoll:
"""
Represent a set of file descriptors being monitored for events.
"""
cdef int fd
cdef int initialized
def __init__(self, int size):
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 _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 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 int fd
cdef PyThreadState *_save
nbytes = sizeof(epoll_event) * maxevents
events = <epoll_event*>malloc(nbytes)
memset(events, 0, nbytes)
try:
fd = self.fd
_save = PyEval_SaveThread()
result = epoll_wait(fd, events, maxevents, timeout)
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)
CTL_ADD = EPOLL_CTL_ADD
CTL_DEL = EPOLL_CTL_DEL
CTL_MOD = EPOLL_CTL_MOD
IN = EPOLLIN
OUT = EPOLLOUT
PRI = EPOLLPRI
ERR = EPOLLERR
HUP = EPOLLHUP
ET = EPOLLET
RDNORM = EPOLLRDNORM
RDBAND = EPOLLRDBAND
WRNORM = EPOLLWRNORM
WRBAND = EPOLLWRBAND
MSG = EPOLLMSG