#include "Python.h" #include #include #include #include #include #include // what the hell is this all about? #ifndef __USE_GNU # define __USE_GNU # include # undef __USE_GNU #else # include #endif // based on my staring at Zach Brown's phhttpd. [or was it pthffft?] // [from kegel's c10k page] // Much of the overhead of calling poll() can be eliminated by using // fcntl(fd, F_SETSIG, signum), which associates a signal with each // file descriptor, and raises that signal when a normal I/O function // like read() or write() completes. Available in Linux 2.3.21 or // higher. To use this, you choose a realtime signal number, mask // that signal and SIGIO with sigprocmask(), use fcntl(fd, F_SETSIG, // signum) on each fd, write a normal poll() outer loop, and inside // it, after you've handled all the fd's noticed by poll(), you loop // calling sigwaitinfo(). If sigwaitinfo returns your realtime // signal, siginfo.si_fd and siginfo.si_band give the same // information as pollfd.fd and pollfd.revents would after a call to // poll(), so you handle the i/o, and continue calling // sigwaitinfo(). If sigwaitinfo returns a traditional SIGIO, the // signal queue overflowed, so you flush the signal queue by // temporarily changing the signal handler to SIG_DFL, and break back // to the outer poll() loop. // how strange... extern int __libc_allocate_rtsig(int); static int our_rtsig; static sigset_t our_set; static PyObject * coro_rtsig_attach (PyObject * self, PyObject * args) { int fd; if (!PyArg_ParseTuple (args, "i", &fd)) { return NULL; } else { if ( // (fcntl (fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC) < 0) || (fcntl (fd, F_SETFL, O_ASYNC) < 0) || (fcntl (fd, F_SETSIG, our_rtsig) < 0) || (fcntl (fd, F_SETOWN, getpid()) < 0)) { PyErr_SetFromErrno (PyExc_OSError); return NULL; } else { Py_INCREF(Py_None); return Py_None; } } } static PyObject * coro_rtsig_get_next_event (PyObject * self, PyObject * args) { if (!PyArg_ParseTuple (args, "")) { return NULL; } else { siginfo_t si; int ret; ret = sigwaitinfo (&our_set, &si); if (ret < 0) { PyErr_SetFromErrno (PyExc_OSError); return NULL; } else { return Py_BuildValue ("ii", si.si_fd, si.si_band); } } } static PyMethodDef coro_rtsig_methods[] = { {"attach", coro_rtsig_attach, 1}, {"get_next_event", coro_rtsig_get_next_event, 1}, {NULL, NULL} // sentinel }; DL_EXPORT(void) initcoro_rtsig() { PyObject *m, *d; sigset_t old_set; // Create the module and add the functions m = Py_InitModule("coro_rtsig", coro_rtsig_methods); // Add some symbolic constants to the module d = PyModule_GetDict(m); // PyDict_SetItemString(d, "error", ErrorObject); // I'm assuming this is linux-specific. Don't see anything // obvious in the 'single unix spec' manpages. // allocate a real-time signal and fire it up. our_rtsig = __libc_allocate_rtsig(0); // I wonder if signal(SIGIO) needs to be done before this? // or does it even need to be done? sigemptyset (&our_set); sigaddset (&our_set, our_rtsig); sigprocmask (SIG_BLOCK, &our_set, &old_set); }