mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Prevent dtor of generator in suspended fiber (#10462)
Generators that suspended a fiber should not be dtor because they will be executed during the fiber dtor. Fiber dtor throws an exception in the fiber's context in order to unwind and execute finally blocks, which will also properly dtor the generator. Fixes GH-9916
This commit is contained in:
27
Zend/tests/gh9916-001.phpt
Normal file
27
Zend/tests/gh9916-001.phpt
Normal file
@@ -0,0 +1,27 @@
|
||||
--TEST--
|
||||
Bug GH-9916 001 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$gen = (function() {
|
||||
$x = new stdClass;
|
||||
try {
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
print "Not executed";
|
||||
yield;
|
||||
} finally {
|
||||
print "Finally\n";
|
||||
}
|
||||
print "Not executed";
|
||||
})();
|
||||
$fiber = new Fiber(function() use ($gen, &$fiber) {
|
||||
$gen->current();
|
||||
print "Not executed";
|
||||
});
|
||||
$fiber->start();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before suspend
|
||||
==DONE==
|
||||
Finally
|
||||
21
Zend/tests/gh9916-002.phpt
Normal file
21
Zend/tests/gh9916-002.phpt
Normal file
@@ -0,0 +1,21 @@
|
||||
--TEST--
|
||||
Bug GH-9916 002 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$gen = (function() {
|
||||
$x = new stdClass;
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
print "Not executed\n";
|
||||
yield;
|
||||
})();
|
||||
$fiber = new Fiber(function() use ($gen, &$fiber) {
|
||||
$gen->current();
|
||||
print "Not executed";
|
||||
});
|
||||
$fiber->start();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before suspend
|
||||
==DONE==
|
||||
36
Zend/tests/gh9916-003.phpt
Normal file
36
Zend/tests/gh9916-003.phpt
Normal file
@@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
Bug GH-9916 003 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$gen = (function() {
|
||||
$x = new stdClass;
|
||||
try {
|
||||
yield from (function () {
|
||||
$x = new stdClass;
|
||||
try {
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
print "Not executed\n";
|
||||
yield;
|
||||
} finally {
|
||||
print "Finally (inner)\n";
|
||||
}
|
||||
})();
|
||||
print "Not executed\n";
|
||||
yield;
|
||||
} finally {
|
||||
print "Finally\n";
|
||||
}
|
||||
})();
|
||||
$fiber = new Fiber(function() use ($gen, &$fiber) {
|
||||
$gen->current();
|
||||
print "Not executed";
|
||||
});
|
||||
$fiber->start();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before suspend
|
||||
==DONE==
|
||||
Finally (inner)
|
||||
Finally
|
||||
26
Zend/tests/gh9916-004.phpt
Normal file
26
Zend/tests/gh9916-004.phpt
Normal file
@@ -0,0 +1,26 @@
|
||||
--TEST--
|
||||
Bug GH-9916 004 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$gen = (function() {
|
||||
$x = new stdClass;
|
||||
yield from (function () {
|
||||
$x = new stdClass;
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
print "Not executed\n";
|
||||
yield;
|
||||
})();
|
||||
print "Not executed\n";
|
||||
yield;
|
||||
})();
|
||||
$fiber = new Fiber(function() use ($gen, &$fiber) {
|
||||
$gen->current();
|
||||
print "Not executed";
|
||||
});
|
||||
$fiber->start();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before suspend
|
||||
==DONE==
|
||||
25
Zend/tests/gh9916-005.phpt
Normal file
25
Zend/tests/gh9916-005.phpt
Normal file
@@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
Bug GH-9916 005 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$gen = (function() {
|
||||
$x = new stdClass;
|
||||
$fiber = yield;
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
yield;
|
||||
})();
|
||||
$fiber = new Fiber(function() use ($gen, &$fiber) {
|
||||
$gen->send($fiber);
|
||||
$gen->current();
|
||||
});
|
||||
$fiber->start();
|
||||
|
||||
$gen = null;
|
||||
$fiber = null;
|
||||
gc_collect_cycles();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before suspend
|
||||
==DONE==
|
||||
37
Zend/tests/gh9916-006.phpt
Normal file
37
Zend/tests/gh9916-006.phpt
Normal file
@@ -0,0 +1,37 @@
|
||||
--TEST--
|
||||
Bug GH-9916 006 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$gen = (function() {
|
||||
$x = new stdClass;
|
||||
yield from (function () {
|
||||
$x = new stdClass;
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
print "After suspend\n";
|
||||
yield;
|
||||
})();
|
||||
yield from (function () {
|
||||
$x = new stdClass;
|
||||
print "Before exit\n";
|
||||
exit;
|
||||
print "Not executed\n";
|
||||
yield;
|
||||
})();
|
||||
yield;
|
||||
})();
|
||||
$fiber = new Fiber(function() use ($gen, &$fiber) {
|
||||
$gen->current();
|
||||
print "Fiber return\n";
|
||||
});
|
||||
$fiber->start();
|
||||
$fiber->resume();
|
||||
$gen->next();
|
||||
$gen->current();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before suspend
|
||||
After suspend
|
||||
Fiber return
|
||||
Before exit
|
||||
57
Zend/tests/gh9916-007.phpt
Normal file
57
Zend/tests/gh9916-007.phpt
Normal file
@@ -0,0 +1,57 @@
|
||||
--TEST--
|
||||
Bug GH-9916 007 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$it = new class implements Iterator
|
||||
{
|
||||
public function current(): mixed
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function key(): mixed
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
try {
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
print "Not executed\n";
|
||||
} finally {
|
||||
print "Finally (iterator)\n";
|
||||
}
|
||||
}
|
||||
|
||||
public function valid(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
$gen = (function() use ($it) {
|
||||
try {
|
||||
yield from $it;
|
||||
print "Not executed\n";
|
||||
} finally {
|
||||
print "Finally\n";
|
||||
}
|
||||
})();
|
||||
$fiber = new Fiber(function() use ($gen, &$fiber) {
|
||||
$gen->current();
|
||||
print "Not executed";
|
||||
});
|
||||
$fiber->start();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before suspend
|
||||
==DONE==
|
||||
Finally (iterator)
|
||||
Finally
|
||||
47
Zend/tests/gh9916-008.phpt
Normal file
47
Zend/tests/gh9916-008.phpt
Normal file
@@ -0,0 +1,47 @@
|
||||
--TEST--
|
||||
Bug GH-9916 008 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$it = new class implements Iterator
|
||||
{
|
||||
public function current(): mixed
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function key(): mixed
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$x = new stdClass;
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
}
|
||||
|
||||
public function valid(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
$gen = (function() use ($it) {
|
||||
$x = new stdClass;
|
||||
yield from $it;
|
||||
})();
|
||||
$fiber = new Fiber(function() use ($gen, &$fiber) {
|
||||
$gen->current();
|
||||
print "Not executed";
|
||||
});
|
||||
$fiber->start();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before suspend
|
||||
==DONE==
|
||||
35
Zend/tests/gh9916-009.phpt
Normal file
35
Zend/tests/gh9916-009.phpt
Normal file
@@ -0,0 +1,35 @@
|
||||
--TEST--
|
||||
Bug GH-9916 009 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$gen = (function() {
|
||||
$x = new stdClass;
|
||||
try {
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
print "Not executed\n";
|
||||
} finally {
|
||||
print "Finally\n";
|
||||
yield from ['foo' => new stdClass];
|
||||
print "Not executed\n";
|
||||
}
|
||||
})();
|
||||
$fiber = new Fiber(function() use ($gen, &$fiber) {
|
||||
$gen->current();
|
||||
print "Not executed\n";
|
||||
});
|
||||
$fiber->start();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECTF--
|
||||
Before suspend
|
||||
==DONE==
|
||||
Finally
|
||||
|
||||
Fatal error: Uncaught Error: Cannot use "yield from" in a force-closed generator in %s:%d
|
||||
Stack trace:
|
||||
#0 [internal function]: {closure}()
|
||||
#1 %s(%d): Generator->current()
|
||||
#2 [internal function]: {closure}()
|
||||
#3 {main}
|
||||
thrown in %s on line %d
|
||||
34
Zend/tests/gh9916-010.phpt
Normal file
34
Zend/tests/gh9916-010.phpt
Normal file
@@ -0,0 +1,34 @@
|
||||
--TEST--
|
||||
Bug GH-9916 010 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$gen = (function() {
|
||||
$x = new stdClass;
|
||||
print "Before yield\n";
|
||||
yield from (function () {
|
||||
$x = new stdClass;
|
||||
print "Before yield 2\n";
|
||||
yield;
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
})();
|
||||
})();
|
||||
|
||||
$fiber = new Fiber(function () use ($gen, &$fiber) {
|
||||
print "Before current\n";
|
||||
$gen->current();
|
||||
print "Before next\n";
|
||||
$gen->next();
|
||||
print "Not executed\n";
|
||||
});
|
||||
|
||||
$fiber->start();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before current
|
||||
Before yield
|
||||
Before yield 2
|
||||
Before next
|
||||
Before suspend
|
||||
==DONE==
|
||||
41
Zend/tests/gh9916-011.phpt
Normal file
41
Zend/tests/gh9916-011.phpt
Normal file
@@ -0,0 +1,41 @@
|
||||
--TEST--
|
||||
Bug GH-9916 011 (Entering shutdown sequence with a fiber suspended in a Generator emits an unavoidable fatal error or crashes)
|
||||
--FILE--
|
||||
<?php
|
||||
$gen = (function() {
|
||||
$x = new stdClass;
|
||||
print "Before yield\n";
|
||||
$from = (function () {
|
||||
$x = new stdClass;
|
||||
print "Before yield 2\n";
|
||||
yield;
|
||||
print "Before suspend\n";
|
||||
Fiber::suspend();
|
||||
print "Not executed\n";
|
||||
yield;
|
||||
})();
|
||||
try {
|
||||
yield from $from;
|
||||
} finally {
|
||||
$from->next();
|
||||
}
|
||||
})();
|
||||
|
||||
$fiber = new Fiber(function () use ($gen, &$fiber) {
|
||||
print "Before current\n";
|
||||
$gen->current();
|
||||
print "Before next\n";
|
||||
$gen->next();
|
||||
print "Not executed\n";
|
||||
});
|
||||
|
||||
$fiber->start();
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
Before current
|
||||
Before yield
|
||||
Before yield 2
|
||||
Before next
|
||||
Before suspend
|
||||
==DONE==
|
||||
@@ -223,6 +223,14 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
|
||||
uint32_t op_num, try_catch_offset;
|
||||
int i;
|
||||
|
||||
/* Generator is running in a suspended fiber.
|
||||
* Will be dtor during fiber dtor */
|
||||
if (generator->flags & ZEND_GENERATOR_IN_FIBER) {
|
||||
/* Prevent finally blocks from yielding */
|
||||
generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* leave yield from mode to properly allow finally execution */
|
||||
if (UNEXPECTED(Z_TYPE(generator->values) != IS_UNDEF)) {
|
||||
zval_ptr_dtor(&generator->values);
|
||||
@@ -727,7 +735,8 @@ try_again:
|
||||
}
|
||||
|
||||
/* Resume execution */
|
||||
generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING;
|
||||
generator->flags |= ZEND_GENERATOR_CURRENTLY_RUNNING
|
||||
| (EG(active_fiber) ? ZEND_GENERATOR_IN_FIBER : 0);
|
||||
if (!ZEND_OBSERVER_ENABLED) {
|
||||
zend_execute_ex(generator->execute_data);
|
||||
} else {
|
||||
@@ -778,6 +787,7 @@ try_again:
|
||||
goto try_again;
|
||||
}
|
||||
|
||||
generator->flags &= ~ZEND_GENERATOR_IN_FIBER;
|
||||
orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -92,6 +92,7 @@ static const zend_uchar ZEND_GENERATOR_CURRENTLY_RUNNING = 0x1;
|
||||
static const zend_uchar ZEND_GENERATOR_FORCED_CLOSE = 0x2;
|
||||
static const zend_uchar ZEND_GENERATOR_AT_FIRST_YIELD = 0x4;
|
||||
static const zend_uchar ZEND_GENERATOR_DO_INIT = 0x8;
|
||||
static const zend_uchar ZEND_GENERATOR_IN_FIBER = 0x10;
|
||||
|
||||
void zend_register_generator_ce(void);
|
||||
ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution);
|
||||
|
||||
Reference in New Issue
Block a user