Skip to content

Commit 7713302

Browse files
kooldevnikic
authored andcommitted
Implemented Fiber GC handler
1 parent 64525b6 commit 7713302

File tree

3 files changed

+92
-15
lines changed

3 files changed

+92
-15
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
GC can cleanup cycle when callback references fiber
3+
--FILE--
4+
<?php
5+
6+
$ref = new class () {
7+
public $fiber;
8+
9+
public function __destruct() {
10+
var_dump('DTOR');
11+
}
12+
};
13+
14+
$fiber = new Fiber(function () use ($ref) {
15+
die('UNREACHABLE');
16+
});
17+
18+
$ref->fiber = $fiber;
19+
20+
$fiber = null;
21+
$ref = null;
22+
23+
var_dump('COLLECT CYCLES');
24+
gc_collect_cycles();
25+
var_dump('DONE');
26+
27+
?>
28+
--EXPECT--
29+
string(14) "COLLECT CYCLES"
30+
string(4) "DTOR"
31+
string(4) "DONE"
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
GC can cleanup cycle when fiber result references fiber
3+
--FILE--
4+
<?php
5+
6+
$fiber = null;
7+
$fiber = new Fiber(function () use (&$fiber) {
8+
return new class($fiber) {
9+
private $fiber;
10+
11+
public function __construct($fiber) {
12+
$this->fiber = $fiber;
13+
}
14+
15+
public function __destruct() {
16+
var_dump('DTOR');
17+
}
18+
};
19+
});
20+
21+
$fiber->start();
22+
23+
var_dump('COLLECT CYCLES');
24+
gc_collect_cycles();
25+
var_dump('DONE');
26+
27+
var_dump($fiber->isTerminated());
28+
29+
unset($fiber);
30+
31+
var_dump('COLLECT CYCLES');
32+
gc_collect_cycles();
33+
var_dump('DONE');
34+
35+
?>
36+
--EXPECT--
37+
string(14) "COLLECT CYCLES"
38+
string(4) "DONE"
39+
bool(true)
40+
string(14) "COLLECT CYCLES"
41+
string(4) "DTOR"
42+
string(4) "DONE"

Zend/zend_fibers.c

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,9 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)
425425

426426
zend_call_function(&fiber->fci, &fiber->fci_cache);
427427

428+
/* Cleanup callback and unset field to prevent GC / duplicate dtor issues. */
428429
zval_ptr_dtor(&fiber->fci.function_name);
430+
ZVAL_UNDEF(&fiber->fci.function_name);
429431

430432
if (EG(exception)) {
431433
if (!(fiber->flags & ZEND_FIBER_FLAG_DESTROYED)
@@ -516,9 +518,8 @@ static zend_always_inline zend_fiber_transfer zend_fiber_suspend(zend_fiber *fib
516518

517519
static zend_object *zend_fiber_object_create(zend_class_entry *ce)
518520
{
519-
zend_fiber *fiber;
521+
zend_fiber *fiber = emalloc(sizeof(zend_fiber));
520522

521-
fiber = emalloc(sizeof(zend_fiber));
522523
memset(fiber, 0, sizeof(zend_fiber));
523524

524525
zend_object_std_init(&fiber->std, ce);
@@ -565,16 +566,25 @@ static void zend_fiber_object_free(zend_object *object)
565566
{
566567
zend_fiber *fiber = (zend_fiber *) object;
567568

568-
if (fiber->context.status == ZEND_FIBER_STATUS_INIT) {
569-
// Fiber was never started, so we need to release the reference to the callback.
570-
zval_ptr_dtor(&fiber->fci.function_name);
571-
}
572-
569+
zval_ptr_dtor(&fiber->fci.function_name);
573570
zval_ptr_dtor(&fiber->result);
574571

575572
zend_object_std_dtor(&fiber->std);
576573
}
577574

575+
static HashTable *zend_fiber_object_gc(zend_object *object, zval **table, int *num)
576+
{
577+
zend_fiber *fiber = (zend_fiber *) object;
578+
zend_get_gc_buffer *buf = zend_get_gc_buffer_create();
579+
580+
zend_get_gc_buffer_add_zval(buf, &fiber->fci.function_name);
581+
zend_get_gc_buffer_add_zval(buf, &fiber->result);
582+
583+
zend_get_gc_buffer_use(buf, table, num);
584+
585+
return NULL;
586+
}
587+
578588
ZEND_METHOD(Fiber, __construct)
579589
{
580590
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(ZEND_THIS);
@@ -590,12 +600,9 @@ ZEND_METHOD(Fiber, __construct)
590600
ZEND_METHOD(Fiber, start)
591601
{
592602
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(ZEND_THIS);
593-
zval *params;
594-
uint32_t param_count;
595-
zend_array *named_params;
596603

597604
ZEND_PARSE_PARAMETERS_START(0, -1)
598-
Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params);
605+
Z_PARAM_VARIADIC_WITH_NAMED(fiber->fci.params, fiber->fci.param_count, fiber->fci.named_params);
599606
ZEND_PARSE_PARAMETERS_END();
600607

601608
if (UNEXPECTED(zend_fiber_switch_blocked())) {
@@ -608,10 +615,6 @@ ZEND_METHOD(Fiber, start)
608615
RETURN_THROWS();
609616
}
610617

611-
fiber->fci.params = params;
612-
fiber->fci.param_count = param_count;
613-
fiber->fci.named_params = named_params;
614-
615618
if (!zend_fiber_init_context(&fiber->context, zend_ce_fiber, zend_fiber_execute, EG(fiber_stack_size))) {
616619
RETURN_THROWS();
617620
}
@@ -827,6 +830,7 @@ void zend_register_fiber_ce(void)
827830
zend_fiber_handlers = std_object_handlers;
828831
zend_fiber_handlers.dtor_obj = zend_fiber_object_destroy;
829832
zend_fiber_handlers.free_obj = zend_fiber_object_free;
833+
zend_fiber_handlers.get_gc = zend_fiber_object_gc;
830834
zend_fiber_handlers.clone_obj = NULL;
831835

832836
zend_ce_fiber_error = register_class_FiberError(zend_ce_error);

0 commit comments

Comments
 (0)