Skip to content

Allow fiber switching in destructors #13460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Zend/tests/bug69446.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ unset($foo);
gc_collect_cycles();
var_dump($bar);
?>
--EXPECT--
object(bad)#2 (2) {
--EXPECTF--
object(bad)#%d (2) {
["x"]=>
object(stdClass)#3 (0) {
object(stdClass)#%d (0) {
}
["y"]=>
object(stdClass)#4 (0) {
object(stdClass)#%d (0) {
}
}
53 changes: 53 additions & 0 deletions Zend/tests/fibers/destructors_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
--TEST--
Fibers in destructors 001: Suspend in destructor
--FILE--
<?php

register_shutdown_function(function () {
printf("Shutdown\n");
});

class Cycle {
public static $counter = 0;
public $self;
public function __construct() {
$this->self = $this;
}
public function __destruct() {
$id = self::$counter++;
printf("%d: Start destruct\n", $id);
if ($id === 0) {
global $f2;
$f2 = Fiber::getCurrent();
Fiber::suspend(new stdClass);
}
printf("%d: End destruct\n", $id);
}
}

$f = new Fiber(function () {
global $f2;
new Cycle();
new Cycle();
new Cycle();
new Cycle();
new Cycle();
gc_collect_cycles();
$f2->resume();
});

$f->start();

?>
--EXPECT--
0: Start destruct
1: Start destruct
1: End destruct
2: Start destruct
2: End destruct
3: Start destruct
3: End destruct
4: Start destruct
4: End destruct
0: End destruct
Shutdown
35 changes: 35 additions & 0 deletions Zend/tests/fibers/destructors_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
--TEST--
Fibers in destructors 002: Start in destructor
--FILE--
<?php

register_shutdown_function(function () {
printf("Shutdown\n");
});

class Cycle {
public static $counter = 0;
public $self;
public function __construct() {
$this->self = $this;
}
public function __destruct() {
$id = self::$counter++;
printf("%d: Start destruct\n", $id);
$f = new Fiber(function () { });
$f->start();
printf("%d: End destruct\n", $id);
}
}

new Cycle();
new Cycle();
gc_collect_cycles();

?>
--EXPECT--
0: Start destruct
0: End destruct
1: Start destruct
1: End destruct
Shutdown
42 changes: 42 additions & 0 deletions Zend/tests/fibers/destructors_003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
Fibers in destructors 003: Resume in destructor
--FILE--
<?php

register_shutdown_function(function () {
printf("Shutdown\n");
});

class Cycle {
public static $counter = 0;
public $self;
public function __construct() {
$this->self = $this;
}
public function __destruct() {
$id = self::$counter++;
printf("%d: Start destruct\n", $id);
global $f;
$f->resume();
printf("%d: End destruct\n", $id);
}
}

$f = new Fiber(function () {
while (true) {
Fiber::suspend();
}
});
$f->start();

new Cycle();
new Cycle();
gc_collect_cycles();

?>
--EXPECT--
0: Start destruct
0: End destruct
1: Start destruct
1: End destruct
Shutdown
79 changes: 79 additions & 0 deletions Zend/tests/fibers/destructors_004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
--TEST--
Fibers in destructors 004: Suspend and throw in destructor
--FILE--
<?php

register_shutdown_function(function () {
printf("Shutdown\n");
});

class Cycle {
public static $counter = 0;
public $self;
public function __construct() {
$this->self = $this;
}
public function __destruct() {
$id = self::$counter++;
printf("%d: Start destruct\n", $id);
if ($id === 0) {
global $f2;
$f2 = Fiber::getCurrent();
Fiber::suspend(new stdClass);
}
printf("%d: End destruct\n", $id);
throw new \Exception(sprintf("%d exception", $id));
}
}

$f = new Fiber(function () {
global $f2;
new Cycle();
new Cycle();
new Cycle();
try {
gc_collect_cycles();
} catch (\Exception $e) {
echo $e, "\n";
}
$f2->resume();
});

$f->start();

?>
--EXPECTF--
0: Start destruct
1: Start destruct
1: End destruct
2: Start destruct
2: End destruct
Exception: 1 exception in %s:%d
Stack trace:
#0 [internal function]: Cycle->__destruct()
#1 [internal function]: gc_destructor_fiber()
#2 %s(%d): gc_collect_cycles()
#3 [internal function]: {closure:%s:%d}()
#4 %s(%d): Fiber->start()
#5 {main}

Next Exception: 2 exception in %s:%d
Stack trace:
#0 [internal function]: Cycle->__destruct()
#1 [internal function]: gc_destructor_fiber()
#2 %s(%d): gc_collect_cycles()
#3 [internal function]: {closure:%s:%d}()
#4 %s(%d): Fiber->start()
#5 {main}
0: End destruct

Fatal error: Uncaught Exception: 0 exception in %s:%d
Stack trace:
#0 [internal function]: Cycle->__destruct()
#1 [internal function]: gc_destructor_fiber()
#2 %s(%d): Fiber->resume()
#3 [internal function]: {closure:%s:%d}()
#4 %s(%d): Fiber->start()
#5 {main}
thrown in %s on line %d
Shutdown
60 changes: 60 additions & 0 deletions Zend/tests/fibers/destructors_005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
--TEST--
Fibers in destructors 005: Suspended and not resumed destructor
--FILE--
<?php

register_shutdown_function(function () {
printf("Shutdown\n");
});

class Cycle {
public $self;
public function __construct(public int $id) {
$this->self = $this;
}
public function __destruct() {
printf("%d: Start destruct\n", $this->id);
try {
if ($this->id === 0) {
/* Fiber will be collected by GC because it's not referenced */
Fiber::suspend(new stdClass);
} else if ($this->id === 1) {
/* Fiber will be dtor during shutdown */
global $f2;
$f2 = Fiber::getCurrent();
Fiber::suspend(new stdClass);
}
} finally {
printf("%d: End destruct\n", $this->id);
}
}
}

$refs = [];
$f = new Fiber(function () use (&$refs) {
$refs[] = WeakReference::create(new Cycle(0));
$refs[] = WeakReference::create(new Cycle(1));
$refs[] = WeakReference::create(new Cycle(2));
gc_collect_cycles();
});

$f->start();

gc_collect_cycles();

foreach ($refs as $id => $ref) {
printf("%d: %s\n", $id, $ref->get() ? 'Live' : 'Collected');
}

?>
--EXPECT--
2: Start destruct
2: End destruct
0: Start destruct
0: End destruct
1: Start destruct
0: Collected
1: Live
2: Collected
Shutdown
1: End destruct
52 changes: 52 additions & 0 deletions Zend/tests/fibers/destructors_006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--TEST--
Fibers in destructors 006: multiple GC runs
--FILE--
<?php

register_shutdown_function(function () {
printf("Shutdown\n");
});

class Cycle {
public static $counter = 0;
public $self;
public function __construct() {
$this->self = $this;
}
public function __destruct() {
$id = self::$counter++;
printf("%d: Start destruct\n", $id);
if ($id === 0) {
global $f2;
$f2 = Fiber::getCurrent();
Fiber::suspend(new stdClass);
}
printf("%d: End destruct\n", $id);
}
}

$f = new Fiber(function () {
new Cycle();
new Cycle();
gc_collect_cycles();
});

$f->start();

new Cycle();
new Cycle();
gc_collect_cycles();

$f2->resume();

?>
--EXPECT--
0: Start destruct
1: Start destruct
1: End destruct
2: Start destruct
2: End destruct
3: Start destruct
3: End destruct
0: End destruct
Shutdown
54 changes: 54 additions & 0 deletions Zend/tests/fibers/destructors_007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
--TEST--
Fibers in destructors 007: scope destructor
--FILE--
<?php

register_shutdown_function(function () {
printf("Shutdown\n");
});

class Cycle {
public static $counter = 0;
public function __destruct() {
$id = self::$counter++;
printf("%d: Start destruct\n", $id);
switch ($id) {
case 0:
global $f2;
$f2 = Fiber::getCurrent();
Fiber::suspend(new stdClass);
break;
case 1:
$f3 = new Fiber(function () use ($id) {
printf("%d: Fiber\n", $id);
});
$f3->start();
break;
case 2:
global $f2;
$f2->resume();
break;
}
printf("%d: End destruct\n", $id);
}
}

$f = new Fiber(function () {
new Cycle();
});

$f->start();

new Cycle();
new Cycle();

?>
--EXPECT--
0: Start destruct
1: Start destruct
1: Fiber
1: End destruct
2: Start destruct
0: End destruct
2: End destruct
Shutdown
Loading
Loading