13 Commits

Author SHA1 Message Date
Gabriel Cruz
4d0e566cdf
chore: tidying up 2026-07-29 13:54:30 -03:00
Ivan FB
8f3d5cf8c2
fix: reject {.ffi.} calls on an unconstructed ref library
When no {.ffiCtor.} has stored a library, the FFI thread points `myLib` at a
default-valued fallback so handlers always have something to bind. For an
`object` library that is a usable zero value and callers legitimately depend on
it (tests/unit/test_ffi_handle drives a context that never runs a ctor), but for
a `ref` library the default is `nil`: the user body received a nil ref and
faulted on its first field access. A failing ctor is the common way to get
there, since the C entry point hands back a live context before the ctor body
has run on the FFI thread.

Track whether a ctor actually stored a library and, for `ref` library types
only, reject such requests with a clear error instead of dispatching them. The
check sits in the generated handler rather than the C entry point, so it runs
behind any queued constructor — a host that fires a call without awaiting the
create callback still succeeds, as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULz7Md52AF6PmqZeCmh8b7
2026-07-29 11:42:49 -03:00
Ivan FB
dda79fab34
feat: recycle pooled FFI contexts; compile-time request ids
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>
2026-07-29 11:42:49 -03:00
Gabriel Cruz
9ed1fedf96
chore: reduce walls of comments (#132) 2026-07-15 11:46:05 -03:00
Ivan FB
7b028e64c4
feat(ffi): RET_STALE_WARN progress callback replacing handler timeout (#129) 2026-07-14 17:07:33 +02:00
Gabriel Cruz
3e57751e3a
feat(ffi): async teardown hook for {.ffiDtor.} (#115) 2026-07-09 13:53:21 -03:00
Gabriel Cruz
8dcdcdc76a
feat(ffi): configurable per-request handler timeout with a finite default (#93) (#108) 2026-07-06 11:58:04 -03:00
Ivan FB
5d49ee6b08
feat(ffi): sharded MPSC request ingress (alternative to lock-free #98) (#101)
Co-authored-by: Gabriel Cruz <8129788+gmelodie@users.noreply.github.com>
2026-06-27 19:08:10 +02:00
Gabriel Cruz
64a332ca8b
feat(ffi): scalar type mappings + seq[byte] byte-string codec (#99) 2026-06-25 17:16:21 -03:00
Gabriel Cruz
021f469041
feat: {.ffiHandle.} — export complex live objects as opaque handles (#81) 2026-06-16 14:11:31 -03:00
Gabriel Cruz
16dc1b3573
chore(ci): add nph linting (#77) 2026-06-10 16:30:30 -03:00
Gabriel Cruz
9b3cde7674
feat: move user event code to a dedicated event thread (#69) 2026-06-09 11:35:04 -03:00
Gabriel Cruz
54c41a3e62
refactor(ffi): event thread scaffolding + FFIContext lifecycle split (#71) 2026-06-08 11:46:16 -03:00