| #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; | 
 | } |