| #include "Python.h" | |
| #include <CoreFoundation/CFRunLoop.h> | |
| /* These macros are defined in Python 2.3 but not 2.2 */ | |
| #ifndef PyMODINIT_FUNC | |
| #define PyMODINIT_FUNC void | |
| #endif | |
| #ifndef PyDoc_STRVAR | |
| #define PyDoc_STRVAR(Var,Str) static char Var[] = Str | |
| #endif | |
| #undef AUTOGIL_DEBUG | |
| static PyObject *AutoGILError; | |
| static void autoGILCallback(CFRunLoopObserverRef observer, | |
| CFRunLoopActivity activity, | |
| void *info) { | |
| PyThreadState **p_tstate = (PyThreadState **)info; | |
| switch (activity) { | |
| case kCFRunLoopBeforeWaiting: | |
| /* going to sleep, release GIL */ | |
| #ifdef AUTOGIL_DEBUG | |
| fprintf(stderr, "going to sleep, release GIL\n"); | |
| #endif | |
| *p_tstate = PyEval_SaveThread(); | |
| break; | |
| case kCFRunLoopAfterWaiting: | |
| /* waking up, acquire GIL */ | |
| #ifdef AUTOGIL_DEBUG | |
| fprintf(stderr, "waking up, acquire GIL\n"); | |
| #endif | |
| PyEval_RestoreThread(*p_tstate); | |
| *p_tstate = NULL; | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| static void infoRelease(const void *info) { | |
| /* XXX This should get called when the run loop is deallocated, | |
| but this doesn't seem to happen. So for now: leak. */ | |
| PyMem_Free((void *)info); | |
| } | |
| static PyObject * | |
| autoGIL_installAutoGIL(PyObject *self) | |
| { | |
| PyObject *tstate_dict = PyThreadState_GetDict(); | |
| PyObject *v; | |
| CFRunLoopRef rl; | |
| PyThreadState **p_tstate; /* for use in the info field */ | |
| CFRunLoopObserverContext context = {0, NULL, NULL, NULL, NULL}; | |
| CFRunLoopObserverRef observer; | |
| if (tstate_dict == NULL) | |
| return NULL; | |
| v = PyDict_GetItemString(tstate_dict, "autoGIL.InstalledAutoGIL"); | |
| if (v != NULL) { | |
| /* we've already installed a callback for this thread */ | |
| Py_INCREF(Py_None); | |
| return Py_None; | |
| } | |
| rl = CFRunLoopGetCurrent(); | |
| if (rl == NULL) { | |
| PyErr_SetString(AutoGILError, | |
| "can't get run loop for current thread"); | |
| return NULL; | |
| } | |
| p_tstate = PyMem_Malloc(sizeof(PyThreadState *)); | |
| if (p_tstate == NULL) { | |
| PyErr_SetString(PyExc_MemoryError, | |
| "not enough memory to allocate " | |
| "tstate pointer"); | |
| return NULL; | |
| } | |
| *p_tstate = NULL; | |
| context.info = (void *)p_tstate; | |
| context.release = infoRelease; | |
| observer = CFRunLoopObserverCreate( | |
| NULL, | |
| kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting, | |
| 1, 0, autoGILCallback, &context); | |
| if (observer == NULL) { | |
| PyErr_SetString(AutoGILError, | |
| "can't create event loop observer"); | |
| return NULL; | |
| } | |
| CFRunLoopAddObserver(rl, observer, kCFRunLoopDefaultMode); | |
| /* XXX how to check for errors? */ | |
| /* register that we have installed a callback for this thread */ | |
| if (PyDict_SetItemString(tstate_dict, "autoGIL.InstalledAutoGIL", | |
| Py_None) < 0) | |
| return NULL; | |
| Py_INCREF(Py_None); | |
| return Py_None; | |
| } | |
| PyDoc_STRVAR(autoGIL_installAutoGIL_doc, | |
| "installAutoGIL() -> None\n\ | |
| Install an observer callback in the event loop (CFRunLoop) for the\n\ | |
| current thread, that will lock and unlock the Global Interpreter Lock\n\ | |
| (GIL) at appropriate times, allowing other Python threads to run while\n\ | |
| the event loop is idle." | |
| ); | |
| static PyMethodDef autoGIL_methods[] = { | |
| { | |
| "installAutoGIL", | |
| (PyCFunction)autoGIL_installAutoGIL, | |
| METH_NOARGS, | |
| autoGIL_installAutoGIL_doc | |
| }, | |
| { 0, 0, 0, 0 } /* sentinel */ | |
| }; | |
| PyDoc_STRVAR(autoGIL_docs, | |
| "The autoGIL module provides a function (installAutoGIL) that\n\ | |
| automatically locks and unlocks Python's Global Interpreter Lock\n\ | |
| when running an event loop." | |
| ); | |
| PyMODINIT_FUNC | |
| initautoGIL(void) | |
| { | |
| PyObject *mod; | |
| if (PyErr_WarnPy3k("In 3.x, the autoGIL module is removed.", 1) < 0) | |
| return; | |
| mod = Py_InitModule4("autoGIL", autoGIL_methods, autoGIL_docs, | |
| NULL, PYTHON_API_VERSION); | |
| if (mod == NULL) | |
| return; | |
| AutoGILError = PyErr_NewException("autoGIL.AutoGILError", | |
| PyExc_Exception, NULL); | |
| if (AutoGILError == NULL) | |
| return; | |
| Py_INCREF(AutoGILError); | |
| if (PyModule_AddObject(mod, "AutoGILError", | |
| AutoGILError) < 0) | |
| return; | |
| } |