Skip to content

Commit 8c07170

Browse files
committed
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.
1 parent cab0a81 commit 8c07170

File tree

2 files changed

+34
-7
lines changed

2 files changed

+34
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Generator GC when the yield from parent chain does not reach the root
3+
--FILE--
4+
<?php
5+
6+
function root() {
7+
yield 1;
8+
yield 2;
9+
}
10+
11+
function delegate($gen) {
12+
yield from $gen;
13+
}
14+
15+
$gen = delegate(delegate(root()));
16+
$gen1 = delegate(delegate($gen));
17+
$gen2 = delegate(delegate($gen));
18+
var_dump($gen1->current());
19+
var_dump($gen2->current());
20+
$gen1->next();
21+
$gen1->next();
22+
gc_collect_cycles();
23+
24+
?>
25+
--EXPECT--
26+
int(1)
27+
int(1)

Zend/zend_generators.c

+7-7
Original file line numberDiff line numberDiff line change
@@ -276,9 +276,9 @@ static uint32_t calc_gc_buffer_size(zend_generator *generator) /* {{{ */
276276

277277
/* Yield from root references */
278278
if (generator->node.children == 0) {
279-
zend_generator *child = generator, *root = generator->node.ptr.root;
280-
while (root != child) {
281-
child = child->node.parent;
279+
zend_generator *root = generator->node.ptr.root;
280+
while (root != generator) {
281+
root = zend_generator_get_child(&root->node, generator);
282282
size++;
283283
}
284284
}
@@ -341,10 +341,10 @@ static HashTable *zend_generator_get_gc(zval *object, zval **table, int *n) /* {
341341
}
342342

343343
if (generator->node.children == 0) {
344-
zend_generator *child = generator, *root = generator->node.ptr.root;
345-
while (root != child) {
346-
child = child->node.parent;
347-
ZVAL_OBJ(gc_buffer++, &child->std);
344+
zend_generator *root = generator->node.ptr.root;
345+
while (root != generator) {
346+
ZVAL_OBJ(gc_buffer++, &root->std);
347+
root = zend_generator_get_child(&root->node, generator);
348348
}
349349
}
350350

0 commit comments

Comments
 (0)