J'ai créé une extension PHP avec SWIG et tout fonctionne correctement, mais j'observe un étrange comportement de récupération de place lors du chaînage des appels de méthode. Par exemple, cela fonctionne:Garbage de ressources collectées trop tôt
$results = $response->results();
$row = $results->get(0)->iterator()->next();
printf('%s %s' . "\n", $row->getString(0), $row->getString(1));
Mais ce défaut seg:
$row = $response->results()->get(0)->iterator()->next();
printf('%s %s' . "\n", $row->getString(0), $row->getString(1));
La seule différence est que le premier crée $results
, tandis que les deuxièmes chaînes les appels en même temps. En fait, SWIG expose uniquement des fonctions à PHP et génère des classes proxy PHP pour interagir avec eux. Ces classes proxy contiennent essentiellement une ressource qui est transmise à chacune des fonctions exposées avec tous les autres arguments que ces fonctions prendraient normalement. Pensant que ces classes de proxy étaient peut-être le problème, j'ai retravaillé le code pour les contourner et utiliser à la place les fonctions exposées directement. Comme précédemment, cela fonctionne:
$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));
Et encore, ce défaut de SEG:
$row = TableIterator_next(Table_iterator(Tables_get(InvocationResponse_results($response->_cPtr), 0)));
printf('%s %s' . "\n", Row_getString($row, 0), Row_getString($row, 1));
Encore une fois, la seule différence est que le premier crée $results
, tandis que les secondes chaînes les appels en même temps.
À ce stade, j'ai passé un certain temps à déboguer dans gdb/valgrind et j'ai déterminé que le destructeur pour ce que InvocationResponse_results
renvoie est appelé trop tôt lors du chaînage d'appels. Pour observer, j'ai inséré std::cout
déclarations au sommet des fonctions C++ exposées et leurs destructeurs. Ceci est la sortie sans enchaînant:
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
J'ai imprimé ---
à la fin du script pour pouvoir différencier ce qui se passe lors de l'exécution du script et ce qui se passe après. Hola Mundo
provient de printf
. Le reste vient de C++. Comme vous pouvez le voir, tout est appelé dans l'ordre attendu. Les destructeurs sont seulement appelés après l'exécution du script, bien que le destructeur TableIterator
soit appelé plus tôt que ce à quoi je m'attendais. Cependant, cela n'a causé aucun problème et n'est probablement pas lié. Maintenant, comparez cela à la sortie avec enchaînant:
InvocationResponse_results()
Tables_get()
__wrap_delete_Tables
Table_iterator()
TableIterator_next()
__wrap_delete_TableIterator
Row_getString()
Segmentation fault (core dumped)
Sans la valeur de retour de InvocationResponse_results
sauvés dans $results
, il est heureusement des déchets collectés avant l'exécution devient même hors de la chaîne d'appel (entre Tables_get
et Table_iterator
) et ce rapidement provoque des problèmes sur la route, conduisant finalement à un défaut de seg.
J'ai également inspecté les comptages de référence en utilisant xdebug_debug_zval()
à divers endroits, mais je n'ai rien trouvé d'inhabituel. Voici sa sortie sur $results
et $row
sans enchaînant:
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)
Et $row
avec enchaînant:
row: (refcount=1, is_ref=0)=resource(21) of type (_p_voltdb__Row)
J'ai passé quelques jours sur ce maintenant et je suis à peu près d'idées , donc vraiment tout aperçu sur la façon de résoudre ce problème serait grandement apprécié.
Il est hautement improbable que quiconque n'ayant pas de pouvoirs de débogage psychique soit capable de comprendre cela. Je vous suggère de mettre un point d'arrêt dans '_zend_list_delete' et de comprendre pourquoi le code appelant supprime la ressource. Ce peut être le refcount de ressource frappant 0 ou une suppression directe. – Artefacto
@Artefacto J'ai jeté un oeil à '_zend_list_delete' pendant que' __wrap_delete_Tables' est appelé et dans les deux cas (pas d'erreur de segment et erreur de segment), il est récupéré car son refcount ('--le-> refcount') vaut -1. –
Alors découvrez pourquoi '__wrap_delete_Tables' est appelé à ce moment précis dans une occasion mais pas dans l'autre et continuez à monter. – Artefacto