509 lines
17 KiB
C
509 lines
17 KiB
C
#include <Python.h>
|
|
#include <ev.h>
|
|
|
|
typedef struct libevwrapper_Loop {
|
|
PyObject_HEAD
|
|
struct ev_loop *loop;
|
|
} libevwrapper_Loop;
|
|
|
|
static void
|
|
Loop_dealloc(libevwrapper_Loop *self) {
|
|
self->ob_type->tp_free((PyObject *)self);
|
|
};
|
|
|
|
static PyObject*
|
|
Loop_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
|
|
libevwrapper_Loop *self;
|
|
|
|
self = (libevwrapper_Loop *)type->tp_alloc(type, 0);
|
|
if (self != NULL) {
|
|
// select is the most portable backend, it is generally the most
|
|
// efficient for the number of file descriptors we'll be working with,
|
|
// and the epoll backend seems to occasionally leave write requests
|
|
// hanging (although I'm not sure if that's due to a misuse of libev
|
|
// or not)
|
|
self->loop = ev_default_loop(EVBACKEND_SELECT);
|
|
if (!self->loop) {
|
|
PyErr_SetString(PyExc_Exception, "Error getting default ev loop");
|
|
Py_DECREF(self);
|
|
return NULL;
|
|
}
|
|
}
|
|
return (PyObject *)self;
|
|
};
|
|
|
|
static int
|
|
Loop_init(libevwrapper_Loop *self, PyObject *args, PyObject *kwds) {
|
|
if (!PyArg_ParseTuple(args, "")) {
|
|
PyErr_SetString(PyExc_TypeError, "Loop.__init__() takes no arguments");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
static PyObject *
|
|
Loop_start(libevwrapper_Loop *self, PyObject *args) {
|
|
Py_BEGIN_ALLOW_THREADS
|
|
ev_run(self->loop, 0);
|
|
Py_END_ALLOW_THREADS
|
|
Py_RETURN_NONE;
|
|
};
|
|
|
|
static PyObject *
|
|
Loop_unref(libevwrapper_Loop *self, PyObject *args) {
|
|
ev_unref(self->loop);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyMethodDef Loop_methods[] = {
|
|
{"start", (PyCFunction)Loop_start, METH_NOARGS, "Start the event loop"},
|
|
{"unref", (PyCFunction)Loop_unref, METH_NOARGS, "Unrefrence the event loop"},
|
|
{NULL} /* Sentinel */
|
|
};
|
|
|
|
static PyTypeObject libevwrapper_LoopType = {
|
|
PyObject_HEAD_INIT(NULL)
|
|
0, /*ob_size*/
|
|
"cassandra.io.libevwrapper.Loop",/*tp_name*/
|
|
sizeof(libevwrapper_Loop), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
(destructor)Loop_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*/
|
|
0, /*tp_getattro*/
|
|
0, /*tp_setattro*/
|
|
0, /*tp_as_buffer*/
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
|
"Loop objects", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
Loop_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
(initproc)Loop_init, /* tp_init */
|
|
0, /* tp_alloc */
|
|
Loop_new, /* tp_new */
|
|
};
|
|
|
|
typedef struct libevwrapper_IO {
|
|
PyObject_HEAD
|
|
struct ev_io io;
|
|
struct libevwrapper_Loop *loop;
|
|
PyObject *callback;
|
|
} libevwrapper_IO;
|
|
|
|
static void
|
|
IO_dealloc(libevwrapper_IO *self) {
|
|
Py_XDECREF(self->loop);
|
|
Py_XDECREF(self->callback);
|
|
self->ob_type->tp_free((PyObject *)self);
|
|
};
|
|
|
|
static void io_callback(struct ev_loop *loop, ev_io *watcher, int revents) {
|
|
libevwrapper_IO *self = watcher->data;
|
|
PyObject *result;
|
|
PyGILState_STATE gstate = PyGILState_Ensure();
|
|
if (revents & EV_ERROR && errno) {
|
|
result = PyObject_CallFunction(self->callback, "Obi", self, revents, errno);
|
|
} else {
|
|
result = PyObject_CallFunction(self->callback, "Ob", self, revents);
|
|
}
|
|
if (!result) {
|
|
PyErr_WriteUnraisable(self->callback);
|
|
}
|
|
Py_XDECREF(result);
|
|
PyGILState_Release(gstate);
|
|
};
|
|
|
|
static int
|
|
IO_init(libevwrapper_IO *self, PyObject *args, PyObject *kwds) {
|
|
PyObject *socket;
|
|
PyObject *callback;
|
|
PyObject *loop;
|
|
int io_flags = 0;
|
|
|
|
if (!PyArg_ParseTuple(args, "OiOO", &socket, &io_flags, &loop, &callback)) {
|
|
return -1;
|
|
}
|
|
|
|
if (loop) {
|
|
Py_INCREF(loop);
|
|
self->loop = (libevwrapper_Loop *)loop;
|
|
}
|
|
|
|
if (callback) {
|
|
if (!PyCallable_Check(callback)) {
|
|
PyErr_SetString(PyExc_TypeError, "callback parameter must be callable");
|
|
Py_XDECREF(loop);
|
|
return -1;
|
|
}
|
|
Py_INCREF(callback);
|
|
self->callback = callback;
|
|
}
|
|
|
|
int fd = PyObject_AsFileDescriptor(socket);
|
|
if (fd == -1) {
|
|
PyErr_SetString(PyExc_TypeError, "unable to get file descriptor from socket");
|
|
Py_XDECREF(callback);
|
|
Py_XDECREF(loop);
|
|
return -1;
|
|
}
|
|
ev_io_init(&self->io, io_callback, fd, io_flags);
|
|
self->io.data = self;
|
|
return 0;
|
|
}
|
|
|
|
static PyObject*
|
|
IO_start(libevwrapper_IO *self, PyObject *args) {
|
|
ev_io_start(self->loop->loop, &self->io);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject*
|
|
IO_stop(libevwrapper_IO *self, PyObject *args) {
|
|
ev_io_stop(self->loop->loop, &self->io);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject*
|
|
IO_is_active(libevwrapper_IO *self, PyObject *args) {
|
|
return PyBool_FromLong(ev_is_active(&self->io));
|
|
}
|
|
|
|
static PyObject*
|
|
IO_is_pending(libevwrapper_IO *self, PyObject *args) {
|
|
return PyBool_FromLong(ev_is_pending(&self->io));
|
|
}
|
|
|
|
static PyMethodDef IO_methods[] = {
|
|
{"start", (PyCFunction)IO_start, METH_NOARGS, "Start the watcher"},
|
|
{"stop", (PyCFunction)IO_stop, METH_NOARGS, "Stop the watcher"},
|
|
{"is_active", (PyCFunction)IO_is_active, METH_NOARGS, "Is the watcher active?"},
|
|
{"is_pending", (PyCFunction)IO_is_pending, METH_NOARGS, "Is the watcher pending?"},
|
|
{NULL} /* Sentinal */
|
|
};
|
|
|
|
static PyTypeObject libevwrapper_IOType = {
|
|
PyObject_HEAD_INIT(NULL)
|
|
0, /*ob_size*/
|
|
"cassandra.io.libevwrapper.IO", /*tp_name*/
|
|
sizeof(libevwrapper_IO), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
(destructor)IO_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*/
|
|
0, /*tp_getattro*/
|
|
0, /*tp_setattro*/
|
|
0, /*tp_as_buffer*/
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
|
"IO objects", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
IO_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
(initproc)IO_init, /* tp_init */
|
|
};
|
|
|
|
typedef struct libevwrapper_Async {
|
|
PyObject_HEAD
|
|
struct ev_async async;
|
|
struct libevwrapper_Loop *loop;
|
|
} libevwrapper_Async;
|
|
|
|
static void
|
|
Async_dealloc(libevwrapper_Async *self) {
|
|
self->ob_type->tp_free((PyObject *)self);
|
|
};
|
|
|
|
static void async_callback(EV_P_ ev_async *watcher, int revents) {};
|
|
|
|
static int
|
|
Async_init(libevwrapper_Async *self, PyObject *args, PyObject *kwds) {
|
|
PyObject *loop;
|
|
|
|
static char *kwlist[] = {"loop", NULL};
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, &loop)) {
|
|
PyErr_SetString(PyExc_TypeError, "unable to get file descriptor from socket");
|
|
return -1;
|
|
}
|
|
|
|
if (loop) {
|
|
Py_INCREF(loop);
|
|
self->loop = (libevwrapper_Loop *)loop;
|
|
} else {
|
|
return -1;
|
|
}
|
|
ev_async_init(&self->async, async_callback);
|
|
return 0;
|
|
};
|
|
|
|
static PyObject *
|
|
Async_start(libevwrapper_Async *self, PyObject *args) {
|
|
ev_async_start(self->loop->loop, &self->async);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
Async_send(libevwrapper_Async *self, PyObject *args) {
|
|
ev_async_send(self->loop->loop, &self->async);
|
|
Py_RETURN_NONE;
|
|
};
|
|
|
|
static PyMethodDef Async_methods[] = {
|
|
{"start", (PyCFunction)Async_start, METH_NOARGS, "Start the watcher"},
|
|
{"send", (PyCFunction)Async_send, METH_NOARGS, "Notify the event loop"},
|
|
{NULL} /* Sentinel */
|
|
};
|
|
|
|
static PyTypeObject libevwrapper_AsyncType = {
|
|
PyObject_HEAD_INIT(NULL)
|
|
0, /*ob_size*/
|
|
"cassandra.io.libevwrapper.Async", /*tp_name*/
|
|
sizeof(libevwrapper_Async), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
(destructor)Async_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*/
|
|
0, /*tp_getattro*/
|
|
0, /*tp_setattro*/
|
|
0, /*tp_as_buffer*/
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
|
"Async objects", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
Async_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
(initproc)Async_init, /* tp_init */
|
|
};
|
|
|
|
typedef struct libevwrapper_Prepare {
|
|
PyObject_HEAD
|
|
struct ev_prepare prepare;
|
|
struct libevwrapper_Loop *loop;
|
|
PyObject *callback;
|
|
} libevwrapper_Prepare;
|
|
|
|
static void
|
|
Prepare_dealloc(libevwrapper_Prepare *self) {
|
|
Py_XDECREF(self->loop);
|
|
Py_XDECREF(self->callback);
|
|
self->ob_type->tp_free((PyObject *)self);
|
|
}
|
|
|
|
static void prepare_callback(struct ev_loop *loop, ev_prepare *watcher, int revents) {
|
|
libevwrapper_Prepare *self = watcher->data;
|
|
|
|
PyGILState_STATE gstate;
|
|
gstate = PyGILState_Ensure();
|
|
|
|
PyObject *result = PyObject_CallFunction(self->callback, "O", self);
|
|
if (!result) {
|
|
PyErr_WriteUnraisable(self->callback);
|
|
}
|
|
Py_XDECREF(result);
|
|
|
|
PyGILState_Release(gstate);
|
|
}
|
|
|
|
static int
|
|
Prepare_init(libevwrapper_Prepare *self, PyObject *args, PyObject *kwds) {
|
|
PyObject *callback;
|
|
PyObject *loop;
|
|
|
|
if (!PyArg_ParseTuple(args, "OO", &loop, &callback)) {
|
|
return -1;
|
|
}
|
|
|
|
if (loop) {
|
|
Py_INCREF(loop);
|
|
self->loop = (libevwrapper_Loop *)loop;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
if (callback) {
|
|
if (!PyCallable_Check(callback)) {
|
|
PyErr_SetString(PyExc_TypeError, "callback parameter must be callable");
|
|
Py_XDECREF(loop);
|
|
return -1;
|
|
}
|
|
Py_INCREF(callback);
|
|
self->callback = callback;
|
|
}
|
|
ev_prepare_init(&self->prepare, prepare_callback);
|
|
self->prepare.data = self;
|
|
return 0;
|
|
}
|
|
|
|
static PyObject *
|
|
Prepare_start(libevwrapper_Prepare *self, PyObject *args) {
|
|
ev_prepare_start(self->loop->loop, &self->prepare);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyObject *
|
|
Prepare_stop(libevwrapper_Prepare *self, PyObject *args) {
|
|
ev_prepare_stop(self->loop->loop, &self->prepare);
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
static PyMethodDef Prepare_methods[] = {
|
|
{"start", (PyCFunction)Prepare_start, METH_NOARGS, "Start the Prepare watcher"},
|
|
{"stop", (PyCFunction)Prepare_stop, METH_NOARGS, "Stop the Prepare watcher"},
|
|
{NULL} /* Sentinal */
|
|
};
|
|
|
|
static PyTypeObject libevwrapper_PrepareType = {
|
|
PyObject_HEAD_INIT(NULL)
|
|
0, /*ob_size*/
|
|
"cassandra.io.libevwrapper.Prepare", /*tp_name*/
|
|
sizeof(libevwrapper_Prepare), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
(destructor)Prepare_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*/
|
|
0, /*tp_getattro*/
|
|
0, /*tp_setattro*/
|
|
0, /*tp_as_buffer*/
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
|
"Prepare objects", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
Prepare_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
(initproc)Prepare_init, /* tp_init */
|
|
};
|
|
|
|
static PyMethodDef module_methods[] = {
|
|
{NULL} /* Sentinal */
|
|
};
|
|
|
|
|
|
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
|
|
#define PyMODINIT_FUNC void
|
|
#endif
|
|
PyMODINIT_FUNC
|
|
initlibevwrapper(void)
|
|
{
|
|
PyObject *m;
|
|
|
|
if (PyType_Ready(&libevwrapper_LoopType) < 0)
|
|
return;
|
|
|
|
libevwrapper_IOType.tp_new = PyType_GenericNew;
|
|
if (PyType_Ready(&libevwrapper_IOType) < 0)
|
|
return;
|
|
|
|
libevwrapper_PrepareType.tp_new = PyType_GenericNew;
|
|
if (PyType_Ready(&libevwrapper_PrepareType) < 0)
|
|
return;
|
|
|
|
libevwrapper_AsyncType.tp_new = PyType_GenericNew;
|
|
if (PyType_Ready(&libevwrapper_AsyncType) < 0)
|
|
return;
|
|
|
|
m = Py_InitModule3("libevwrapper", module_methods, "libev wrapper methods");
|
|
|
|
if (PyModule_AddIntConstant(m, "EV_READ", EV_READ) == -1)
|
|
return;
|
|
if (PyModule_AddIntConstant(m, "EV_WRITE", EV_WRITE) == -1)
|
|
return;
|
|
if (PyModule_AddIntConstant(m, "EV_ERROR", EV_ERROR) == -1)
|
|
return;
|
|
|
|
Py_INCREF(&libevwrapper_LoopType);
|
|
if (PyModule_AddObject(m, "Loop", (PyObject *)&libevwrapper_LoopType) == -1)
|
|
return;
|
|
|
|
Py_INCREF(&libevwrapper_IOType);
|
|
if (PyModule_AddObject(m, "IO", (PyObject *)&libevwrapper_IOType) == -1)
|
|
return;
|
|
|
|
Py_INCREF(&libevwrapper_PrepareType);
|
|
if (PyModule_AddObject(m, "Prepare", (PyObject *)&libevwrapper_PrepareType) == -1)
|
|
return;
|
|
|
|
Py_INCREF(&libevwrapper_AsyncType);
|
|
if (PyModule_AddObject(m, "Async", (PyObject *)&libevwrapper_AsyncType) == -1)
|
|
return;
|
|
|
|
if (!PyEval_ThreadsInitialized()) {
|
|
PyEval_InitThreads();
|
|
}
|
|
}
|