1 #include "Python.h"
2 #include <CoreFoundation/CFRunLoop.h>
3
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
11
12
13 #undef AUTOGIL_DEBUG
14
15 static PyObject *AutoGILError;
16
17
autoGILCallback(CFRunLoopObserverRef observer,CFRunLoopActivity activity,void * info)18 static void autoGILCallback(CFRunLoopObserverRef observer,
19 CFRunLoopActivity activity,
20 void *info) {
21 PyThreadState **p_tstate = (PyThreadState **)info;
22
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;
41 }
42 }
43
infoRelease(const void * info)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);
48 }
49
50 static PyObject *
autoGIL_installAutoGIL(PyObject * self)51 autoGIL_installAutoGIL(PyObject *self)
52 {
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;
59
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;
67 }
68
69 rl = CFRunLoopGetCurrent();
70 if (rl == NULL) {
71 PyErr_SetString(AutoGILError,
72 "can't get run loop for current thread");
73 return NULL;
74 }
75
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;
82 }
83 *p_tstate = NULL;
84 context.info = (void *)p_tstate;
85 context.release = infoRelease;
86
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;
95 }
96 CFRunLoopAddObserver(rl, observer, kCFRunLoopDefaultMode);
97 /* XXX how to check for errors? */
98
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;
103
104 Py_INCREF(Py_None);
105 return Py_None;
106 }
107
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."
114 );
115
116 static PyMethodDef autoGIL_methods[] = {
117 {
118 "installAutoGIL",
119 (PyCFunction)autoGIL_installAutoGIL,
120 METH_NOARGS,
121 autoGIL_installAutoGIL_doc
122 },
123 { 0, 0, 0, 0 } /* sentinel */
124 };
125
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."
130 );
131
132 PyMODINIT_FUNC
initautoGIL(void)133 initautoGIL(void)
134 {
135 PyObject *mod;
136
137 if (PyErr_WarnPy3k("In 3.x, the autoGIL module is removed.", 1) < 0)
138 return;
139
140 mod = Py_InitModule4("autoGIL", autoGIL_methods, autoGIL_docs,
141 NULL, PYTHON_API_VERSION);
142 if (mod == NULL)
143 return;
144 AutoGILError = PyErr_NewException("autoGIL.AutoGILError",
145 PyExc_Exception, NULL);
146 if (AutoGILError == NULL)
147 return;
148 Py_INCREF(AutoGILError);
149 if (PyModule_AddObject(mod, "AutoGILError",
150 AutoGILError) < 0)
151 return;
152 }
153