Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
14af3cb
async: coroutine engine core, the reference scheduler and the corouti…
EdmondDantes Jul 16, 2026
bd18e37
async: keep the main coroutine alive until the loop is done with it
EdmondDantes Oct 1, 2026
45c834a
async: return a fiber body's value into its coroutine, not the Fiber …
EdmondDantes Oct 1, 2026
6111c75
async: shorten two comments
EdmondDantes Oct 1, 2026
5d5fe35
async: unwind a bailed-out request without allocating the loop's VM s…
EdmondDantes Oct 1, 2026
2b3f8e4
Merge remote-tracking branch 'upstream/master' into async-core
EdmondDantes Oct 1, 2026
9944e2d
async: version the scheduler API and refuse a provider built for another
EdmondDantes Oct 1, 2026
996a9bd
async: mark a coroutine started when its body begins
EdmondDantes Oct 2, 2026
5add2bc
async: read F_CANCELLED as a request, not as a final state
EdmondDantes Oct 2, 2026
c625aa4
fibers: keep an EH_THROW window with the fiber that opened it
EdmondDantes Oct 2, 2026
1d84327
async: refuse to enqueue a finished coroutine
EdmondDantes Oct 2, 2026
38eadbb
gc: start the GC coroutine without waiting where switching is blocked
EdmondDantes Oct 2, 2026
4c39d6e
gc: clear the GC coroutine pointers when a bailout cuts a run off
EdmondDantes Oct 2, 2026
101407b
fibers: survive a shutdown slot that clears the exit exception
EdmondDantes Oct 2, 2026
c267f46
async: clear the current and main coroutine when the request deactivates
EdmondDantes Oct 2, 2026
2b64c1f
async: document the parked frame, the await refusal and the flag bits
EdmondDantes Oct 2, 2026
5c47924
fibers: refuse Fiber methods while the scheduler runs its own work
EdmondDantes Oct 2, 2026
2aee763
fibers: call the switch observer only when something observes
EdmondDantes Oct 2, 2026
565f515
fibers: read the stack before a cleanup that may free the context
EdmondDantes Oct 2, 2026
2cb30e5
fibers: hold the object a class-string callable resolved to
EdmondDantes Oct 3, 2026
c43060e
shutdown: tell the scheduler of a bailout in a shutdown function
EdmondDantes Oct 3, 2026
28ef067
async: drop the active coroutine count nothing writes or reads
EdmondDantes Oct 3, 2026
56938ff
async: start the shutdown destructors iterator on shutdown_destructor…
EdmondDantes Oct 3, 2026
d76af5d
fibers: say that the fiber holds the coroutine, not the other way round
EdmondDantes Oct 3, 2026
c440e48
async: drop API surface no caller uses and the RFC does not define
EdmondDantes Oct 3, 2026
5e901ac
async: document that a finish handler fires at most once and a throwi…
EdmondDantes Oct 3, 2026
5e53ee2
async: drop new_coroutine's extra_size, which nothing passed or honored
EdmondDantes Oct 3, 2026
5d33958
test_scheduler: say why seven ported tests expect other output than t…
EdmondDantes Oct 3, 2026
3d155c1
test_scheduler: add a fault seam for new_coroutine and the first enqueue
EdmondDantes Oct 3, 2026
a88d007
test_scheduler: start the loop in suspend when nothing started it
EdmondDantes Oct 3, 2026
5e5fffa
async: clear the error of a shutdown iterator the scheduler refuses
EdmondDantes Oct 3, 2026
a67d22d
gc: handle a destructor iterator the scheduler refuses like one it co…
EdmondDantes Oct 3, 2026
cc9907b
async: say who owns the coroutine new_coroutine returns
EdmondDantes Oct 3, 2026
d7f3e1d
test_scheduler: test the core's paths where a coroutine cannot be cre…
EdmondDantes Oct 3, 2026
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
56 changes: 56 additions & 0 deletions Zend/tests/fibers/error-handling-window-per-fiber.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
--TEST--
An EH_THROW error handling window stays with the fiber that opened it
--FILE--
<?php
// SplFileObject::__construct() opens an EH_THROW window (warnings become
// RuntimeException) and calls the user wrapper inside it.
class SuspendingWrapper {
public $context;

public function url_stat(string $path, int $flags): array
{
return ['mode' => 0100644];
}

public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool
{
$started = new Fiber(function () {
trigger_error("in a fiber started inside the window", E_USER_WARNING);
});
$started->start();

Fiber::suspend();

try {
trigger_error("inside the window after resume", E_USER_WARNING);
} catch (RuntimeException $exception) {
echo "caught: ", $exception->getMessage(), "\n";
}

return false;
}
}

stream_wrapper_register('suspending', SuspendingWrapper::class);

$fiber = new Fiber(function () {
try {
new SplFileObject('suspending://file');
} catch (RuntimeException $exception) {
echo "constructor: ", get_class($exception), "\n";
}
});

$fiber->start();
trigger_error("in main while the fiber is inside the window", E_USER_WARNING);
$fiber->resume();
trigger_error("in main after the window closed", E_USER_WARNING);
?>
--EXPECTF--
Warning: in a fiber started inside the window in %s on line %d

Warning: in main while the fiber is inside the window in %s on line %d
caught: inside the window after resume
constructor: RuntimeException

Warning: in main after the window closed in %s on line %d
33 changes: 33 additions & 0 deletions Zend/tests/fibers/fiber-callable-object-outlives-caller.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
Fiber keeps the object a class-string callable resolved to: $this of the creating method outlives it
--FILE--
<?php
class Target {
public string $label = "kept";

public function method(): void {
echo "method: {$this->label}\n";
}

public function makeFiber(): Fiber {
return new Fiber([Target::class, 'method']);
}

public function __destruct() {
echo "released\n";
}
}

// The only reference to the Target is the creating call's $this.
$fiber = (new Target)->makeFiber();
$filler = array_fill(0, 100, new stdClass());
$fiber->start();
echo "started\n";
unset($fiber);
echo "done\n";
?>
--EXPECT--
method: kept
released
started
done
42 changes: 42 additions & 0 deletions Zend/tests/fibers/gc-generator-in-suspended-fiber.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
--TEST--
GC collects a cycle through a generator parked in Fiber::suspend() on a fiber stack
--FILE--
<?php

class C {
public function __destruct() {
echo __METHOD__, "\n";
}
}

function gen() {
$c = new C();
$self = Fiber::getCurrent(); // cycle: fiber -> frames -> $self -> fiber
yield 1;
Fiber::suspend("from gen");
yield 2;
}

$fiber = new Fiber(function () {
$g = gen();
$g->current();
$g->next(); // parks in Fiber::suspend() inside the generator
echo "unreachable\n";
});

$fiber->start();
print "1\n";

// Still referenced: nothing to collect.
gc_collect_cycles();
print "2\n";

$fiber = null;
gc_collect_cycles();
print "3\n";
?>
--EXPECT--
1
2
C::__destruct
3
9 changes: 8 additions & 1 deletion Zend/zend.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "zend_API.h"
#include "zend_exceptions.h"
#include "zend_builtin_functions.h"
#include "zend_async_API.h"
#include "zend_ini.h"
#include "zend_vm.h"
#include "zend_dtrace.h"
Expand Down Expand Up @@ -834,6 +835,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
executor_globals->current_fiber_context = NULL;
executor_globals->main_fiber_context = NULL;
executor_globals->active_fiber = NULL;
memset(&executor_globals->shutdown_context, 0, sizeof(executor_globals->shutdown_context));
#ifdef ZEND_WIN32
zend_get_windows_version_info(&executor_globals->windows_version_info);
#endif
Expand Down Expand Up @@ -1982,8 +1984,13 @@ ZEND_API zend_result zend_execute_script(int type, zval *retval, zend_file_handl
if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
zend_user_exception_handler();
}

if (EG(exception)) {
ret = zend_exception_error(EG(exception), E_ERROR);
if (ZEND_ASYNC_CURRENT_COROUTINE == NULL) {
ret = zend_exception_error(EG(exception), E_ERROR);
} else {
ret = FAILURE;
}
}
}
zend_destroy_static_vars(op_array);
Expand Down
Loading
Loading