Two foreign-host concurrency fixes for refc, both needed so a Go host can
hammer the FFI under load without corrupting Nim's per-thread GC.
1. Compile-time request ids. ffiNewReq / the method + ctor wrappers built
the request id with `$T` at runtime, allocating a Nim GC string on the
foreign caller's (often transient) thread. Emit a `cstring` literal of
the type name instead — no allocation on the caller thread.
2. Recycle pooled contexts instead of destroy/recreate. Restores the
release/v0.1 model that v0.2 dropped: a pool slot's worker + event
threads and signal fds are built once and reused. The ffiDtor now
requests a synchronous recycle (drain in-flight handlers, free the lib,
clear listeners, release the slot) on the FFI thread, keeping the
threads alive; createFFIContext reuses an initialised slot. Without this
every create/destroy churned ~6 signal fds, so fd numbers climbed past
FD_SETSIZE (1024) and ThreadSignalPtr.waitSync's select() failed with
EINVAL under create/destroy load.
Adds CtxLifecycle (Active/RecyclePending/Recycling), ctx-level
inUse/tryClaim/release/markAsActive, requestRecycle (waits on a new
recycleDoneSignal), freeLib (refc GC_unref / orc =destroy of ctor-owned
libs), recycleContext, FFIEventRegistry.clearListeners, and roots the
ctor-stored ref lib under refc (GC_ref, balanced in freeLib).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>