2010-08-19 14 views
9

He creado una extensión de PHP con SWIG y todo funciona bien, pero estoy observando un extraño comportamiento de recolección de basura al encadenar llamadas de método. Por ejemplo, esto funciona:Basura de recursos recogidos demasiado pronto

faltas
$results = $response->results(); 
$row = $results->get(0)->iterator()->next(); 
printf('%s %s' . "\n", $row->getString(0), $row->getString(1)); 

Pero esta seg:

$row = $response->results()->get(0)->iterator()->next(); 
printf('%s %s' . "\n", $row->getString(0), $row->getString(1)); 

La única diferencia es que el primero crea $results, mientras que las segundas cadenas de las llamadas.

SWIG en realidad solo expone funciones a PHP y genera clases de proxy PHP para interactuar con ellas. Estas clases de proxy básicamente contienen un recurso que se pasa a cada una de las funciones expuestas junto con cualquier otro argumento que esas funciones normalmente tomarían. Pensando que tal vez estas clases proxy eran el problema, volví a trabajar el código para eludirlas y en su lugar usé las funciones expuestas directamente. Al igual que antes, esto funciona:

$results = InvocationResponse_results($response->_cPtr); 
$row = TableIterator_next(Table_iterator(Tables_get($results, 0))); 
printf('%s %s' . "\n", Row_getString($row, 0), Row_getString($row, 1)); 

Y de nuevo, esta seg fallos:

$row = TableIterator_next(Table_iterator(Tables_get(InvocationResponse_results($response->_cPtr), 0))); 
printf('%s %s' . "\n", Row_getString($row, 0), Row_getString($row, 1)); 

Una vez más, la única diferencia es que el primero crea $results, mientras que las segundas cadenas de las llamadas.

En este punto, pasé un tiempo depurando en gdb/valgrind y determiné que el destructor para lo que InvocationResponse_results devuelve se llama demasiado temprano cuando se encadenan llamadas. Para observar, inserté las declaraciones std::cout en la parte superior de las funciones expuestas de C++ y sus destructores. Esta es la salida sin encadenamiento:

InvocationResponse_results() 
Tables_get() 
Table_iterator() 
TableIterator_next() 
__wrap_delete_TableIterator 
Row_getString() 
Row_getString() 
Hola Mundo 
--- 
__wrap_delete_InvocationResponse 
__wrap_delete_Row 
__wrap_delete_Tables 

Imprimí --- al final de la secuencia de comandos para ser capaz de diferenciar lo que sucede durante la ejecución del script y qué sucede después. Hola Mundo es de printf. El resto es de C++. Como puede ver, todo se llama en el orden esperado. Los destructores solo se invocan después de la ejecución del guión, aunque se llama al destructor TableIterator antes de lo que hubiera esperado. Sin embargo, esto no ha causado ningún problema y es probable que no esté relacionado. Ahora compare esto a la salida con el encadenamiento:

InvocationResponse_results() 
Tables_get() 
__wrap_delete_Tables 
Table_iterator() 
TableIterator_next() 
__wrap_delete_TableIterator 
Row_getString() 
Segmentation fault (core dumped) 

Sin el valor de retorno de InvocationResponse_results se guarda en $results, es felizmente basura recogida antes de la ejecución, incluso sale de la cadena de llamadas (entre Tables_get y Table_iterator) y esto de manera rápida causa problemas en el futuro, lo que en última instancia conduce a una falla seg.

También inspeccioné los recuentos de referencia utilizando xdebug_debug_zval() en varios lugares, pero no detecté nada inusual. Aquí está su salida en $results y $row sin encadenamiento:

results: (refcount=1, is_ref=0)=resource(18) of type (_p_std__vectorT_voltdb__Table_t) 
row: (refcount=1, is_ref=0)=resource(21) of type (_p_voltdb__Row) 

Y en $row con el encadenamiento:

row: (refcount=1, is_ref=0)=resource(21) of type (_p_voltdb__Row) 

he pasado un par de días en este momento y estoy casi sin ideas , por lo que en realidad sería muy apreciado cualquier idea sobre cómo resolver esto.

+0

Es muy poco probable que alguien sin poderes de depuración psíquica pueda resolver esto. Sugiero que coloque un punto de interrupción en '_zend_list_delete' y descubra por qué el código de llamada está borrando el recurso. Puede ser el recuento de recursos que golpea 0 o una eliminación directa. – Artefacto

+0

@Artefacto Eché un vistazo dentro de '_zend_list_delete' mientras' __wrap_delete_Tables' se está llamando y en ambos casos (sin fallo seg y seg), es basura recolectada porque su refcount ('--le-> refcount') es -1. –

+0

Así que averigüe por qué '__wrap_delete_Tables' se llama en ese momento específico en una ocasión pero no en la otra y continúa subiendo. – Artefacto

Respuesta

1

This resultó ser parte del problema en un problema de depuración similar segfaulting. (lo que dijo Artefacto)

Cuestiones relacionadas