libevent-python/libevent/eventmodule.c

706 lines
23 KiB
C

/*
* eventmodule.c: a wrapper for libevent (http://monkey.org/~provos/libevent/)
* Copyright (c) 2006 Andy Gross <andy@andygross.org>
* Copyright (c) 2006 Nick Mathewson
* See LICENSE.txt for licensing information.
*/
#include <sys/time.h>
#include <sys/types.h>
#include <event.h>
#include <Python.h>
#include <structmember.h>
#define DEFAULT_NUM_PRIORITIES 3
/*
* EventBaseObject wraps a (supposedly) thread-safe libevent dispatch context.
*/
typedef struct EventBaseObject {
PyObject_HEAD
struct event_base *ev_base;
} EventBaseObject;
/* Forward declaration of CPython type object */
static PyTypeObject EventBase_Type;
/*
* EventObject wraps a libevent 'struct event'
*/
typedef struct EventObject {
PyObject_HEAD
struct event ev;
EventBaseObject *eventBase;
PyObject *callback;
} EventObject;
/* Forward declaration of CPython type object */
static PyTypeObject Event_Type;
/* EventObject prototypes */
static PyObject *Event_New(PyTypeObject *, PyObject *, PyObject *);
static int Event_Init(EventObject *, PyObject *, PyObject *);
/* Singleton default event base */
static EventBaseObject *defaultEventBase;
/* Reference to the logging callback */
static PyObject *logCallback;
/* Error Objects */
PyObject *EventErrorObject;
/* Typechecker */
int EventBase_Check(PyObject *o) {
return ((o->ob_type) == &EventBase_Type);
}
/* Construct a new EventBaseObject */
static PyObject *EventBase_New(PyTypeObject *type, PyObject *args,
PyObject *kwds)
{
EventBaseObject *self = NULL;
assert(type != NULL && type->tp_alloc != NULL);
self = (EventBaseObject *)type->tp_alloc(type, 0);
if (self != NULL) {
self->ev_base = event_init();
if (self->ev_base == NULL) {
return NULL;
}
}
return (PyObject *)self;
}
/* EventBaseObject initializer */
static int EventBase_Init(EventBaseObject *self, PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = {"numPriorities", NULL};
int numPriorities = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:event", kwlist,
&numPriorities))
return -1;
if (!numPriorities)
numPriorities = DEFAULT_NUM_PRIORITIES;
if ( (event_base_priority_init(self->ev_base, numPriorities)) < 0) {
return -1;
}
return 0;
}
/* EventBaseObject destructor */
static void EventBase_Dealloc(EventBaseObject *obj) {
obj->ob_type->tp_free((PyObject *)obj);
}
/* EventBaseObject methods */
PyDoc_STRVAR(EventBase_LoopDoc,
"loop(self, [flags=0])\n\
\n\
Perform one iteration of the event loop. Valid flags arg EVLOOP_NONBLOCK \n\
and EVLOOP_ONCE.");
static PyObject *EventBase_Loop(EventBaseObject *self, PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = {"flags", NULL};
int flags = 0;
int rv = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i:loop", kwlist, &flags))
return NULL;
rv = event_base_loop(self->ev_base, flags);
return PyInt_FromLong(rv);
}
PyDoc_STRVAR(EventBase_LoopExitDoc,
"loopExit(self, seconds=0)\n\
\n\
Cause the event loop to exit after <seconds> seconds.");
static PyObject *EventBase_LoopExit(EventBaseObject *self, PyObject *args,
PyObject *kwargs) {
static char * kwlist[] = {"seconds", NULL};
struct timeval tv;
int rv = 0;
double exitAfterSecs = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "d:loopExit",
kwlist, &exitAfterSecs))
return NULL;
tv.tv_sec = (long) exitAfterSecs;
tv.tv_usec = (exitAfterSecs - (long) exitAfterSecs) * 1000000;
rv = event_base_loopexit(self->ev_base, &tv);
return PyInt_FromLong(rv);
}
PyDoc_STRVAR(EventBase_DispatchDoc,
"dispatch(self)\n\
\n\
Run the main dispatch loop associated with this event base. This function\n\
only terminates when no events remain, or the loop is terminated via an \n\
explicit call to EventBase.loopExit() or via a signal.");
static PyObject *EventBase_Dispatch(EventBaseObject *self, PyObject *args,
PyObject *kwargs) {
int rv = event_base_dispatch(self->ev_base);
return PyInt_FromLong(rv);
}
PyDoc_STRVAR(EventBase_CreateEventDoc,
"createEvent(self, fd, events, callback)\n\
\n\
Create a new Event object for the given file descriptor that will call\n\
<callback> with a 3-tuple of (fd, events, eventObject) when the event\n\
fires. The first argument, fd, can be either an integer file descriptor\n\
or a 'file-like' object with a fileno() method.");
static EventObject *EventBase_CreateEvent(EventBaseObject *self,
PyObject *args, PyObject *kwargs)
{
EventObject *newEvent = NULL;
newEvent = (EventObject *)Event_New(&Event_Type,NULL,NULL);
if (Event_Init(newEvent, args, kwargs) < 0)
return NULL;
if (PyObject_CallMethod((PyObject *)newEvent,
"setEventBase", "O", self) == NULL)
return NULL;
return newEvent;
}
PyDoc_STRVAR(EventBase_CreateTimerDoc,
"createTimer(self, callback) -> new timer Event\n\
\n\
Create a new timer object that will call <callback>. The timeout is not\n\
specified here, but rather via the Event.addToLoop([timeout]) method");
static EventObject *EventBase_CreateTimer(EventBaseObject *self,
PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"callback", NULL};
EventObject *newTimer = NULL;
PyObject *callback = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O:createTimer",
kwlist, &callback))
return NULL;
newTimer = (EventObject *)PyObject_CallMethod((PyObject *)self,
"createEvent",
"OiO", Py_None, EV_TIMEOUT,
callback);
return newTimer;
}
PyDoc_STRVAR(EventBase_CreateSignalHandlerDoc,
"createSignalHandler(self, signum, callback) -> new signal handler Event\n\
\n\
Create a new signal handler object that will call <callback> when the signal\n\
is received. Signal handlers are by default persistent - you must manually\n\
remove them with removeFromLoop().");
static EventObject *EventBase_CreateSignalHandler(EventBaseObject *self,
PyObject *args,
PyObject *kwargs) {
static char *kwlist[] = {"signal", "callback", NULL};
EventObject *newSigHandler = NULL;
PyObject *callback = NULL;
int sig = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "iO:createSignalHandler",
kwlist, &sig, &callback))
return NULL;
newSigHandler = (EventObject *)PyObject_CallMethod((PyObject *)self,
"createEvent",
"iiO",
sig,
EV_SIGNAL|EV_PERSIST,
callback);
return newSigHandler;
}
static PyGetSetDef EventBase_Properties[] = {
{NULL},
};
static PyMemberDef EventBase_Members[] = {
{NULL},
};
static PyMethodDef EventBase_Methods[] = {
{"loop", (PyCFunction)EventBase_Loop,
METH_VARARGS|METH_KEYWORDS, EventBase_LoopDoc},
{"loopExit", (PyCFunction)EventBase_LoopExit,
METH_VARARGS|METH_KEYWORDS, EventBase_LoopExitDoc},
{"createEvent", (PyCFunction)EventBase_CreateEvent,
METH_VARARGS|METH_KEYWORDS, EventBase_CreateEventDoc},
{"createSignalHandler", (PyCFunction)EventBase_CreateSignalHandler,
METH_VARARGS|METH_KEYWORDS, EventBase_CreateSignalHandlerDoc},
{"createTimer", (PyCFunction)EventBase_CreateTimer,
METH_VARARGS|METH_KEYWORDS, EventBase_CreateTimerDoc},
{"dispatch", (PyCFunction)EventBase_Dispatch,
METH_NOARGS, EventBase_DispatchDoc},
{NULL},
};
static PyTypeObject EventBase_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"event.EventBase", /*tp_name*/
sizeof(EventBaseObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)EventBase_Dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
PyObject_GenericGetAttr, /*tp_getattro*/
PyObject_GenericSetAttr, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
EventBase_Methods, /*tp_methods*/
EventBase_Members, /*tp_members*/
EventBase_Properties, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)EventBase_Init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
EventBase_New, /*tp_new*/
PyObject_Del, /*tp_free*/
0, /*tp_is_gc*/
};
/* Typechecker */
int Event_Check(PyObject *o) {
return ((o->ob_type) == &Event_Type);
}
/* Construct a new EventObject */
static PyObject *Event_New(PyTypeObject *type, PyObject *args,
PyObject *kwargs)
{
EventObject *self = NULL;
assert(type != NULL && type->tp_alloc != NULL);
self = (EventObject *)type->tp_alloc(type, 0);
self->eventBase = NULL;
return (PyObject *)self;
}
/* Callback thunk. */
static void __libevent_ev_callback(int fd, short events, void *arg) {
EventObject *ev = arg;
PyObject *result;
PyObject *tuple = PyTuple_New(3);
PyTuple_SET_ITEM(tuple, 0, PyInt_FromLong(fd));
PyTuple_SET_ITEM(tuple, 1, PyInt_FromLong(events));
PyTuple_SET_ITEM(tuple, 2, (PyObject *) ev);
Py_INCREF((PyObject *) ev);
result = PyObject_Call(ev->callback, tuple, NULL);
Py_DECREF((PyObject *) ev);
//Py_DECREF(tuple);
if (result) {
Py_DECREF(result);
}
else {
/*
* The callback raised an exception. This usually isnt a problem because
* the callback's caller is in Python-land. Here, we don't have many
* good options. For now, we just print the exception. The commented
* out code below is supposed to asynchronously raise an exception in
* the main thread, but that doesn't work if libevent is blocked on
* an I/O call like select() or kevent(). We could terminate the
* event loop from here, but that seems a little drastic. Somehow,
* we should move the callback invocation to Python. I think.
*/
/*
PyThreadState *ts = PyThreadState_Get();
int r = PyThreadState_SetAsyncExc(ts->thread_id, EventErrorObject);
printf("%d\n", r);
*/
PyErr_WriteUnraisable(ev->callback);
}
}
/* EventObject initializer */
static int Event_Init(EventObject *self, PyObject *args, PyObject *kwargs) {
int fd = -1;
PyObject *fdObj = NULL;
int events = 0;
PyObject *callback = NULL;
static char *kwlist[] = {"fd", "events", "callback", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OiO:event", kwlist,
&fdObj, &events, &callback))
return -1;
if (!PyCallable_Check(callback)) {
PyErr_SetString(EventErrorObject,"callback argument must be callable");
return -1;
}
if (fdObj != Py_None) {
if ( (fd = PyObject_AsFileDescriptor(fdObj)) == -1 ) {
return -1;
}
}
event_set(&self->ev, fd, events, __libevent_ev_callback, self);
if (! event_initialized(&self->ev) )
return -1;
Py_INCREF(callback);
self->callback = callback;
return 0;
}
PyDoc_STRVAR(Event_SetPriorityDoc,
"setPriority(self, priority)\n\
\n\
Set the priority for this event.");
static PyObject *Event_SetPriority(EventObject *self, PyObject *args,
PyObject *kwargs)
{
static char *kwlist[] = {"priority", NULL};
int priority = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs , "|i:setPriority",
kwlist, &priority))
return NULL;
if (event_priority_set(&self->ev, priority) < 0) {
PyErr_SetString(EventErrorObject,
"error setting event priority - event is either already active or priorities are not enabled");
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(Event_AddToLoopDoc,
"addToLoop(self, timeout=-1)\n\
\n\
Add this event to the event loop, with a timeout of <timeout> seconds.\n\
A timeout value of -1 seconds causes the event to remain in the loop \n\
until it fires or is manually removed with removeFromLoop().");
static PyObject *Event_AddToLoop(EventObject *self, PyObject *args,
PyObject *kwargs) {
double timeout = -1.0;
struct timeval tv;
static char *kwlist[] = {"timeout", NULL};
int rv;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|d:addToLoop", kwlist,
&timeout))
return NULL;
if (timeout >= 0.0) {
tv.tv_sec = (long) timeout;
tv.tv_usec = (timeout - (long) timeout) * 1000000;
rv = event_add(&((EventObject *) self)->ev, &tv);
}
else {
rv = event_add(&((EventObject *) self)->ev, NULL);
}
if (rv != 0) {
PyErr_SetFromErrno(EventErrorObject);
return NULL;
}
Py_INCREF(self);
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(Event_RemoveFromLoopDoc,
"removeFromLoop(self)\n\
\n\
Remove the event from the event loop.");
static PyObject *Event_RemoveFromLoop(EventObject *self, PyObject *args,
PyObject *kwargs) {
if (event_del(&self->ev) < 0) {
PyErr_SetFromErrno(EventErrorObject);
return NULL;
}
Py_DECREF(self);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *Event_SetEventBase(EventObject *self, PyObject *args,
PyObject *kwargs) {
static char *kwlist[] = {"eventBase", NULL};
PyObject *eventBase;
int rv = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &eventBase))
return NULL;
if (!EventBase_Check(eventBase)) {
PyErr_SetString(EventErrorObject, "argument is not an EventBase object");
return NULL;
}
rv = event_base_set(((EventBaseObject *)eventBase)->ev_base, &self->ev);
if (rv < 0) {
PyErr_SetString(EventErrorObject, "unable to set event base");
return NULL;
}
if (self->eventBase != NULL) {
Py_XDECREF(self->eventBase);
}
Py_INCREF(eventBase);
self->eventBase = (EventBaseObject *)eventBase;
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(Event_PendingDoc,
"pending(self)\n\
\n\
Returns the event flags set for this event OR'd together.");
static PyObject *Event_Pending(EventObject *self, PyObject *args,
PyObject *kwargs) {
int flags;
flags = event_pending(&((EventObject *) self)->ev,
EV_TIMEOUT | EV_READ | EV_WRITE | EV_SIGNAL, NULL);
return PyInt_FromLong(flags);
}
PyDoc_STRVAR(Event_GetTimeoutDoc,
"getTimeout(self)\n\
\n\
Returns the expiration time of this event.");
static PyObject *Event_GetTimeout(EventObject *self, PyObject *args,
PyObject *kwargs) {
double d;
struct timeval tv;
tv.tv_sec = -1;
event_pending(&((EventObject *) self)->ev, 0, &tv);
if (tv.tv_sec > -1) {
d = tv.tv_sec + (tv.tv_usec / 1000000.0);
return PyFloat_FromDouble(d);
}
Py_INCREF(Py_None);
return Py_None;
}
PyDoc_STRVAR(Event_FilenoDoc,
"fileno(self)\n\
\n\
Return the integer file descriptor number associated with this event.\n\
Not especially meaningful for signal or timer events.");
static PyObject *Event_Fileno(EventObject *self, PyObject *args,
PyObject *kwargs) {
return PyInt_FromLong(self->ev.ev_fd);
}
/* EventObject destructor */
static void Event_Dealloc(EventObject *obj) {
Py_XDECREF(obj->eventBase);
Py_XDECREF(obj->callback);
obj->ob_type->tp_free((PyObject *)obj);
}
static PyObject *Event_Repr(EventObject *self) {
char buf[512];
PyOS_snprintf(buf, sizeof(buf),
"<event object, fd=%ld, events=%d>",
(long) self->ev.ev_fd,
(int) self->ev.ev_events);
return PyString_FromString(buf);
}
#define OFF(x) offsetof(EventObject, x)
static PyMemberDef Event_Members[] = {
{"eventBase", T_OBJECT, OFF(eventBase),
RO, "The EventBase for this event object"},
{"callback", T_OBJECT, OFF(callback),
RO, "The callback for this event object"},
{"events", T_SHORT, OFF(ev.ev_events),
RO, "Events registered for this event object"},
{"numCalls", T_SHORT, OFF(ev.ev_ncalls),
RO, "Number of times this event has been called"},
{"priority", T_INT, OFF(ev.ev_pri),
RO, "Event priority"},
{"flags", T_INT, OFF(ev.ev_flags),
RO, "Event flags (internal)"},
{NULL}
};
#undef OFF
static PyGetSetDef Event_Properties[] = {
{NULL},
};
static PyMethodDef Event_Methods[] = {
{"addToLoop", (PyCFunction)Event_AddToLoop,
METH_VARARGS|METH_KEYWORDS, Event_AddToLoopDoc},
{"removeFromLoop", (PyCFunction)Event_RemoveFromLoop,
METH_NOARGS, Event_RemoveFromLoopDoc},
{"fileno", (PyCFunction)Event_Fileno,
METH_NOARGS, Event_FilenoDoc},
{"setPriority", (PyCFunction)Event_SetPriority,
METH_VARARGS|METH_KEYWORDS, Event_SetPriorityDoc},
{"setEventBase", (PyCFunction)Event_SetEventBase,
METH_VARARGS|METH_KEYWORDS},
{"pending", (PyCFunction)Event_Pending,
METH_NOARGS, Event_PendingDoc},
{"getTimeout", (PyCFunction)Event_GetTimeout,
METH_NOARGS, Event_GetTimeoutDoc},
{NULL},
};
static PyTypeObject Event_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"event.Event", /*tp_name*/
sizeof(EventObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)Event_Dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
(reprfunc)Event_Repr, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash*/
0, /*tp_call*/
0, /*tp_str*/
PyObject_GenericGetAttr, /*tp_getattro*/
PyObject_GenericSetAttr, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
0, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
Event_Methods, /*tp_methods*/
Event_Members, /*tp_members*/
Event_Properties, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)Event_Init, /*tp_init*/
PyType_GenericAlloc, /*tp_alloc*/
Event_New, /*tp_new*/
PyObject_Del, /*tp_free*/
0, /*tp_is_gc*/
};
static PyObject *EventModule_setLogCallback(PyObject *self, PyObject *args,
PyObject *kwargs) {
static char *kwlist[] = {"callback", NULL};
if (!PyArg_ParseTupleAndKeywords(args,kwargs,"O:setLogCallback", kwlist,
&logCallback))
return NULL;
if (!PyCallable_Check(logCallback)) {
PyErr_SetString(EventErrorObject, "log callback is not a callable");
logCallback = NULL;
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef EventModule_Functions[] = {
{"setLogCallback", (PyCFunction)EventModule_setLogCallback,
METH_VARARGS|METH_KEYWORDS},
{NULL},
};
#define ADDCONST(mod, name, const) PyModule_AddIntConstant(mod, name, const)
DL_EXPORT(void) initevent(void)
{
PyObject *m, *d;
m = Py_InitModule("event", EventModule_Functions);
d = PyModule_GetDict(m);
if (EventErrorObject == NULL) {
EventErrorObject = PyErr_NewException("libevent.EventError",
NULL, NULL);
if (EventErrorObject == NULL)
return;
}
Py_INCREF(EventErrorObject);
PyModule_AddObject(m, "EventError", EventErrorObject);
if (PyType_Ready(&EventBase_Type) < 0) {
return;
}
PyModule_AddObject(m, "EventBase", (PyObject *)&EventBase_Type);
if (PyType_Ready(&Event_Type) < 0)
return;
PyModule_AddObject(m, "Event", (PyObject *)&Event_Type);
defaultEventBase = (EventBaseObject *)EventBase_New(&EventBase_Type,
NULL, NULL);
if (defaultEventBase == NULL) {
PyErr_SetString(EventErrorObject,
"error: couldn't create default event base");
return;
}
if (EventBase_Init(defaultEventBase, PyTuple_New(0), NULL) < 0) {
PyErr_SetString(EventErrorObject,
"error: couldn't initialize default event base");
return;
}
PyModule_AddObject(m, "DefaultEventBase", (PyObject *)defaultEventBase);
/* Add constants to the module */
ADDCONST(m, "EV_READ", EV_READ);
ADDCONST(m, "EV_WRITE", EV_WRITE);
ADDCONST(m, "EV_TIMEOUT", EV_TIMEOUT);
ADDCONST(m, "EV_SIGNAL", EV_SIGNAL);
ADDCONST(m, "EV_PERSIST", EV_PERSIST);
ADDCONST(m, "EVLOOP_ONCE", EVLOOP_ONCE);
ADDCONST(m, "EVLOOP_NONBLOCK", EVLOOP_NONBLOCK);
PyModule_AddObject(m, "LIBEVENT_VERSION",
PyString_FromString(event_get_version()));
PyModule_AddObject(m, "LIBEVENT_METHOD",
PyString_FromString(event_get_method()));
}