From 8c07170ddbe28d8cc31e71f0ceb94fc4ac329657 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sat, 13 Jan 2018 11:04:26 +0100 Subject: [PATCH] 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. --- .../gc_with_root_parent_mismatch.phpt | 27 +++++++++++++++++++ Zend/zend_generators.c | 14 +++++----- 2 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 Zend/tests/generators/gc_with_root_parent_mismatch.phpt diff --git a/Zend/tests/generators/gc_with_root_parent_mismatch.phpt b/Zend/tests/generators/gc_with_root_parent_mismatch.phpt new file mode 100644 index 00000000000..ee388b1c76d --- /dev/null +++ b/Zend/tests/generators/gc_with_root_parent_mismatch.phpt @@ -0,0 +1,27 @@ +--TEST-- +Generator GC when the yield from parent chain does not reach the root +--FILE-- +current()); +var_dump($gen2->current()); +$gen1->next(); +$gen1->next(); +gc_collect_cycles(); + +?> +--EXPECT-- +int(1) +int(1) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index a932f405e25..9242269906f 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -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); } }