mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2: [ci skip] NEWS Fix destruction of generator running in fibers during shutdown (#15158)
This commit is contained in:
commit
e24101acb4
9 changed files with 364 additions and 10 deletions
36
Zend/tests/gh15108-001.phpt
Normal file
36
Zend/tests/gh15108-001.phpt
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
--TEST--
|
||||||
|
GH-15108 001: Segfault with delegated generator in suspended fiber
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class It implements \IteratorAggregate
|
||||||
|
{
|
||||||
|
public function getIterator(): \Generator
|
||||||
|
{
|
||||||
|
yield 'foo';
|
||||||
|
Fiber::suspend();
|
||||||
|
var_dump("not executed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
yield from new It();
|
||||||
|
}
|
||||||
|
|
||||||
|
$iterable = f();
|
||||||
|
|
||||||
|
$fiber = new Fiber(function () use ($iterable) {
|
||||||
|
var_dump($iterable->current());
|
||||||
|
$iterable->next();
|
||||||
|
var_dump("not executed");
|
||||||
|
});
|
||||||
|
|
||||||
|
$ref = $fiber;
|
||||||
|
|
||||||
|
$fiber->start();
|
||||||
|
|
||||||
|
?>
|
||||||
|
==DONE==
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "foo"
|
||||||
|
==DONE==
|
40
Zend/tests/gh15108-002.phpt
Normal file
40
Zend/tests/gh15108-002.phpt
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
--TEST--
|
||||||
|
GH-15108 002: Segfault with delegated generator in suspended fiber
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class It implements \IteratorAggregate
|
||||||
|
{
|
||||||
|
public function getIterator(): \Generator
|
||||||
|
{
|
||||||
|
yield 'foo';
|
||||||
|
Fiber::suspend();
|
||||||
|
var_dump("not executed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() {
|
||||||
|
yield from new It();
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
yield from g();
|
||||||
|
}
|
||||||
|
|
||||||
|
$iterable = f();
|
||||||
|
|
||||||
|
$fiber = new Fiber(function () use ($iterable) {
|
||||||
|
var_dump($iterable->current());
|
||||||
|
$iterable->next();
|
||||||
|
var_dump("not executed");
|
||||||
|
});
|
||||||
|
|
||||||
|
$ref = $fiber;
|
||||||
|
|
||||||
|
$fiber->start();
|
||||||
|
|
||||||
|
?>
|
||||||
|
==DONE==
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "foo"
|
||||||
|
==DONE==
|
38
Zend/tests/gh15108-003.phpt
Normal file
38
Zend/tests/gh15108-003.phpt
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
--TEST--
|
||||||
|
GH-15108 003: Segfault with delegated generator in suspended fiber
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class It implements \IteratorAggregate
|
||||||
|
{
|
||||||
|
public function getIterator(): \Generator
|
||||||
|
{
|
||||||
|
yield 'foo';
|
||||||
|
Fiber::suspend();
|
||||||
|
var_dump("not executed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function f($gen) {
|
||||||
|
yield from $gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
$a = new It();
|
||||||
|
$b = f($a);
|
||||||
|
$c = f($a);
|
||||||
|
|
||||||
|
$fiber = new Fiber(function () use ($a, $b, $c) {
|
||||||
|
var_dump($b->current());
|
||||||
|
$b->next();
|
||||||
|
var_dump("not executed");
|
||||||
|
});
|
||||||
|
|
||||||
|
$ref = $fiber;
|
||||||
|
|
||||||
|
$fiber->start();
|
||||||
|
|
||||||
|
?>
|
||||||
|
==DONE==
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "foo"
|
||||||
|
==DONE==
|
39
Zend/tests/gh15108-004.phpt
Normal file
39
Zend/tests/gh15108-004.phpt
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
--TEST--
|
||||||
|
GH-15108 004: Segfault with delegated generator in suspended fiber
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
function gen1() {
|
||||||
|
yield 'foo';
|
||||||
|
Fiber::suspend();
|
||||||
|
var_dump("not executed");
|
||||||
|
};
|
||||||
|
|
||||||
|
function gen2($gen) {
|
||||||
|
yield from $gen;
|
||||||
|
var_dump("not executed");
|
||||||
|
}
|
||||||
|
|
||||||
|
$a = gen1();
|
||||||
|
/* Both $b and $c have a root marked with IN_FIBER, but only $b is actually
|
||||||
|
* running in a fiber (at shutdown) */
|
||||||
|
$b = gen2($a);
|
||||||
|
$c = gen2($a);
|
||||||
|
|
||||||
|
$fiber = new Fiber(function () use ($a, $b, $c) {
|
||||||
|
var_dump($b->current());
|
||||||
|
var_dump($c->current());
|
||||||
|
$b->next();
|
||||||
|
var_dump("not executed");
|
||||||
|
});
|
||||||
|
|
||||||
|
$ref = $fiber;
|
||||||
|
|
||||||
|
$fiber->start();
|
||||||
|
|
||||||
|
?>
|
||||||
|
==DONE==
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "foo"
|
||||||
|
string(3) "foo"
|
||||||
|
==DONE==
|
45
Zend/tests/gh15108-005.phpt
Normal file
45
Zend/tests/gh15108-005.phpt
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
--TEST--
|
||||||
|
GH-15108 005: Segfault with delegated generator in suspended fiber
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class It implements \IteratorAggregate
|
||||||
|
{
|
||||||
|
public function getIterator(): \Generator
|
||||||
|
{
|
||||||
|
yield 'foo';
|
||||||
|
Fiber::suspend();
|
||||||
|
var_dump("not executed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
yield from new It();
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() {
|
||||||
|
yield from f();
|
||||||
|
}
|
||||||
|
|
||||||
|
function h() {
|
||||||
|
/* g() is an intermediate node and will not be marked with IN_FIBER */
|
||||||
|
yield from g();
|
||||||
|
}
|
||||||
|
|
||||||
|
$iterable = h();
|
||||||
|
var_dump($iterable->current());
|
||||||
|
|
||||||
|
$fiber = new Fiber(function () use ($iterable) {
|
||||||
|
$iterable->next();
|
||||||
|
var_dump("not executed");
|
||||||
|
});
|
||||||
|
|
||||||
|
$ref = $fiber;
|
||||||
|
|
||||||
|
$fiber->start();
|
||||||
|
|
||||||
|
?>
|
||||||
|
==DONE==
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "foo"
|
||||||
|
==DONE==
|
49
Zend/tests/gh15108-006.phpt
Normal file
49
Zend/tests/gh15108-006.phpt
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
--TEST--
|
||||||
|
GH-15108 006: Segfault with delegated generator in suspended fiber
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class It implements \IteratorAggregate
|
||||||
|
{
|
||||||
|
public function getIterator(): \Generator
|
||||||
|
{
|
||||||
|
yield 'foo';
|
||||||
|
Fiber::suspend();
|
||||||
|
var_dump("not executed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
yield from new It();
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() {
|
||||||
|
yield from f();
|
||||||
|
}
|
||||||
|
|
||||||
|
function gen($gen) {
|
||||||
|
/* $gen is an intermediate node and will not be marked with IN_FIBER */
|
||||||
|
yield from $gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
$g = g();
|
||||||
|
$a = gen($g);
|
||||||
|
$b = gen($g);
|
||||||
|
var_dump($a->current());
|
||||||
|
var_dump($b->current());
|
||||||
|
|
||||||
|
$fiber = new Fiber(function () use ($a, $b, $g) {
|
||||||
|
$a->next();
|
||||||
|
var_dump("not executed");
|
||||||
|
});
|
||||||
|
|
||||||
|
$ref = $fiber;
|
||||||
|
|
||||||
|
$fiber->start();
|
||||||
|
|
||||||
|
?>
|
||||||
|
==DONE==
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "foo"
|
||||||
|
string(3) "foo"
|
||||||
|
==DONE==
|
51
Zend/tests/gh15108-007.phpt
Normal file
51
Zend/tests/gh15108-007.phpt
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
--TEST--
|
||||||
|
GH-15108 007: Segfault with delegated generator in suspended fiber
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class It implements \IteratorAggregate
|
||||||
|
{
|
||||||
|
public function getIterator(): \Generator
|
||||||
|
{
|
||||||
|
yield 'foo';
|
||||||
|
Fiber::suspend();
|
||||||
|
var_dump("not executed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
yield from new It();
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() {
|
||||||
|
yield from f();
|
||||||
|
}
|
||||||
|
|
||||||
|
function gen($gen) {
|
||||||
|
/* $gen is an intermediate node and will not be marked with IN_FIBER */
|
||||||
|
yield from $gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
$g = g();
|
||||||
|
$a = gen($g);
|
||||||
|
$b = gen($g);
|
||||||
|
$c = gen($g);
|
||||||
|
$d = gen($g);
|
||||||
|
var_dump($a->current());
|
||||||
|
var_dump($b->current());
|
||||||
|
|
||||||
|
$fiber = new Fiber(function () use ($a, $b, $c, $d, $g) {
|
||||||
|
$b->next();
|
||||||
|
var_dump("not executed");
|
||||||
|
});
|
||||||
|
|
||||||
|
$ref = $fiber;
|
||||||
|
|
||||||
|
$fiber->start();
|
||||||
|
|
||||||
|
?>
|
||||||
|
==DONE==
|
||||||
|
--EXPECT--
|
||||||
|
string(3) "foo"
|
||||||
|
string(3) "foo"
|
||||||
|
==DONE==
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "zend.h"
|
#include "zend.h"
|
||||||
#include "zend_API.h"
|
#include "zend_API.h"
|
||||||
|
#include "zend_hash.h"
|
||||||
#include "zend_interfaces.h"
|
#include "zend_interfaces.h"
|
||||||
#include "zend_exceptions.h"
|
#include "zend_exceptions.h"
|
||||||
#include "zend_generators.h"
|
#include "zend_generators.h"
|
||||||
|
@ -216,19 +217,68 @@ static zend_always_inline void clear_link_to_root(zend_generator *generator) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* In the context of zend_generator_dtor_storage during shutdown, check if
|
||||||
|
* the intermediate node 'generator' is running in a fiber */
|
||||||
|
static inline bool check_node_running_in_fiber(zend_generator *generator) {
|
||||||
|
ZEND_ASSERT(EG(flags) & EG_FLAGS_IN_SHUTDOWN);
|
||||||
|
ZEND_ASSERT(generator->execute_data);
|
||||||
|
|
||||||
|
if (generator->flags & ZEND_GENERATOR_IN_FIBER) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generator->node.children == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generator->flags & ZEND_GENERATOR_DTOR_VISITED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
generator->flags |= ZEND_GENERATOR_DTOR_VISITED;
|
||||||
|
|
||||||
|
if (generator->node.children == 1) {
|
||||||
|
if (check_node_running_in_fiber(generator->node.child.single)) {
|
||||||
|
goto in_fiber;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
zend_generator *child;
|
||||||
|
ZEND_HASH_FOREACH_PTR(generator->node.child.ht, child) {
|
||||||
|
if (check_node_running_in_fiber(child)) {
|
||||||
|
goto in_fiber;
|
||||||
|
}
|
||||||
|
} ZEND_HASH_FOREACH_END();
|
||||||
|
return false;
|
||||||
|
|
||||||
|
in_fiber:
|
||||||
|
generator->flags |= ZEND_GENERATOR_IN_FIBER;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
|
static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
|
||||||
{
|
{
|
||||||
zend_generator *generator = (zend_generator*) object;
|
zend_generator *generator = (zend_generator*) object;
|
||||||
|
zend_generator *current_generator = zend_generator_get_current(generator);
|
||||||
zend_execute_data *ex = generator->execute_data;
|
zend_execute_data *ex = generator->execute_data;
|
||||||
uint32_t op_num, try_catch_offset;
|
uint32_t op_num, try_catch_offset;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Generator is running in a suspended fiber.
|
/* If current_generator is running in a fiber, there are 2 cases to consider:
|
||||||
* Will be dtor during fiber dtor */
|
* - If generator is also marked with ZEND_GENERATOR_IN_FIBER, then the
|
||||||
if (zend_generator_get_current(generator)->flags & ZEND_GENERATOR_IN_FIBER) {
|
* entire path from current_generator to generator is executing in a
|
||||||
/* Prevent finally blocks from yielding */
|
* fiber. Do not dtor now: These will be dtor when terminating the fiber.
|
||||||
generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
|
* - If generator is not marked with ZEND_GENERATOR_IN_FIBER, and has a
|
||||||
return;
|
* child marked with ZEND_GENERATOR_IN_FIBER, then this an intermediate
|
||||||
|
* node of case 1. Otherwise generator is not executing in a fiber and we
|
||||||
|
* can dtor.
|
||||||
|
*/
|
||||||
|
if (current_generator->flags & ZEND_GENERATOR_IN_FIBER) {
|
||||||
|
if (check_node_running_in_fiber(generator)) {
|
||||||
|
/* Prevent finally blocks from yielding */
|
||||||
|
generator->flags |= ZEND_GENERATOR_FORCED_CLOSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* leave yield from mode to properly allow finally execution */
|
/* leave yield from mode to properly allow finally execution */
|
||||||
|
@ -718,6 +768,11 @@ try_again:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (EG(active_fiber)) {
|
||||||
|
orig_generator->flags |= ZEND_GENERATOR_IN_FIBER;
|
||||||
|
generator->flags |= ZEND_GENERATOR_IN_FIBER;
|
||||||
|
}
|
||||||
|
|
||||||
/* Drop the AT_FIRST_YIELD flag */
|
/* Drop the AT_FIRST_YIELD flag */
|
||||||
orig_generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
|
orig_generator->flags &= ~ZEND_GENERATOR_AT_FIRST_YIELD;
|
||||||
|
|
||||||
|
@ -748,7 +803,8 @@ try_again:
|
||||||
EG(current_execute_data) = original_execute_data;
|
EG(current_execute_data) = original_execute_data;
|
||||||
EG(jit_trace_num) = original_jit_trace_num;
|
EG(jit_trace_num) = original_jit_trace_num;
|
||||||
|
|
||||||
orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
|
orig_generator->flags &= ~(ZEND_GENERATOR_DO_INIT | ZEND_GENERATOR_IN_FIBER);
|
||||||
|
generator->flags &= ~ZEND_GENERATOR_IN_FIBER;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* If there are no more delegated values, resume the generator
|
/* If there are no more delegated values, resume the generator
|
||||||
|
@ -761,8 +817,7 @@ try_again:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resume execution */
|
/* 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) {
|
if (!ZEND_OBSERVER_ENABLED) {
|
||||||
zend_execute_ex(generator->execute_data);
|
zend_execute_ex(generator->execute_data);
|
||||||
} else {
|
} else {
|
||||||
|
@ -813,7 +868,7 @@ try_again:
|
||||||
goto try_again;
|
goto try_again;
|
||||||
}
|
}
|
||||||
|
|
||||||
orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
|
orig_generator->flags &= ~(ZEND_GENERATOR_DO_INIT | ZEND_GENERATOR_IN_FIBER);
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,7 @@ static const uint8_t ZEND_GENERATOR_FORCED_CLOSE = 0x2;
|
||||||
static const uint8_t ZEND_GENERATOR_AT_FIRST_YIELD = 0x4;
|
static const uint8_t ZEND_GENERATOR_AT_FIRST_YIELD = 0x4;
|
||||||
static const uint8_t ZEND_GENERATOR_DO_INIT = 0x8;
|
static const uint8_t ZEND_GENERATOR_DO_INIT = 0x8;
|
||||||
static const uint8_t ZEND_GENERATOR_IN_FIBER = 0x10;
|
static const uint8_t ZEND_GENERATOR_IN_FIBER = 0x10;
|
||||||
|
static const uint8_t ZEND_GENERATOR_DTOR_VISITED = 0x20;
|
||||||
|
|
||||||
void zend_register_generator_ce(void);
|
void zend_register_generator_ce(void);
|
||||||
ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution);
|
ZEND_API void zend_generator_close(zend_generator *generator, bool finished_execution);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue