The branch carried a dated `[0.2.0] - 2026-06-04` release section and
`version = "0.2.0"` while the active release line is still 0.1.x, which
misrepresents unshipped work as a released version. Treat 0.1.4 as the latest
release and move everything above it back under `[Unreleased]`, merging the
0.2.0 bullets into the existing Added/Changed/Fixed groups. Set the package
version to 0.1.4 and realign the example `requires` (>= 0.2.0 -> >= 0.1.4) so
they stay satisfiable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The recycle/async-destroy work changed the Nim `ffiDtor` export from
`int destroy(ctx)` to `int destroy(ctx, callback, userData)`, but the C++
and Rust generators still emitted the 1-arg signature. Foreign callers
therefore passed only `ctx`; inside Nim, `callback`/`userData` held
uninitialised register garbage. `requestRecycle` stored the garbage
callback and the recycle handler later invoked it — a jump through a wild
pointer that segfaulted in every C++ E2E / ASan / TSan job (the crash
surfaced at teardown, after each test's assertions had already passed).
Generate the 3-arg ABI and have the destructor/Drop block on the recycle
callback via the existing sync-call helper, so the pool slot is fully
drained and parked before the handle goes away — otherwise rapid
create/destroy churn (StressShortLivedPerThreadContext, ThreadedHammer)
could outrun the recycle and exhaust the pool.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Call initializeLibrary() (setupForeignThreadGc) in the `.ffi.` request
wrapper and in add/remove_event_listener so a foreign (Go) caller thread
has an initialised Nim heap before any allocation ($reqTypeName /
$eventName / registry ops). Without it such a thread segfaults in the
allocator under GC pressure — the production unwrap SIGSEGV.
- recycleContext resets the event registry/queue + stuck flag on park so a
reused pool slot starts clean.
- ffiDtor doc/cleanup for the async recycle ABI.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>