Skip to content

v0.7.0 — ThreadPool, request scope, stability hardening

Choose a tag to compare

@EdmondDantes EdmondDantes released this 02 Jun 15:38
· 15 commits to main since this release

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 root Scope no longer sets DISPOSE_SAFELY: dispose() cancels its coroutines synchronously instead of leaving zombies. Main scope and Scope::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_register gains sendfile_fn/fs_open_fn; io_closed field on IO/UDP reqs); TaskGroup/TaskSet seal()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; ThreadPoolException when closed.
  • Workers auto-detect (workers: 0available_parallelism()), per-worker bootloader hook, and coroutine: true mode (each task is a coroutine in its own child scope — may await/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 scopeAsync\request_context(): ?Context, O(1) ZEND_ASYNC_REQUEST_SCOPE (#105). This is what the true-async/server HttpServerConfig::setRequestScope() knob builds on.
  • Channel deadlock protection (3 layers): per-channel noProducerTimeout/noConsumerTimeout, global soft-timer resolver, owner-scope auto-close; typed Async\ChannelCloseReason.
  • PDO Pool prepared-statement cachePDO::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/TaskSet queueLimit backpressure (default 2 × concurrency).
  • Timer rearm / multishot API.

Added — I/O & introspection

  • Async sendfile (uv_fs_sendfile) and async open(2).
  • Async\available_parallelism() (respects cgroup quota/affinity); CPU probesCpuSnapshot::now(), cpu_usage(), loadavg() (ZTS-safe; null on Windows where N/A).

Changed

  • TaskGroup/TaskSet seal()close() terminology.
  • fuzzy_tests/fuzzy-tests/.

Performance

  • +32% RPS on a minimal HTTP handler — static TSRMLS cache (ZEND_ENABLE_STATIC_TSRMLS_CACHE) turns every EG()/ASYNC_G() into a single __thread load.

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 / poolsChannel(0) close-vs-send split-brain (#127) and missing rendezvous (#108), pool deadlock on broken-release (#141), recvAsync()/pool GC-cycle leaks.
  • curlcurl_multi_select cancel 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\iterate refcount 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 under TRUE_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, and frankenphp; Docker images are published from true-async/releases.