RESPONDERSEPor qué está lanzando PyGILState_Release errores Python fatales
Ok, he resuelto este problema. Es todo en cómo inicializar el estado del hilo. No necesita usar ReleaseLock en absoluto. Sólo tiene que añadir InitThreads llamada a su definición de módulo:
BOOST_PYTHON_MODULE(ModuleName)
{
PyEval_InitThreads();
...
}
Ok, he intentado para diagnosticar este problema durante horas y se vierte a través de lo que parece ser cada ejemplo en la web. Ahora estoy cansado así que me puede estar perdiendo algo obvio, pero esto es lo que está sucediendo:
Estoy envolviendo una biblioteca en boost python. Estoy ejecutando una secuencia de comandos python que importa la lib, construye algunos objetos y luego recibe devoluciones de llamada de C++, que vuelve a llamar a python. Antes de invocar llame a cualquier función de Python, intento adquirir el bloqueo de intérprete global. Aquí hay un código de muestra:
class ScopedGILRelease
{
public:
inline ScopedGILRelease()
{
d_gstate = PyGILState_Ensure();
}
inline ~ScopedGILRelease()
{
PyGILState_Release(d_gstate);
}
private:
PyGILState_STATE d_gstate;
};
class PyTarget : public DingoClient::ClientRequest::Target, public wrapper<DingoClient::ClientRequest::Target>
{
public:
PyTarget(PyObject* self_) : self(self_) {}
~PyTarget() {
ScopedGILRelease gil_lock;
}
PyObject* self;
void onData(const boost::shared_ptr<Datum>::P & data, const void * closure)
{
ScopedGILRelease gil_lock;
// invoke call_method to python
}
...
}
La biblioteca llama al método onData en el objeto Target como una devolución de llamada. En Python, heredamos de PyTarget e implementamos otro método. Luego usamos call_method <> para llamar a ese método. gil_lock adquiere el bloqueo y RIAA garantiza que el estado del hilo adquirido es siempre el único y que, de hecho, siempre se libera cuando se sale del alcance.
Sin embargo, cuando ejecuto esto en un script que intenta obtener una gran cantidad de devoluciones de llamada en esta función, siempre segfaults. La escritura es como la siguiente:
# Initialize the library and setup callbacks
...
# Wait until user breaks
while 1:
pass
Además, la secuencia de comandos de Python siempre se construye un objeto que se ejecuta:
PyEval_InitThreads();
PyEval_ReleaseLock();
antes de recibir devoluciones de llamada.
He reducido el código a donde ni siquiera estoy llamando a python en onData, solo estoy adquiriendo el bloqueo. En comunicado de que siempre accidentes, ya sea con:
Fatal Python error: ceval: tstate mix-up
Fatal Python error: This thread state must be current when releasing
o
Fatal Python error: ceval: orphan tstate
Fatal Python error: This thread state must be current when releasing
Es aparentemente al azar. Estoy loco aquí, porque siento que estoy usando el bloqueo GIL correctamente, sin embargo, parece que no funciona en absoluto.
Otras notas: Solo un hilo llama al método onData del objeto Target.
Cuando duermo en el bucle while en el módulo python de llamada con time.sleep(), parece que permite que el script se ejecute más tiempo, pero eventualmente script falla por defecto con problemas similares. La cantidad de tiempo que dura es proporcional a la cantidad de tiempo. Sueño (es decir, el tiempo de sueño (10) dura más que el tiempo. Sueño (0.01). Esto me hace pensar en cómo el script vuelve a adquirir el GIL sin mi permiso
PyGILState_Release y PyGILState_Ensure se llama en ningún otro lugar en mi código, en ningún otro lugar debería llamar a python.
actualización
He leído otra pregunta que sugiere enhebrado de importación en el módulo como una alternativa a la ejecución
PyEval_InitThreads();
PyEval_ReleaseLock();
Sin embargo, no parece funcionar cuando la importación enhebrar antes de mi módulo y elimine las dos líneas anteriores de mi contenedor de boost python.
Me alegro de que lo haya descubierto. Ese también me atrapó, cuándo. FYI, puede publicar una respuesta a su propia pregunta. Eso permitirá que las personas que les gusta su solución lo voten. –
Gracias, relativamente nuevo en el desbordamiento de la pila y todavía entendiéndolo. Publicaremos –