[Pyrex] Callbacks from other threads (crash in PyGILState_Release)
Geoff Schmidt
gschmidt at gschmidt.org
Tue Jun 20 21:56:25 UTC 2006
Stefan,
thank you very much for your response. It's very encouraging to know
that other people have been successful with this :)
> That's not enough. That only makes sure you /have/ the GIL, it
> doesn't allow
> you to use threads. You must wrap the sequence where you want to
> allow threads
> with PyEval_SaveThread() and PyEval_RestoreThread() as written in
> the Python
> docs. You then call GIL-ensure/release from the C callback function
> to get
> back the GIL while it's running.
My understanding of the docs was that PyEval_SaveThread effectively
suspend the thread (from Python's perspective) so that another
function had a chance to gain access to the interpreter by calling
PyEval_RestoreThread or PyGILState_Ensure, and that PyGILState_Ensure/
Release was basically PyEval_Save/RestoreThread plus logic to create
and destroy thread states if necessary. Is this understanding correct?
> cdef PyThreadState *state
> state = PyEval_SaveThread()
> function_that_calls_callback_in_thread(callback)
> PyEval_RestoreThread(state)
My function_that_calls_callback_in_thread() returns immediately, so I
didn't think PyEval_Save/RestoreThread was necessary -- by the time
the callback gets called, we're back in the interpreter, which (IIRC)
is yielding to other threads with Save/Restore every few hundred
bytecodes, giving the PyGILState_Ensure in my callback a chance to
take the lock.
I tried adding the extra PyEval_Save/RestoreThread that you show in
your example, but I still get the same segfault. The revised code is
below. Could you take a look and tell me if I understood your advise?
--- cut ---
cdef extern from "stdio.h":
int printf(char *str, ...)
cdef extern from "Python.h":
ctypedef int PyGILState_STATE
PyGILState_STATE PyGILState_Ensure()
void PyGILState_Release(PyGILState_STATE gstate)
struct PyThreadState:
pass
PyThreadState *PyEval_SaveThread()
void PyEval_RestoreThread(PyThreadState *state)
cdef extern from "pthread.h":
ctypedef void *pthread_t # it'll do
int pthread_create(pthread_t *thread, void *attr,
void *(*start_routine)(void *), void *arg)
cdef extern void *func(void *x):
printf("Entering func(%p)\n", x)
cdef PyGILState_STATE st
printf("PyGILState_Ensure\n")
st = PyGILState_Ensure()
printf("PyGILState_Release\n")
PyGILState_Release(st)
printf("Leaving func\n")
def callFuncDirectly():
func(NULL)
def callFuncInThread():
cdef PyThreadState *state
state = PyEval_SaveThread()
cdef pthread_t thr
pthread_create(&thr, NULL, func, NULL);
PyEval_RestoreThread(state)
--- cut ---
Sample output:
Python 2.4.3 (#1, Apr 7 2006, 10:54:33)
[GCC 4.0.1 (Apple Computer, Inc. build 5250)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo
>>> foo.callFuncInThread()
>>> Entering func(0x0)
PyGILState_Ensure
PyGILState_Release
Bus error
FWIW, this shows that callFuncInThread() is in fact returning to the
interpreter before the callback triggers.
thanks again,
Geoff Schmidt
gschmidt at gschmidt.org
More information about the Pyrex
mailing list