another callback question

Charles Crain ccrain at austin.rr.com
Mon Dec 18 10:03:56 EST 2000


Thanks to everyone who replied to my question about the currentThread()
warning.  I have another, more involved question this time.

Again, I am attempting to write a .pyd wrapper for a C library that uses
callbacks.  I want to propagate the callbacks into Python.  I have gotten as
far as creating a new PyThreadState for the callback thread, then using
PyEval_AcquireThread()/PyEval_ReleaseThread() within the callbacks.  My
problem, however, is this: the callbacks are not always invoked from this
"callback thread."  Sometimes, the callbacks are invoked SYNCHRONOUSLY from
within calls intp this C library.

So for instance, I may have a python-exported function call a function in
this C API, which, from within the function, invokes one of the callbacks.
The problem here is that when I get to the callback itself, since it was
called from within the interpreter, I already have a PyThreadState attached
to the thread, and PyEval_AcquireThread() blows up with a fatal error.  And
I can't use PyThreadState_Get() as a check, because this call will die with
a fatal error if the current thread state is NULL.  So far, I have only come
up with two possible solutions to this, neither of them very elegant.

1.  Given that I know all the C API functions that might invoke callbacks
synchonously (which I don't, but I can find out by experimentation), I can
do a Py_BEGIN_ALLOW_THREADS before the call and a Py_END_ALLOW_THREADS
after.  Since Py_BEGIN_ALLOW_THREADS, among other things, sets the current
PyThreadState to NULL (right?), the subsequent call to
PyEval_AcquireThreads() should work.  I have had limited success with this
technique however, since I have yet to determine all the places that I need
to do this.

2.  Instead of saying PyEval_AcquireThread()/PyEval_ReleaseThread(), I
instead do the following:

my_callback()
{
PyEval_AcquireLock();
pTempThread = PyThreadState_Swap(pCallbackThread);

// Do stuff, including the call to PyEval_CallObject()

pCallbackThread = PyThreadState_Swap(pTempThread);
PyEval_ReleaseLock();
}

That seems to work all the time (on Windows at least).  My questions are:

1.  Is there a better way to solve this problem and/or is one of my current
solutions better than the other?
2.  It occurs to me that, if this callback is called synchonously, the
global interpreter lock might already be held upon entry into the callback.
The dox say that calling PyEval_AcquireLock() from a thread that already
owns the lock will deadlock.  Clearly this doesn't happen on Windows, as my
stuff works, but I am afraid that this fact arises from the fact that
Windows critical sections are re-entrant, whereas similar lock objects on
other OS's may not be.  Does anyone forsee this being a problem were I to
try to port this module to Linux for instance?







More information about the Python-list mailing list