Working on a control of an eGige camera, having a C library, I've started a cython code project with the idea to have the best things of each language.
The library provides a way to listen a heartbeat from the camera to know if it has been disconnected. The callback with in a C++ class I've already did, but from this C++ class, call a python method of a class is falling into a Segmentation fault in all the ways I've tried.
I've encapsulated it in a specific C++ class:
#include <Python.h>
/* (...) */
PyCallback::PyCallback(PyObject* self, const char* methodName)
{
Py_XINCREF(self);
_self = self;
_method = PyObject_GetAttrString(self, methodName);
}
PyCallback::~PyCallback()
{
Py_XDECREF(_self);
}
void PyCallback::execute()
{
try
{
PyObject *args = PyTuple_Pack(1,_self);
PyObject_CallFunctionObjArgs(_method, args);
}catch(...){
_error("Exception calling python");
}
}
From a cython object the code is:
cdef class Camera(...):
# (...)
cdef registerRemovalCallback(self):
cdef:
PyCallback* obj
obj = new PyCallback(<PyObject*> self, <char*> "cameraRemovalCallback")
cdef cameraRemovalCallback(self):
self._isPresent = False
The lowest part of the backtrace it's just when try to prepare the arguments.
#0 0x00007ffff7b24592 in PyErr_Restore () from /usr/lib64/libpython2.6.so.1.0
#1 0x00007ffff7b23fef in PyErr_SetString () from /usr/lib64/libpython2.6.so.1.0
#2 0x00007ffff7b314dd in ?? () from /usr/lib64/libpython2.6.so.1.0
#3 0x00007ffff7b313ca in ?? () from /usr/lib64/libpython2.6.so.1.0
#4 0x00007ffff7b316c1 in ?? () from /usr/lib64/libpython2.6.so.1.0
#5 0x00007ffff7b31d2f in ?? () from /usr/lib64/libpython2.6.so.1.0
#6 0x00007ffff7b31e9c in Py_BuildValue () from /usr/lib64/libpython2.6.so.1.0
#7 0x00007ffff637cbf8 in PyCallback::execute (this=0x16212a0) at pylon/PyCallback.cpp:53
#8 0x00007ffff6376248 in CppCamera::removalCallback (this=0x161fb30, pDevice=<value optimized out>) at pylon/Camera.cpp:387
I've tried to make the arguments using _Py_BuildValue("(self)", self); but then I've got a segfault there.
I've tried also with PyObject_CallFunctionObjArgs with NULL in the arguments field, thinking that perhaps the pointer to "self" is already embedded as the method point to an specific address with in this object. But them I've got the segfault there.
Do anyone see my mistake? Something there that shall be made in a different way? I hope this is a misunderstanding from my side about who to do that.
Update @ 2016/08/01:
Following comments indications, two modifications are made in the code:
First of all the pointer storage to the PyCallback has been stored as a member of the Camera cython class:
cdef class Camera(...):
cdef:
#(...)
PyCallback* _cbObj
# (...)
cdef registerRemovalCallback(self):
self._cbObj = new PyCallback(<PyObject*> self, <char*> "cameraRemovalCallback")
cdef cameraRemovalCallback(self):
self._isPresent = False
Even this is a fundamental source of segfaults it looks it wasn't involved in the current one.
Then PyCallback::execute() in the c++, I've made some changes. After reading about the GIL (Global Interpreter Lock) and add some calls for it, I've added a check that may guide to the solution:
PyCallback::PyCallback(PyObject* self, const char* methodName)
{
Py_Initialize();
Py_XINCREF(self);
_self = self;
_method = PyObject_GetAttrString(self, methodName);
}
PyCallback::~PyCallback()
{
Py_XDECREF(_self);
Py_Finalize();
}
void PyCallback::execute()
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
try
{
if ( PyCallable_Check(_method) )
{
_info("Build arguments and call method");
PyObject *args = Py_BuildValue("(O)", _self);
PyObject *kwargs = Py_BuildValue("{}", "", NULL);
PyObject_Call(_method, args, kwargs);
}
else
{
_warning("The given method is not callable!");
}
}
catch(...)
{
// TODO: collect and show more information about the exception
_error("Exception calling python");
}
PyGILState_Release(gstate);
}
Even I'm not sure how to do the call, the main point is that the _PyCallable_Check_ returns false.
I've also tested to use the typedef option and C pointer to function to call it with the same segfault result.
Update @ 2016/08/03:
I've proceed with the suggested modifications. cameraRemovalCallback
is now changed from cdef
to def
and some if
s in the PyCallback
reports that the method now is found. Also has been added to ~PyCallback()
a call to Py_XDECREF(_method)
in case it was found in the constructor. The useless try-catch
has been also removed.
From the reference to the Python's Object protocol, that DavidW mention, I've check many of the *Call*
combinations: falling to the segfault.
I think this question is becoming dirty and is getting the appearance of a forum (question->answer->replay->...). I'm sorry about that, and I'll try to, next time I'll write, tell the segfault was solve and just what was.