Skip to content
Merged
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
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