Fix generator GC if yield from parent chain does not reach root

Parents may be unlinked while another generator sharing part of the
chain is running. As such, we cannot assume that the parent chain
goes all the way to the root. Instead walk backwards from root to
leaf, like we also do during destruction.
This commit is contained in:
Nikita Popov 2018-01-13 11:04:26 +01:00
parent cab0a814bd
commit 8c07170ddb
2 changed files with 34 additions and 7 deletions

View file

@ -0,0 +1,27 @@
--TEST--
Generator GC when the yield from parent chain does not reach the root
--FILE--
<?php
function root() {
yield 1;
yield 2;
}
function delegate($gen) {
yield from $gen;
}
$gen = delegate(delegate(root()));
$gen1 = delegate(delegate($gen));
$gen2 = delegate(delegate($gen));
var_dump($gen1->current());
var_dump($gen2->current());
$gen1->next();
$gen1->next();
gc_collect_cycles();
?>
--EXPECT--
int(1)
int(1)

View file

@ -276,9 +276,9 @@ static uint32_t calc_gc_buffer_size(zend_generator *generator) /* {{{ */
/* Yield from root references */
if (generator->node.children == 0) {
zend_generator *child = generator, *root = generator->node.ptr.root;
while (root != child) {
child = child->node.parent;
zend_generator *root = generator->node.ptr.root;
while (root != generator) {
root = zend_generator_get_child(&root->node, generator);
size++;
}
}
@ -341,10 +341,10 @@ static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* {
}
if (generator->node.children == 0) {
zend_generator *child = generator, *root = generator->node.ptr.root;
while (root != child) {
child = child->node.parent;
ZVAL_OBJ(gc_buffer++, &child->std);
zend_generator *root = generator->node.ptr.root;
while (root != generator) {
ZVAL_OBJ(gc_buffer++, &root->std);
root = zend_generator_get_child(&root->node, generator);
}
}