mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-05-11 12:49:28 +00:00
introduce callback state into ffi context
This commit is contained in:
parent
729801b999
commit
5c31d2f313
@ -6,6 +6,12 @@ import std/[options, atomics, os, net, locks, json, tables]
|
||||
import chronicles, chronos, chronos/threadsync, taskpools/channels_spsc_single, results
|
||||
import ./ffi_types, ./ffi_thread_request, ./internal/ffi_macro, ./logging
|
||||
|
||||
type FFICallbackState* = object
|
||||
## Holds the C event callback and its associated user-data pointer.
|
||||
## Embedded in FFIContext and referenced from the FFI thread via a thread-local.
|
||||
callback*: pointer
|
||||
userData*: pointer
|
||||
|
||||
type FFIContext*[T] = object
|
||||
myLib*: ptr T
|
||||
# main library object (e.g., Waku, LibP2P, SDS, the one to be exposed as a library)
|
||||
@ -19,51 +25,54 @@ type FFIContext*[T] = object
|
||||
reqReceivedSignal: ThreadSignalPtr
|
||||
# to signal main thread, interfacing with the FFI thread, that FFI thread received the request
|
||||
userData*: pointer
|
||||
eventCallback*: pointer
|
||||
eventUserdata*: pointer
|
||||
callbackState*: FFICallbackState
|
||||
running: Atomic[bool] # To control when the threads are running
|
||||
registeredRequests: ptr Table[cstring, FFIRequestProc]
|
||||
# Pointer to with the registered requests at compile time
|
||||
|
||||
var ffiCurrentCallbackState* {.threadvar.}: ptr FFICallbackState
|
||||
## Set by ffiThreadBody at thread startup; read by dispatchFfiEvent.
|
||||
|
||||
const git_version* {.strdefine.} = "n/a"
|
||||
|
||||
template callEventCallback*(ctx: ptr FFIContext, eventName: string, body: untyped) =
|
||||
if isNil(ctx[].eventCallback):
|
||||
if isNil(ctx[].callbackState.callback):
|
||||
chronicles.error eventName & " - eventCallback is nil"
|
||||
return
|
||||
|
||||
foreignThreadGc:
|
||||
try:
|
||||
let event = body
|
||||
cast[FFICallBack](ctx[].eventCallback)(
|
||||
RET_OK, unsafeAddr event[0], cast[csize_t](len(event)), ctx[].eventUserData
|
||||
cast[FFICallBack](ctx[].callbackState.callback)(
|
||||
RET_OK, unsafeAddr event[0], cast[csize_t](len(event)), ctx[].callbackState.userData
|
||||
)
|
||||
except Exception, CatchableError:
|
||||
let msg =
|
||||
"Exception " & eventName & " when calling 'eventCallBack': " &
|
||||
getCurrentExceptionMsg()
|
||||
cast[FFICallBack](ctx[].eventCallback)(
|
||||
RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), ctx[].eventUserData
|
||||
cast[FFICallBack](ctx[].callbackState.callback)(
|
||||
RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), ctx[].callbackState.userData
|
||||
)
|
||||
|
||||
template dispatchFfiEvent*(w: typed, eventName: string, body: untyped) =
|
||||
## Fires an FFI event via the library object's event callback fields.
|
||||
## Works with any type that has ffiEventCallback and ffiEventUserData fields.
|
||||
if w.ffiEventCallback.isNil():
|
||||
chronicles.error eventName & " - ffiEventCallback is nil"
|
||||
template dispatchFfiEvent*(eventName: string, body: untyped) =
|
||||
## Dispatches an FFI event to the callback registered via `{libName}_set_event_callback`.
|
||||
## `body` is evaluated lazily — only when a callback is registered.
|
||||
## Valid only on the FFI thread (i.e., inside {.ffi.} proc bodies and their async closures).
|
||||
let ffiState = ffiCurrentCallbackState
|
||||
if isNil(ffiState) or isNil(ffiState[].callback):
|
||||
chronicles.error eventName & " - event callback not set"
|
||||
return
|
||||
foreignThreadGc:
|
||||
try:
|
||||
let event = body
|
||||
cast[FFICallBack](w.ffiEventCallback)(
|
||||
RET_OK, unsafeAddr event[0], cast[csize_t](len(event)), w.ffiEventUserData
|
||||
cast[FFICallBack](ffiState[].callback)(
|
||||
RET_OK, unsafeAddr event[0], cast[csize_t](len(event)), ffiState[].userData
|
||||
)
|
||||
except Exception, CatchableError:
|
||||
let msg =
|
||||
"Exception " & eventName & " when calling 'ffiEventCallback': " &
|
||||
getCurrentExceptionMsg()
|
||||
cast[FFICallBack](w.ffiEventCallback)(
|
||||
RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), w.ffiEventUserData
|
||||
"Exception dispatching " & eventName & ": " & getCurrentExceptionMsg()
|
||||
cast[FFICallBack](ffiState[].callback)(
|
||||
RET_ERR, unsafeAddr msg[0], cast[csize_t](len(msg)), ffiState[].userData
|
||||
)
|
||||
|
||||
proc sendRequestToFFIThread*(
|
||||
@ -196,6 +205,7 @@ proc processRequest[T](
|
||||
|
||||
proc ffiThreadBody[T](ctx: ptr FFIContext[T]) {.thread.} =
|
||||
## FFI thread body that attends library user API requests
|
||||
ffiCurrentCallbackState = addr ctx[].callbackState
|
||||
|
||||
logging.setupLog(logging.LogLevel.DEBUG, logging.LogFormat.TEXT)
|
||||
|
||||
|
||||
@ -93,13 +93,12 @@ macro declareLibraryBase(libraryName: static[string]): untyped =
|
||||
|
||||
macro declareLibrary*(libraryName: static[string], libType: untyped): untyped =
|
||||
## Declares a library with the given name and automatically generates
|
||||
## `{libraryName}_set_event_callback`, a C-exported function that lets callers
|
||||
## register an event callback on both the FFIContext and the library object.
|
||||
## `{libraryName}_set_event_callback`, a C-exported function that stores the
|
||||
## caller's event callback on the FFIContext.
|
||||
##
|
||||
## `libType` is the Nim type of the main library object (e.g. `Waku`). It is used
|
||||
## to type the `ctx: ptr FFIContext[libType]` parameter of the generated
|
||||
## `{libraryName}_set_event_callback` proc, and to conditionally propagate the
|
||||
## callback to `ctx.myLib[].ffiEventCallback` when that field exists on `libType`.
|
||||
## `{libraryName}_set_event_callback` proc.
|
||||
result = newStmtList()
|
||||
|
||||
# Emit the base bootstrap (pragmas, linker flags, NimMain, initializeLibrary)
|
||||
@ -117,12 +116,8 @@ macro declareLibrary*(libraryName: static[string], libType: untyped): untyped =
|
||||
if isNil(ctx):
|
||||
echo `errorMsg`
|
||||
return
|
||||
ctx[].eventCallback = cast[pointer](callback)
|
||||
ctx[].eventUserData = userData
|
||||
when compiles(ctx.myLib[].ffiEventCallback):
|
||||
if not isNil(ctx.myLib) and not isNil(ctx.myLib[]):
|
||||
ctx.myLib[].ffiEventCallback = cast[pointer](callback)
|
||||
ctx.myLib[].ffiEventUserData = userData
|
||||
ctx[].callbackState.callback = cast[pointer](callback)
|
||||
ctx[].callbackState.userData = userData
|
||||
|
||||
let procNode = newProc(
|
||||
name = funcIdent,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user