Skip to content

Commit

Permalink
Fix generator GC if yield from parent chain does not reach root
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikic committed Jan 13, 2018
1 parent cab0a81 commit 8c07170
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 7 deletions.
27 changes: 27 additions & 0 deletions Zend/tests/generators/gc_with_root_parent_mismatch.phpt
Original file line number Diff line number Diff line change
@@ -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)
14 changes: 7 additions & 7 deletions Zend/zend_generators.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++;
}
}
Expand Down Expand Up @@ -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);
}
}

Expand Down

0 comments on commit 8c07170

Please sign in to comment.