diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2018-01-13 11:04:26 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2018-01-13 11:04:26 +0100 |
commit | 8c07170ddbe28d8cc31e71f0ceb94fc4ac329657 (patch) | |
tree | 72ca8d2ee9b51139aa0f8fc6b48b1e4cb9062a97 | |
parent | cab0a814bdbfc653754f74b42056c38bdf4fbadb (diff) | |
download | php-git-8c07170ddbe28d8cc31e71f0ceb94fc4ac329657.tar.gz |
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.
-rw-r--r-- | Zend/tests/generators/gc_with_root_parent_mismatch.phpt | 27 | ||||
-rw-r--r-- | Zend/zend_generators.c | 14 |
2 files changed, 34 insertions, 7 deletions
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 0000000000..ee388b1c76 --- /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-- +<?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) diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index a932f405e2..9242269906 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); } } |