Added duplicate call to fileConfig() to ensure that it cleans up after itself correctly.
[python.git] / Mac / Modules / autoGIL.c
blob31088164ca6a2d16754ff77d61fe29e93e3b52ac
1 #include "Python.h"
2 #include <CoreFoundation/CFRunLoop.h>
4 /* These macros are defined in Python 2.3 but not 2.2 */
5 #ifndef PyMODINIT_FUNC
6 #define PyMODINIT_FUNC void
7 #endif
8 #ifndef PyDoc_STRVAR
9 #define PyDoc_STRVAR(Var,Str) static char Var[] = Str
10 #endif
13 #undef AUTOGIL_DEBUG
15 static PyObject *AutoGILError;
18 static void autoGILCallback(CFRunLoopObserverRef observer,
19 CFRunLoopActivity activity,
20 void *info) {
21 PyThreadState **p_tstate = (PyThreadState **)info;
23 switch (activity) {
24 case kCFRunLoopBeforeWaiting:
25 /* going to sleep, release GIL */
26 #ifdef AUTOGIL_DEBUG
27 fprintf(stderr, "going to sleep, release GIL\n");
28 #endif
29 *p_tstate = PyEval_SaveThread();
30 break;
31 case kCFRunLoopAfterWaiting:
32 /* waking up, acquire GIL */
33 #ifdef AUTOGIL_DEBUG
34 fprintf(stderr, "waking up, acquire GIL\n");
35 #endif
36 PyEval_RestoreThread(*p_tstate);
37 *p_tstate = NULL;
38 break;
39 default:
40 break;
44 static void infoRelease(const void *info) {
45 /* XXX This should get called when the run loop is deallocated,
46 but this doesn't seem to happen. So for now: leak. */
47 PyMem_Free((void *)info);
50 static PyObject *
51 autoGIL_installAutoGIL(PyObject *self)
53 PyObject *tstate_dict = PyThreadState_GetDict();
54 PyObject *v;
55 CFRunLoopRef rl;
56 PyThreadState **p_tstate; /* for use in the info field */
57 CFRunLoopObserverContext context = {0, NULL, NULL, NULL, NULL};
58 CFRunLoopObserverRef observer;
60 if (tstate_dict == NULL)
61 return NULL;
62 v = PyDict_GetItemString(tstate_dict, "autoGIL.InstalledAutoGIL");
63 if (v != NULL) {
64 /* we've already installed a callback for this thread */
65 Py_INCREF(Py_None);
66 return Py_None;
69 rl = CFRunLoopGetCurrent();
70 if (rl == NULL) {
71 PyErr_SetString(AutoGILError,
72 "can't get run loop for current thread");
73 return NULL;
76 p_tstate = PyMem_Malloc(sizeof(PyThreadState *));
77 if (p_tstate == NULL) {
78 PyErr_SetString(PyExc_MemoryError,
79 "not enough memory to allocate "
80 "tstate pointer");
81 return NULL;
83 *p_tstate = NULL;
84 context.info = (void *)p_tstate;
85 context.release = infoRelease;
87 observer = CFRunLoopObserverCreate(
88 NULL,
89 kCFRunLoopBeforeWaiting | kCFRunLoopAfterWaiting,
90 1, 0, autoGILCallback, &context);
91 if (observer == NULL) {
92 PyErr_SetString(AutoGILError,
93 "can't create event loop observer");
94 return NULL;
96 CFRunLoopAddObserver(rl, observer, kCFRunLoopDefaultMode);
97 /* XXX how to check for errors? */
99 /* register that we have installed a callback for this thread */
100 if (PyDict_SetItemString(tstate_dict, "autoGIL.InstalledAutoGIL",
101 Py_None) < 0)
102 return NULL;
104 Py_INCREF(Py_None);
105 return Py_None;
108 PyDoc_STRVAR(autoGIL_installAutoGIL_doc,
109 "installAutoGIL() -> None\n\
110 Install an observer callback in the event loop (CFRunLoop) for the\n\
111 current thread, that will lock and unlock the Global Interpreter Lock\n\
112 (GIL) at appropriate times, allowing other Python threads to run while\n\
113 the event loop is idle."
116 static PyMethodDef autoGIL_methods[] = {
118 "installAutoGIL",
119 (PyCFunction)autoGIL_installAutoGIL,
120 METH_NOARGS,
121 autoGIL_installAutoGIL_doc
123 { 0, 0, 0, 0 } /* sentinel */
126 PyDoc_STRVAR(autoGIL_docs,
127 "The autoGIL module provides a function (installAutoGIL) that\n\
128 automatically locks and unlocks Python's Global Interpreter Lock\n\
129 when running an event loop."
132 PyMODINIT_FUNC
133 initautoGIL(void)
135 PyObject *mod;
137 mod = Py_InitModule4("autoGIL", autoGIL_methods, autoGIL_docs,
138 NULL, PYTHON_API_VERSION);
139 if (mod == NULL)
140 return;
141 AutoGILError = PyErr_NewException("autoGIL.AutoGILError",
142 PyExc_Exception, NULL);
143 if (AutoGILError == NULL)
144 return;
145 Py_INCREF(AutoGILError);
146 if (PyModule_AddObject(mod, "AutoGILError",
147 AutoGILError) < 0)
148 return;