• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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