From 6767eed22a8a3beecaf4a4e0d2bde109dc73d895 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sat, 3 May 2003 00:18:55 +0000 Subject: Short and sweet module doing something very useful: for event loop based threads, release the GIL when the event loop goes to sleep, and acquire it again when the event loop wakes up again. OSX-only. --- Mac/Modules/autoGIL.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 Mac/Modules/autoGIL.c diff --git a/Mac/Modules/autoGIL.c b/Mac/Modules/autoGIL.c new file mode 100644 index 0000000..4ee1e75 --- /dev/null +++ b/Mac/Modules/autoGIL.c @@ -0,0 +1,149 @@ +#include "Python.h" +#include + +/* 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 running." +); + +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; + + 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; +} -- cgit v0.12