Skip to content

Commit

Permalink
Implemented Fiber GC handler
Browse files Browse the repository at this point in the history
  • Loading branch information
kooldev authored and nikic committed Jun 28, 2021
1 parent 64525b6 commit 7713302
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 15 deletions.
31 changes: 31 additions & 0 deletions Zend/tests/fibers/gc-cycle-callback.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
GC can cleanup cycle when callback references fiber
--FILE--
<?php

$ref = new class () {
public $fiber;

public function __destruct() {
var_dump('DTOR');
}
};

$fiber = new Fiber(function () use ($ref) {
die('UNREACHABLE');
});

$ref->fiber = $fiber;

$fiber = null;
$ref = null;

var_dump('COLLECT CYCLES');
gc_collect_cycles();
var_dump('DONE');

?>
--EXPECT--
string(14) "COLLECT CYCLES"
string(4) "DTOR"
string(4) "DONE"
42 changes: 42 additions & 0 deletions Zend/tests/fibers/gc-cycle-result.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
GC can cleanup cycle when fiber result references fiber
--FILE--
<?php

$fiber = null;
$fiber = new Fiber(function () use (&$fiber) {
return new class($fiber) {
private $fiber;

public function __construct($fiber) {
$this->fiber = $fiber;
}

public function __destruct() {
var_dump('DTOR');
}
};
});

$fiber->start();

var_dump('COLLECT CYCLES');
gc_collect_cycles();
var_dump('DONE');

var_dump($fiber->isTerminated());

unset($fiber);

var_dump('COLLECT CYCLES');
gc_collect_cycles();
var_dump('DONE');

?>
--EXPECT--
string(14) "COLLECT CYCLES"
string(4) "DONE"
bool(true)
string(14) "COLLECT CYCLES"
string(4) "DTOR"
string(4) "DONE"
34 changes: 19 additions & 15 deletions Zend/zend_fibers.c
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,9 @@ static ZEND_STACK_ALIGNED void zend_fiber_execute(zend_fiber_transfer *transfer)

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

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

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

static zend_object *zend_fiber_object_create(zend_class_entry *ce)
{
zend_fiber *fiber;
zend_fiber *fiber = emalloc(sizeof(zend_fiber));

fiber = emalloc(sizeof(zend_fiber));
memset(fiber, 0, sizeof(zend_fiber));

zend_object_std_init(&fiber->std, ce);
Expand Down Expand Up @@ -565,16 +566,25 @@ static void zend_fiber_object_free(zend_object *object)
{
zend_fiber *fiber = (zend_fiber *) object;

if (fiber->context.status == ZEND_FIBER_STATUS_INIT) {
// Fiber was never started, so we need to release the reference to the callback.
zval_ptr_dtor(&fiber->fci.function_name);
}

zval_ptr_dtor(&fiber->fci.function_name);
zval_ptr_dtor(&fiber->result);

zend_object_std_dtor(&fiber->std);
}

static HashTable *zend_fiber_object_gc(zend_object *object, zval **table, int *num)
{
zend_fiber *fiber = (zend_fiber *) object;
zend_get_gc_buffer *buf = zend_get_gc_buffer_create();

zend_get_gc_buffer_add_zval(buf, &fiber->fci.function_name);
zend_get_gc_buffer_add_zval(buf, &fiber->result);

zend_get_gc_buffer_use(buf, table, num);

return NULL;
}

ZEND_METHOD(Fiber, __construct)
{
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(ZEND_THIS);
Expand All @@ -590,12 +600,9 @@ ZEND_METHOD(Fiber, __construct)
ZEND_METHOD(Fiber, start)
{
zend_fiber *fiber = (zend_fiber *) Z_OBJ_P(ZEND_THIS);
zval *params;
uint32_t param_count;
zend_array *named_params;

ZEND_PARSE_PARAMETERS_START(0, -1)
Z_PARAM_VARIADIC_WITH_NAMED(params, param_count, named_params);
Z_PARAM_VARIADIC_WITH_NAMED(fiber->fci.params, fiber->fci.param_count, fiber->fci.named_params);
ZEND_PARSE_PARAMETERS_END();

if (UNEXPECTED(zend_fiber_switch_blocked())) {
Expand All @@ -608,10 +615,6 @@ ZEND_METHOD(Fiber, start)
RETURN_THROWS();
}

fiber->fci.params = params;
fiber->fci.param_count = param_count;
fiber->fci.named_params = named_params;

if (!zend_fiber_init_context(&fiber->context, zend_ce_fiber, zend_fiber_execute, EG(fiber_stack_size))) {
RETURN_THROWS();
}
Expand Down Expand Up @@ -827,6 +830,7 @@ void zend_register_fiber_ce(void)
zend_fiber_handlers = std_object_handlers;
zend_fiber_handlers.dtor_obj = zend_fiber_object_destroy;
zend_fiber_handlers.free_obj = zend_fiber_object_free;
zend_fiber_handlers.get_gc = zend_fiber_object_gc;
zend_fiber_handlers.clone_obj = NULL;

zend_ce_fiber_error = register_class_FiberError(zend_ce_error);
Expand Down

0 comments on commit 7713302

Please sign in to comment.