Skip to content

Add the Async Scheduler Hook API: a thin, policy-free concurrency core - #13

Closed
EdmondDantes wants to merge 1 commit into
masterfrom
async-core
Closed

Add the Async Scheduler Hook API: a thin, policy-free concurrency core#13
EdmondDantes wants to merge 1 commit into
masterfrom
async-core

Conversation

@EdmondDantes

Copy link
Copy Markdown

The engine gains the ability to activate a concurrent execution mode: a scheduler (a C extension or a PHP library) registers a set of hooks through a single call and takes control of concurrency. The core contains no implementation — no scheduler, no queues, no event system; it is hook routing plus the minimal mechanism only the engine can perform.

RFC: https://github.com/true-async/php-async-core-rfc/blob/main/RFC.md
Integration reference: https://github.com/true-async/php-async-core-rfc/blob/main/SCHEDULER.md

Tested: Zend/tests/async (13 tests) plus the full fibers and gc suites — 194/194.

The engine gains the ability to activate a concurrent execution mode.
A scheduler - a C extension or a PHP library - registers a set of
hooks through a single call and takes control of concurrency. The core
contains NO implementation: no scheduler, no queues, no event system;
it is hook routing plus the minimal mechanism only the engine can
perform (context switching, the GC destructor walker, the coroutine
lifecycle status).
Core ABI (Zend/zend_async_API.h):
- zend_coroutine_t: lifecycle status packed into the flags word,
modifier flags, object_offset for the single-allocation embed
pattern (or a stored object pointer via OBJ_REF), an awaiting_info
diagnostics hook. No embedded wait state.
- scheduler slots registered once per process via a versioned struct:
new_coroutine, enqueue_coroutine, suspend(from_main, is_bailout),
resume, cancel, launch, shutdown, get_class_ce, call_on_main_stack,
context accessors (userland zval keys + internal numeric keys with
a core-owned key registry), intercept_fiber, gc_destructors, defer.
- zend_async_microtask_t: a thin one-shot task (handler, dtor, 32-bit
ref_count, named flag bits incl. is_cancelled). The queue is
provider-owned; the defer slot only routes the pointer.
- per-thread globals: state, current/main coroutine, live coroutine
count, the in_scheduler_context flag, the PHP bridge hook storage.
Engine integration:
- the scheduler is not lazy: it launches right before the script code
runs (main.c, phpdbg) and receives the after-main handover; after
destructors the API deactivates.
- fibers: intercept_fiber links a fiber to a coroutine (the hook
returns the coroutine to bind, created by the scheduler, or NULL
for the legacy low-level path). There is NO switching API: inside
scheduler code (in_scheduler_context, set around hook invocations)
the plain Fiber API on a bound fiber performs the direct context
switch; in application code the same calls park the value and
route through the hooks. Deferred starts take an owned deep copy
of their arguments. Fiber::suspend() is unchanged.
- GC: the destructor phase is an around-interceptor. The engine keeps
the walker, the once-per-object guarantee and the phase cursor, and
re-runs missed destructors after the hook as a safety net; the hook
brackets the run with provider logic (open a completion group, run,
await everything the destructors spawned, transitively). Each
destructor executes as application code (the scheduler flag is
dropped around it). The executor reaches the hook as a Closure over
an unregistered internal function - unreachable from application
code.
PHP bridge (Zend/zend_scheduler_hook.c): final class Async\
SchedulerHook - hook-name constants, register(module, hooks) (throws
when a scheduler is already registered), getModule(), defer()
(forwards to the DEFER hook). Hooks are stored by numeric index in the
per-thread globals; each slot is backed by a C thunk forwarding to the
stored callable.
Tests (Zend/tests/async, 13): registration semantics, constants,
launch-at-registration, managed-fiber lifecycle through a plain-Fiber
scheduler loop (start/suspend/resume/finish, throw at the suspension
point, uncaught exception propagation, after-main drain, direct-
inside/routed-outside context semantics), low-level fibers untouched
by a null intercept, microtask forwarding, and a GC cycle whose
destructors spawn managed fibers awaited by the hook. Full async,
fibers and gc suites pass (194/194).
RFC and integration reference:
https://github.com/true-async/php-async-core-rfc
@EdmondDantes
EdmondDantes requested a review from bukka as a code ownerJuly 3, 2026 08:20
EdmondDantes added a commit to true-async/php-async-core-rfc that referenced this pull request Jul 3, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@EdmondDantes