PyEval_CallObject and threads
Stefan Migowsky
smigowsky at dspace.de
Tue Mar 13 03:24:40 EST 2001
> [Phlip]
>
> Tim Peters wrote:
>
> > [Phlip]
> >> My colleague came over and asked me to write this:
> >>
> >> "We got multiple C++ pthreads calling PyEval_CallObject.
> How threadsafe
> >> is the Python API? A C++ module using Boost BPL creates multiple
threads,
> >> which then call back into Python. When two threads simultaneously call
> >> PyEval_CallObject we go boom."
> >>
> >> I will now go examine his serialization primitives. But if anyone has
any
> >> better ideas...
> >
> > With very few exceptions, all calls to Python C API functions must be
made
> > while holding the global interpreter lock. The exceptions have mostly
to
> > do with a handful of functions related to initialization and shutdown.
See
> > the section "Thread State and the Global Interpreter Lock" in the
Python/C
> > API Reference Manual for details.
>
> Further inspection reveals our architect(s) think we need to call Python
> script asynchronously from multiple threads. By 'lock' we assume both you
> and the section you cite mean 'block'. Isn't this what Stackless Python is
> for?
We had a similar problem having both : threads started with Python commands
and threads started and controlled by the C-runtime. Since we couldn't be
sure if a threadstate is always set( a NULL Threadstate would end up in a
PyFatalError if calling PyEvalSaveThread) we emulated the creation of a
Python
thread in the C-Runtime thread. For this you have to store the
Interpreterstate
and the use some code like this:
// This code is running is a thread spawned by the C-program.
....
if(NULL != g_ppyinstInterp) // Check global interpreterstate
{
return;
}
if(NULL != g_ppyoPyThreadFunc ) // Check the function to be called
{
PyThreadState *ppythstState = NULL;
PyObject *ppyobRes = NULL;
PyObject *ppyobArgs = NULL;
PyObject *ppyobFirstArg = NULL;
// 1. Set up the args
ppyobFirstArg = GetFirstArg();
ppyobArgs = PyTuple_New(1);
if(NULL == ppyobArgs )
{
Py_XDECREF(ppyobFirstArg);
return;
}
PyTuple_SetItem(ppyobArgs, 0, ppyobFirstArg);
// 2. Create a new threadstate for the current thread. This thread
// state is needed to insert the current thread into the Python
// interpreter.
ppythstState = PyThreadState_New(g_ppyinstInterp);
// 3. Acquire Thread
PyEval_AcquireThread(ppythstState);
// 4. Call the function
ppyobRes = PyEval_CallObjectWithKeywords(g_ppyoPyThreadFunc, ppyobArgs,
NULL);
Py_DECREF(ppyobArgs);
// 5. check result of callback function
if (res == NULL)
{
if (PyErr_ExceptionMatches(PyExc_SystemExit))
{
PyErr_Clear();
}
else
{
fprintf(stderr, "Unhandled exception in thread:\n");
PyErr_PrintEx(0);
}
}
else
{
Py_DECREF(res);
}
// 6. Cleanup
PyThreadState_Clear(ppythstState);
PyEval_ReleaseThread(ppythstState);
PyThreadState_Delete(ppythstState);
}
Stefan
More information about the Python-list
mailing list