Reputation: 5564
I've a simplest test case here:
%module test
%{
static char* MyExceptionName = "_test.MyException";
static PyObject* MyException = NULL;
%}
%inline %{
static PyObject* Foo()
{
PyErr_SetNone(MyException);
return NULL;
}
%}
%init
{
MyException = PyErr_NewException(MyExceptionName, NULL, NULL);
}
Here's the setup.py script:
from distutils.core import setup, Extension
setup(name="test", version="1.0",
ext_modules = [Extension("_test", ["test_wrap.c"])])
When I build it and test it as follows, I get:
swig -python -threads test.i
python_d -c "import test; test.Foo()"
Fatal Python error: PyThreadState_Get: no current thread
The traceback I got was
python27_d.dll!Py_FatalError(const char * msg=0x000000001e355a00) Line 1677 C
python27_d.dll!PyThreadState_Get() Line 330 C
python27_d.dll!PyErr_Restore(_object * type=0x00000000020d50b8, _object * value=0x0000000000000000, _object * traceback=0x0000000000000000) Line 27 + 0x5 bytes C
python27_d.dll!PyErr_SetObject(_object * exception=0x00000000020d50b8, _object * value=0x0000000000000000) Line 58 C
python27_d.dll!PyErr_SetNone(_object * exception=0x00000000020d50b8) Line 64 C
_test_d.pyd!Foo() Line 2976 C
Environment:
Upvotes: 5
Views: 1662
Reputation: 5564
The reason for the error, as it turns out is because when -threads
is enabled via
swig -threads -python test.i
We get something like this (excess code has been redacted):
PyObject *_wrap_Foo(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *resultobj = 0;
PyObject *result = 0 ;
if (!PyArg_ParseTuple(args,(char *)":Foo")) SWIG_fail;
{
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
result = (PyObject *)Foo();
SWIG_PYTHON_THREAD_END_ALLOW;
}
resultobj = result;
return resultobj;
fail:
return NULL;
}
static PyObject* Foo()
{
PyErr_SetNone(MyException);
return NULL;
}
See, when Foo() is called, the global interpreter lock has already been released. Foo() should no longer make any Python API calls.
The solution is to use SWIG_Python_SetErrorObj, which grabs the Global Interpreter Lock before calling Python C API.
static PyObject* Foo()
{
SWIG_Python_SetErrorObj(MyException, Py_None);
return NULL;
}
Another method is to use SWIG_PYTHON_THREAD_BEGIN_BLOCK; and SWIG_PYTHON_THREAD_END_BLOCK;
static PyObject* Foo()
{
SWIG_PYTHON_THREAD_BEGIN_BLOCK;
PyErr_SetNone(MyException);
SWIG_PYTHON_THREAD_END_BLOCK;
return NULL;
}
Upvotes: 3