The pool's fixed array holds FFIContext objects, so "contexts" names what
it actually stores; "slot" emphasised array positions but the element type
already conveys that. Swept the "slot" vocabulary to "context" across the
pool, context, macro and test prose so the terminology is consistent with
the field name. Also corrected a stale doc reference — pool.releaseSlot
(no such proc on this branch) is now the real mechanism, ctx.unclaim().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The ffiDtor doc still claimed the generated destructor "calls
destroyFFIContext", but it now uses the recycle path. Replaced that line
with a concise note that the slot is recycled for reuse (bounding fd
usage) and that the call is non-blocking — RET_OK once accepted, real
outcome via callback. Switched the example placeholder from Waku to a
generic MyLibObj, and dropped the duplicate inline comment in the body.
Addresses PR #74 review comment r3363200458.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
clearContext had no call sites left after the pool refactor split teardown
into stopAndJoinThreads + cleanUpResources / destroyFFIContext. Remove it
and repoint the ThreadExitTimeout doc comment, which still named it, at
stopAndJoinThreads.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The reuse branch in createFFIContext only fires for a parked slot, and a
slot is only parked by releaseFFIContext. The generated destructor was
still calling destroyFFIContext (full teardown, marks the slot
uninitialised), so the reuse path never triggered and the fd-leak fix was
inert in the generated API.
Switch ffiDtor to releaseFFIContext so the worker and its fds survive the
destroy and get reused on the next create. This is safe because the
framework handles one request at a time: by the time the destructor runs
the worker is idle, not mid-request, so parking cannot race a handler.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
destroyFFIContext stopped and joined the worker threads on every release,
and createFFIContext rebuilt them on the next acquire. Each cycle therefore
allocated a fresh worker — 4 ThreadSignalPtr socketpairs + the ffi/watchdog
thread chronos dispatcher kqueues — and never reclaimed the old ones (the
pool deliberately skips closing them, relying on slot reuse that never
actually reused the resources). A consumer that creates and destroys
contexts repeatedly (e.g. nim-sds ReliabilityManager) leaked ~10 fds per
cycle, unbounded.
Make the pool genuinely reuse a slot's worker:
- Track per-slot `initialized`; createFFIContext builds the worker once and
reuses it on every later acquisition of the same slot.
- Add releaseFFIContext: parks a context (returns its slot) WITHOUT stopping
the threads, so the next acquire reuses the same fds. It also drops the
stale C event callback so a watchdog tick on a parked slot cannot invoke a
callback whose user-data the consumer may already have freed. The caller is
responsible for quiescing its library object (on the FFI thread) first.
- destroyFFIContext keeps full-teardown semantics for error/non-pooling
paths and now marks the slot uninitialised so a later acquire rebuilds it.
Tests: add a park & reuse suite (same-slot live-worker reuse, callback/lib
pointer dropped on park, and fd usage bounded across 20 park/reuse cycles).
The fd test fails by ~10 fds/cycle against the pre-fix behaviour. Green
under both --mm:refc and --mm:orc.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* protect against mem leak in case of failures sending requests to ffi thread
* better cleanup if failures in createFFIContext
* avoid dangling cstring in handleRes under ARC/ORC
* better resource cleanup in destroyFFIContext
* invoke onNotResponding if failure in destroyFFIContext
* correct seq copy in alloc
* make sure the lock is init before cleanUpResources
* better possible exception handling in processReq
* guard allocSharedSeq if given seq is empty
* enhance error handling in ffi_context
* add new tests and some corrections