From 32246792df0edfa068d242c686ffc872ac6138ac Mon Sep 17 00:00:00 2001 From: Ivan FB Date: Tue, 12 May 2026 01:54:32 +0200 Subject: [PATCH] restore back pool logic --- ffi/ffi_context.nim | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/ffi/ffi_context.nim b/ffi/ffi_context.nim index ca725fb..5492b8e 100644 --- a/ffi/ffi_context.nim +++ b/ffi/ffi_context.nim @@ -371,6 +371,31 @@ proc initContextResources*[T](ctx: ptr FFIContext[T]): Result[void, string] = success = true return ok() +const MaxFFIContexts* = 32 + ## Maximum number of concurrently live FFI contexts when using FFIContextPool. + ## Fds and threads are only consumed for slots that are actually acquired, + ## so this value only affects the upfront memory of the pool array. + +type FFIContextPool*[T] = object + ## Fixed-size pool of FFI contexts. Avoids dynamic heap allocation per context + ## and bounds the total number of file descriptors consumed by ThreadSignalPtrs + ## to at most MaxFFIContexts * 2. + slots: array[MaxFFIContexts, FFIContext[T]] + inUse: array[MaxFFIContexts, Atomic[bool]] + +proc acquireSlot[T](pool: var FFIContextPool[T]): Result[ptr FFIContext[T], string] = + for i in 0 ..< MaxFFIContexts: + var expected = false + if pool.inUse[i].compareExchange(expected, true): + return ok(pool.slots[i].addr) + return err("FFI context pool exhausted (max " & $MaxFFIContexts & " contexts)") + +proc releaseSlot[T](pool: var FFIContextPool[T], ctx: ptr FFIContext[T]) = + for i in 0 ..< MaxFFIContexts: + if pool.slots[i].addr == ctx: + pool.inUse[i].store(false) + return + # ── Public API ──────────────────────────────────────────────────────────────── proc createFFIContext*[T](): Result[ptr FFIContext[T], string] = @@ -382,6 +407,18 @@ proc createFFIContext*[T](): Result[ptr FFIContext[T], string] = return err(error) return ok(ctx) +proc createFFIContext*[T]( + pool: var FFIContextPool[T] +): Result[ptr FFIContext[T], string] = + ## Acquires a slot from the fixed pool and initialises it as an FFI context. + ## Bounded fd usage: at most MaxFFIContexts * 2 ThreadSignalPtr fds are ever open. + let ctx = pool.acquireSlot().valueOr: + return err(error) + initContextResources(ctx).isOkOr: + pool.releaseSlot(ctx) + return err(error) + return ok(ctx) + proc signalStop*[T](ctx: ptr FFIContext[T]): Result[void, string] = ctx.running.store(false) let ffiSignaled = ctx.reqSignal.fireSync().valueOr: