summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2018-01-13 11:04:26 +0100
committerNikita Popov <nikita.ppv@gmail.com>2018-01-13 11:04:26 +0100
commit8c07170ddbe28d8cc31e71f0ceb94fc4ac329657 (patch)
tree72ca8d2ee9b51139aa0f8fc6b48b1e4cb9062a97
parentcab0a814bdbfc653754f74b42056c38bdf4fbadb (diff)
downloadphp-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.phpt27
-rw-r--r--Zend/zend_generators.c14
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);
}
}