fiber-interop

Conformance

Every adapter runs the same 22 examples from the api and must print identical output. Results are produced by CI from the current main of every repository, daily and on every push.

Summary

RuntimeLibrary / extension version testedPHPResultScope stateStressRun at
AMPHPadapter 9a8126c, amphp/amp v3.1.3, php 8.4.26, api 13cdbd08.4.26all 22 passfull6/62026-10-13 23:05 UTC
OpenSwoole (swoole adapter)adapter 7395594, ext-openswoole 25.2.0, php 8.4.26, api 13cdbd08.4.26all 22 passapi-driven6/62026-10-13 23:07 UTC
phasyncadapter 6f795cd, phasync/phasync 2.0.0-beta7, php 8.4.26, api 13cdbd08.4.26all 22 passfull6/62026-10-13 23:05 UTC
ReactPHPadapter 83b4fa8, react/async v4.3.0, react/event-loop v1.6.0, php 8.4.26, api 13cdbd08.4.26all 22 passapi-driven5/62026-10-13 23:06 UTC
Revoltadapter 7f86ebe, revolt/event-loop v1.0.9, php 8.4.26, api 13cdbd08.4.26all 22 passfull6/62026-10-13 23:05 UTC
Swooleadapter 7395594, ext-swoole 6.2.3, php 8.4.26, api 13cdbd08.4.26all 22 passapi-driven6/62026-10-13 23:06 UTC
reference adapter (api test double)adapter 13cdbd0, php 8.4.26, api 13cdbd08.4.26all 22 passnot run6/62026-10-13 23:05 UTC

All runs used the api at commit 13cdbd0.

Scope probes

Three standalone scripts per adapter show how far per-scope state (including superglobals) follows the code. api proves that code driven through the fiber-interop api keeps its scope; loop proves the same for code using the loop's own primitives; raw fiber proves that a plain Fiber keeps the scope of its creator.

A userland polyfill sees only the switches that pass through a hook it controls: everything under Revolt and AMPHP, callbacks but not await() under ReactPHP, nothing Swoole performs itself. Covering every switch needs the runtime: the engine for fibers (the case made in the the Scope RFC (draft)), and for Swoole the two switch hooks its core already has but does not expose to PHP. The ext-scope columns run the same probes with the native extension loaded. The extension provides the identity; per-scope superglobals are the RFC's secondary vote and are not in the extension yet, so api and loop fail there today and raw fiber passes. That is the measurement, not a defect of the extension.

Runtimeapiloopraw fiber
polyfillext-scopepolyfillext-scopepolyfillext-scope
AMPHPokFAILokFAILokok
OpenSwoole (swoole adapter)okFAILFAILFAILokok
phasyncokFAILokFAILokok
ReactPHPokFAILFAILFAILokok
RevoltokFAILokFAILokok
SwooleokFAILFAILFAILokok
reference adapter (api test double)not runnot runnot runnot runnot runnot run

Exceptions

Full matrix

Show the full matrix (examples by runtimes)
ExampleAMPHPOpenSwoole (swoole adapter)phasyncReactPHPRevoltSwoolereference
01-go-awaitgo() and await() return a task resultokokokokokokok
02-outside-run-throwsgo/await/sleep throw outside run(); run() worksokokokokokokok
03-sleep-interleavessleep() interleaves tasks by durationokokokokokokok
04-timeout-leaves-task-runningawait timeout throws; the task keeps runningokokokokokokok
05-late-result-ignoreda result arriving after a timeout is ignoredokokokokokokok
06-exception-rethrown-unchangedtask exceptions are rethrown unchangedokokokokokokok
07-rejected-non-throwablenon-Throwable rejection becomes RejectedExceptionokokokokokokok
08-await-thenableany then(ok, fail) object is awaitableokokokokokokok
09-await-allawait_all returns results under the same keysokokokokokokok
10-await-anyawait_any returns the first fulfilled resultokokokokokokok
11-readable-concurrentseveral fibers wait readable() on one streamokokokokokokok
12-writablewritable() waits and returns the resourceokokokokokokok
13-scope-inherited-by-gogo() tasks inherit the starter's Scopeokokokokokokok
14-scope-in-queued-callbackqueued callbacks resume tasks in their own Scopeokokokokokokok
15-in-place-foreign-fiberforeign fibers wait in place inside run()okokokokokokok
16-unhandled-exception-reaches-handlerunhandled service errors reach the error handlerokokokokokokok
17-await-is-repeatablean awaitable can be awaited repeatedlyokokokokokokok
18-run-is-structuredrun() waits for every task started inside itokokokokokokok
19-nested-runnested run() waits only for its own tasksokokokokokokok
20-service-does-not-block-runa service() task does not delay run()okokokokokokok
21-superglobals-per-scope$_GET is per scope; each scope sees its ownokokokokokokok
22-readiness-on-closereadable()/writable() return on peer close and on local close: readiness means the next operation will not blockokokokokokokok
Passing22/2222/2222/2222/2222/2222/2222/22

skipped means the runtime could not be run in that environment.

See the specification for what the conformance set does and does not prove.