OOF2: The Manual
Name
acquirePyLock — ensure thread-safe Python API calls
Synopsis
#include "common/threadstate.h"
void
acquirePyLock
( |
) ;
|
void
releasePyLock
( |
) ;
|
#include "common/ooferror.h"
void
pythonErrorRelay
( |
) ;
|
Description
The three functions described here are used whenever OOF2 C++ code calls Python API functions that might invoke the Python interpreter. It's often difficult to tell when the interpreter will be invoked, so it's a good idea to treat all Python API calls in this way. See http://docs.python.org/ext/thinIce.html for a relevant discussion (although in a different context).
Because the Python interpreter is not thread safe, whenever
a C++ function in OOF2 needs to call the Python API, it
must first acquire the Python global interpreter lock by
calling acquirePyLock()
. When it's
through with the Python API calls, it must release the lock
by calling releasePyLock()
.
A function that neglects to call
acquirePyLock()
risks crashing the
Python interpreter. A function that neglects to call
releasePyLock
will probably deadlock
the program when the C++ routine finishes. If running
OOF2 with the --unthreaded
option solves a crashing or deadlocking problem, then check
the code for missing calls to
acquirePyLock
and
releasePyLock
.
It is important to ensure that
releasePyLock
is called even if an
exception is thrown during the Python API calls. Python API
calls should always occur within a try
... catch
block, like this:
#include "common/threadstate.h" acquirePyLock(); try { // Call Python API } catch (...) { releasePyLock(); throw; } releasePyLock();
It's possible that calls to the Python API will raise a
Python exception but not throw a C++
exception. Generally, if a Python API function returns
NULL
, an exception has been raised. The
OOF2 function pythonErrorRelay
ensures that Python exceptions are re-raised in Python when
the C++ function exits. It should be used like this:
#include "common/ooferror.h" #include "common/threadstate.h" acquirePyLock(); try { PyObject *func, *args; // assume these have been set PyObject *result = PyEval_Call_Object(func, args); // for example if(result == NULL) pythonErrorRelay(); // do something with result } catch (...) { releasePyLock(); throw; } releasePyLock();
pythonErrorRelay
raises a C++ exception
that will be converted back into a Python exception when
control returns to Python. The mechanism even works if the
Python exception was caused by a C++ exception that occurred
during a second call back into C++, as long as the original
C++ exception was derived from the OOF2 ErrError
class.