v0.7.0 — ThreadPool, request scope, stability hardening
First stable release since v0.6.7 — a large capability + hardening release that folds in the entire 0.7.0 alpha/beta/rc cycle. Headlines: a real OS-thread ThreadPool (per-worker bootloaders, coroutine-mode tasks, thread channels), request-level scope (Async\request_context()), opt-in PDO prepared-statement pooling (~2.9×), three-layer channel/pool deadlock protection, and a deep stability pass driven by a new randomized chaos test suite (now 100% public-API coverage). ABI bumped to v0.19.0.
This runtime powers the high-performance HTTP/1.1 · HTTP/2 · HTTP/3 application server true-async/server, released in lockstep as v0.7.2.
⚠️ Breaking changes
new Scope()now defaults to Not-Safe disposal. A fresh rootScopeno longer setsDISPOSE_SAFELY:dispose()cancels its coroutines synchronously instead of leaving zombies. Main scope andScope::inherit(...)chains are unchanged. Migration:(new Scope())->allowZombies()to keep the old behavior.- For API/reactor consumers: ABI 0.15 → 0.19 (unified thread-pool factory;
zend_async_io_registergainssendfile_fn/fs_open_fn;io_closedfield on IO/UDP reqs);TaskGroup/TaskSetseal()→close(),isSealed()→isClosed(); cross-thread transfer now rejects closures that declare classes/functions (file:line).
Added — multithreading
Async\ThreadPool— pool of OS threads for PHP closures:submit()/map()/close()(graceful)/cancel()(rejects backlog), counters,Countable;ThreadPoolExceptionwhen closed.- Workers auto-detect (
workers: 0→available_parallelism()), per-workerbootloaderhook, andcoroutine: truemode (each task is a coroutine in its own child scope — mayawait/use channels/IO).cancel()in coroutine mode actually kills in-flight tasks. Async\ThreadChannel— thread-safe channel via deep-copy snapshot; send/recv suspend the coroutine, not the OS thread (ThreadChannelException).- C-only
ThreadPool::submit_internal; cross-thread top-level zval transfer helpers.
Added — concurrency & pooling
- Request-level scope —
Async\request_context(): ?Context, O(1)ZEND_ASYNC_REQUEST_SCOPE(#105). This is what the true-async/serverHttpServerConfig::setRequestScope()knob builds on. - Channel deadlock protection (3 layers): per-channel
noProducerTimeout/noConsumerTimeout, global soft-timer resolver, owner-scope auto-close; typedAsync\ChannelCloseReason. - PDO Pool prepared-statement cache —
PDO::ATTR_POOL_STMT_CACHE_SIZE(pgsql/mysql/sqlite), per-conn LRU with transparent plan-invalidation retry; ~2.9× on a tight prepare+execute+fetch loop. - PDO_SQLite connection pool (
PDO::ATTR_POOL_ENABLED). TaskGroup/TaskSetqueueLimitbackpressure (default2 × concurrency).- Timer rearm / multishot API.
Added — I/O & introspection
- Async
sendfile(uv_fs_sendfile) and asyncopen(2). Async\available_parallelism()(respects cgroup quota/affinity); CPU probes —CpuSnapshot::now(),cpu_usage(),loadavg()(ZTS-safe;nullon Windows where N/A).
Changed
TaskGroup/TaskSetseal()→close()terminology.fuzzy_tests/→fuzzy-tests/.
Performance
- +32% RPS on a minimal HTTP handler — static TSRMLS cache (
ZEND_ENABLE_STATIC_TSRMLS_CACHE) turns everyEG()/ASYNC_G()into a single__threadload.
Fixed
Deep stability pass (full per-bug detail in CHANGELOG.md):
- Thread pool / cross-thread transfer — worker crash on
exit()/die()in task/bootloader (#154), bootloader-error swallowing (#154), cross-thread task UAF under cancel-vs-blocked-worker (#146),$this-bound closure SEGV, enum singleton identity, self-referential object cycles, type-info deep-copy (~12k→175k req/s under load). - Reactor / I/O — close-mid-read hangs parked reader (#144), concurrent writes to one file/descriptor losing data or corrupting heap on macOS/Windows (#129), writer not waking on peer reset, double-stop event underflow, Windows TCP accept.
- Channels / pools —
Channel(0)close-vs-send split-brain (#127) and missing rendezvous (#108), pool deadlock on broken-release (#141),recvAsync()/pool GC-cycle leaks. - curl —
curl_multi_selectcancel UAF (#145), async-read dangling subscription, callback exception leaks (#118). - JIT — tracing-JIT stale-FP spill SEGV in chaos fuzz (#118).
- Leaks — OpenSSL 3 per-thread RCU state, scope/timer/signal/composite-exception refcounts, OOM-bailout double-warning.
- Late
await()double-throw on a coroutine that finished with an exception (#139);Async\iteraterefcount UAF (#143); BSD/Darwin signal enum values;Async\signal()clobbering worker threads (#109).
Testing & CI
- New randomized chaos suite (
fuzzy-tests/): EvilPeer + Toxiproxy transport fault injection (#127/#129), mutation-block scenarios, 100% public-API coverage (167/167). Runs underTRUE_ASYNC_SCHED=random:{1,7,42,1337}per-PR on top of the deterministic FIFO run.
Ecosystem
- true-async/server — async HTTP/1.1 · HTTP/2 · HTTP/3 server built on this runtime (v0.7.2).
- Coordinated TrueAsync 0.7.0 release also tags
php-src,phpredis,xdebug, andfrankenphp; Docker images are published from true-async/releases.