mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-06-21 08:49:34 +00:00
fix: nph linting
This commit is contained in:
parent
e7c2be66f0
commit
3b3ce57330
@ -35,7 +35,6 @@ export cbor_serialization, options, results
|
||||
const CborNullByte*: byte = 0xf6'u8
|
||||
## CBOR encoding of `null` — used as the wire sentinel for empty OK payloads.
|
||||
|
||||
|
||||
proc cborEncode*[T](x: T): seq[byte] =
|
||||
## CBOR-encode any cbor_serialization-supported type (plus `pointer` / `ptr T`
|
||||
## via our custom writers) into a fresh `seq[byte]`.
|
||||
|
||||
@ -63,7 +63,6 @@ proc reqStructName(p: FFIProcMeta): string =
|
||||
else:
|
||||
camel & "Req"
|
||||
|
||||
|
||||
proc generateCargoToml*(libName: string): string =
|
||||
# `flume` is the unified callback channel (PR #23 Rust review, item 8): one
|
||||
# primitive that supports both `recv_timeout` (blocking trampoline) and
|
||||
|
||||
@ -47,8 +47,10 @@ proc emitLivenessEvent[T, P](ctx: ptr FFIContext[T], name: string, payload: P) =
|
||||
chronicles.error "liveness event encode failed", name = name, err = exc.msg
|
||||
return
|
||||
let dataPtr: pointer =
|
||||
if event.len > 0: cast[pointer](unsafeAddr event[0])
|
||||
else: cast[pointer](emptyListenerPayload)
|
||||
if event.len > 0:
|
||||
cast[pointer](unsafeAddr event[0])
|
||||
else:
|
||||
cast[pointer](emptyListenerPayload)
|
||||
ctx.dispatchToListeners(name, dataPtr, event.len)
|
||||
|
||||
proc onNotResponding*(ctx: ptr FFIContext) =
|
||||
@ -105,8 +107,7 @@ proc check[T](hb: var HeartbeatMonitor, ctx: ptr FFIContext[T]) =
|
||||
hb.lastValue = cur
|
||||
hb.lastChange = Moment.now()
|
||||
hb.notifiedStale = false
|
||||
elif not hb.notifiedStale and
|
||||
Moment.now() - hb.lastChange > FFIHeartbeatStaleThreshold:
|
||||
elif not hb.notifiedStale and Moment.now() - hb.lastChange > FFIHeartbeatStaleThreshold:
|
||||
onNotResponding(ctx)
|
||||
hb.notifiedStale = true
|
||||
|
||||
|
||||
@ -32,13 +32,16 @@ type FFIContext*[T] = object
|
||||
reqReceivedSignal: ThreadSignalPtr
|
||||
# to signal main thread, interfacing with the FFI thread, that FFI thread received the request
|
||||
stopSignal: ThreadSignalPtr
|
||||
threadExitSignal: ThreadSignalPtr # bounds destroyFFIContext's wait so a blocked loop cannot hang the caller
|
||||
eventQueueSignal: ThreadSignalPtr # wakes the event thread on enqueue (used once dispatch is rewired in PR #69)
|
||||
threadExitSignal: ThreadSignalPtr
|
||||
# bounds destroyFFIContext's wait so a blocked loop cannot hang the caller
|
||||
eventQueueSignal: ThreadSignalPtr
|
||||
# wakes the event thread on enqueue (used once dispatch is rewired in PR #69)
|
||||
eventThreadExitSignal: ThreadSignalPtr # mirrors threadExitSignal for the event thread
|
||||
userData*: pointer
|
||||
eventRegistry*: FFIEventRegistry
|
||||
eventQueue*: EventQueue
|
||||
ffiHeartbeat*: Atomic[int64] # advanced each FFI-thread loop; event thread reads for liveness
|
||||
ffiHeartbeat*: Atomic[int64]
|
||||
# advanced each FFI-thread loop; event thread reads for liveness
|
||||
running: Atomic[bool] # To control when the threads are running
|
||||
registeredRequests: ptr Table[cstring, FFIRequestProc]
|
||||
# Pointer to with the registered requests at compile time
|
||||
|
||||
@ -10,7 +10,6 @@ import std/[locks, sequtils, options, tables]
|
||||
import chronicles
|
||||
import ./ffi_types, ./cbor_serial
|
||||
|
||||
|
||||
type EventEnvelope*[T] = object
|
||||
## Standard wire shape for CBOR-encoded FFI events:
|
||||
## { eventType: tstr, payload: <T> }
|
||||
@ -18,7 +17,6 @@ type EventEnvelope*[T] = object
|
||||
eventType*: string
|
||||
payload*: T
|
||||
|
||||
|
||||
type
|
||||
FFIEventListener* = object
|
||||
id*: uint64
|
||||
@ -33,7 +31,6 @@ type
|
||||
nextId*: uint64 ## Monotonic id source. 0 is reserved as "invalid"; ids start at 1.
|
||||
byEvent*: Table[string, seq[FFIEventListener]]
|
||||
|
||||
|
||||
proc initEventRegistry*(reg: var FFIEventRegistry) =
|
||||
## Must be called exactly once on the owning thread before the registry
|
||||
## is shared. The embedded `Lock` wraps a platform primitive that cannot
|
||||
@ -129,7 +126,6 @@ proc snapshotListeners*(
|
||||
listeners.add(l)
|
||||
listeners
|
||||
|
||||
|
||||
const EventQueueCapacity* = 1024
|
||||
## ~24 KiB per context. Sustained backlog at this depth means a
|
||||
## listener is wedged — what the stuck flag exists to surface.
|
||||
@ -202,7 +198,6 @@ proc eventQueueLen*(q: var EventQueue): int {.raises: [], gcsafe.} =
|
||||
withLock q.lock:
|
||||
return q.count
|
||||
|
||||
|
||||
const emptyListenerPayload*: cstring = ""
|
||||
## Non-nil zero-length buffer handed to listeners when a payload is
|
||||
## empty, so a consumer doing `std::string(data, len)` / `memcpy` never
|
||||
@ -216,8 +211,10 @@ proc notifyListeners*(
|
||||
## consumer doing `std::string(data, len)` / `memcpy` never receives nil.
|
||||
let n = max(dataLen, 0)
|
||||
let dataPtr =
|
||||
if n > 0 and not data.isNil(): cast[ptr cchar](data)
|
||||
else: cast[ptr cchar](emptyListenerPayload)
|
||||
if n > 0 and not data.isNil():
|
||||
cast[ptr cchar](data)
|
||||
else:
|
||||
cast[ptr cchar](emptyListenerPayload)
|
||||
for listener in listeners:
|
||||
listener.callback(retCode, dataPtr, cast[csize_t](n), listener.userData)
|
||||
|
||||
@ -225,8 +222,10 @@ proc notifyListenersErr*(listeners: seq[FFIEventListener], msg: string) =
|
||||
## Error fan-out: adapts the message string to `notifyListeners`, which
|
||||
## supplies the non-nil pointer for the empty-message case.
|
||||
let p =
|
||||
if msg.len > 0: cast[pointer](unsafeAddr msg[0])
|
||||
else: cast[pointer](emptyListenerPayload)
|
||||
if msg.len > 0:
|
||||
cast[pointer](unsafeAddr msg[0])
|
||||
else:
|
||||
cast[pointer](emptyListenerPayload)
|
||||
notifyListeners(listeners, RET_ERR, p, msg.len)
|
||||
|
||||
var ffiCurrentEventRegistry* {.threadvar.}: ptr FFIEventRegistry
|
||||
@ -269,8 +268,10 @@ template dispatchFFIEvent*(eventName: string, body: untyped) =
|
||||
withFFIEventDispatch(eventName, listeners):
|
||||
let event = body
|
||||
let dataPtr: pointer =
|
||||
if event.len > 0: cast[pointer](unsafeAddr event[0])
|
||||
else: cast[pointer](emptyListenerPayload)
|
||||
if event.len > 0:
|
||||
cast[pointer](unsafeAddr event[0])
|
||||
else:
|
||||
cast[pointer](emptyListenerPayload)
|
||||
notifyListeners(listeners, RET_OK, dataPtr, event.len)
|
||||
|
||||
template dispatchFFIEventCbor*(eventName: string, eventPayload: typed) =
|
||||
|
||||
@ -7,7 +7,6 @@ when defined(ffiGenBindings):
|
||||
import ../codegen/cpp
|
||||
import ../codegen/cddl
|
||||
|
||||
|
||||
proc isPtr(typ: NimNode): bool =
|
||||
## True iff `typ` is a `ptr T` type expression — i.e. an `nnkPtrTy` AST node.
|
||||
## Used by the binding-generator metadata path to flag pointer-typed params
|
||||
@ -597,7 +596,6 @@ macro ffiRaw*(prc: untyped): untyped =
|
||||
echo stmts.repr
|
||||
return stmts
|
||||
|
||||
|
||||
macro ffi*(prc: untyped): untyped =
|
||||
## Simplified FFI macro — applies to procs or types.
|
||||
##
|
||||
@ -837,7 +835,6 @@ macro ffi*(prc: untyped): untyped =
|
||||
echo stmts.repr
|
||||
return stmts
|
||||
|
||||
|
||||
proc buildCtorRequestType(
|
||||
reqTypeName: NimNode, paramNames: seq[string], paramTypes: seq[NimNode]
|
||||
): NimNode =
|
||||
@ -1248,7 +1245,6 @@ macro ffiCtor*(prc: untyped): untyped =
|
||||
echo stmts.repr
|
||||
return stmts
|
||||
|
||||
|
||||
macro ffiDtor*(prc: untyped): untyped =
|
||||
## Defines a C-exported destructor that tears down the FFIContext after the
|
||||
## body runs.
|
||||
@ -1361,7 +1357,6 @@ macro ffiDtor*(prc: untyped): untyped =
|
||||
echo stmts.repr
|
||||
return stmts
|
||||
|
||||
|
||||
macro ffiEvent*(wireName: static[string], prc: untyped): untyped =
|
||||
## Declares a library-initiated event. The annotated proc has an empty
|
||||
## body — the macro fills it with a `dispatchFFIEventCbor` call so the
|
||||
@ -1449,7 +1444,6 @@ macro ffiEvent*(wireName: static[string], prc: untyped): untyped =
|
||||
echo generated.repr
|
||||
return generated
|
||||
|
||||
|
||||
macro genBindings*(
|
||||
outputDir: static[string] = ffiOutputDir, nimSrcRelPath: static[string] = ffiSrcPath
|
||||
): untyped =
|
||||
|
||||
@ -81,9 +81,18 @@ elseif(NIM_FFI_SANITIZER STREQUAL "tsan")
|
||||
"TSAN_OPTIONS=halt_on_error=1:second_deadlock_stack=1:history_size=7")
|
||||
endif()
|
||||
|
||||
# Discover at test time, not build time: a POST_BUILD discovery run launches the
|
||||
# freshly-linked exe while Windows Defender is still scanning it (and its staged
|
||||
# DLLs), which routinely overran the default 5s timeout on CI. PRE_TEST defers
|
||||
# enumeration to `ctest`, and the bumped timeout absorbs first-launch scan delays.
|
||||
include(GoogleTest)
|
||||
if(_san_test_env)
|
||||
gtest_discover_tests(timer_e2e_tests PROPERTIES ENVIRONMENT "${_san_test_env}")
|
||||
gtest_discover_tests(timer_e2e_tests
|
||||
DISCOVERY_MODE PRE_TEST
|
||||
DISCOVERY_TIMEOUT 120
|
||||
PROPERTIES ENVIRONMENT "${_san_test_env}")
|
||||
else()
|
||||
gtest_discover_tests(timer_e2e_tests)
|
||||
gtest_discover_tests(timer_e2e_tests
|
||||
DISCOVERY_MODE PRE_TEST
|
||||
DISCOVERY_TIMEOUT 120)
|
||||
endif()
|
||||
|
||||
@ -235,7 +235,6 @@ when not defined(gcRefc):
|
||||
# actually landed so a silently-broken dispatch loop is caught.
|
||||
check evt.called
|
||||
|
||||
|
||||
## A foreign-thread mutation must not be able to invalidate the
|
||||
## listener's `userData` while an in-flight dispatch is mid-invocation.
|
||||
## The dispatch templates hold `reg.lock` for the entire snapshot +
|
||||
@ -317,16 +316,14 @@ suite "liveness events":
|
||||
defer:
|
||||
deinitCallbackData(evt)
|
||||
|
||||
discard addEventListener(
|
||||
ctx[].eventRegistry, NotRespondingEventName, captureCb, addr evt
|
||||
)
|
||||
discard
|
||||
addEventListener(ctx[].eventRegistry, NotRespondingEventName, captureCb, addr evt)
|
||||
|
||||
onNotResponding(ctx)
|
||||
|
||||
waitCallback(evt)
|
||||
check evt.retCode == RET_OK
|
||||
let decoded =
|
||||
cborDecode(callbackBytes(evt), EventEnvelope[NotRespondingEvent])
|
||||
let decoded = cborDecode(callbackBytes(evt), EventEnvelope[NotRespondingEvent])
|
||||
check decoded.isOk()
|
||||
check decoded.value.eventType == NotRespondingEventName
|
||||
|
||||
@ -343,9 +340,8 @@ suite "liveness events":
|
||||
defer:
|
||||
deinitCallbackData(evt)
|
||||
|
||||
discard addEventListener(
|
||||
ctx[].eventRegistry, RespondingEventName, captureCb, addr evt
|
||||
)
|
||||
discard
|
||||
addEventListener(ctx[].eventRegistry, RespondingEventName, captureCb, addr evt)
|
||||
|
||||
onResponding(ctx)
|
||||
|
||||
@ -387,9 +383,7 @@ suite "event thread drains queued events":
|
||||
deinitCallbackData(evt)
|
||||
|
||||
const QueuedEvtName = "queued_evt"
|
||||
discard addEventListener(
|
||||
ctx[].eventRegistry, QueuedEvtName, captureCb, addr evt
|
||||
)
|
||||
discard addEventListener(ctx[].eventRegistry, QueuedEvtName, captureCb, addr evt)
|
||||
|
||||
# `tryEnqueueEvent` takes ownership of both buffers on success; the
|
||||
# event thread c_frees them after dispatch returns.
|
||||
|
||||
@ -48,7 +48,6 @@ proc tagCb(
|
||||
copyMem(addr payload[0], msg, int(len))
|
||||
record(t[].rec[], t[].name, retCode, payload)
|
||||
|
||||
|
||||
suite "FFIEventRegistry mutation":
|
||||
test "addEventListener assigns monotonically increasing non-zero ids":
|
||||
var reg: FFIEventRegistry
|
||||
|
||||
@ -324,7 +324,6 @@ suite "sendRequestToFFIThread":
|
||||
check d.retCode == RET_OK
|
||||
check cborDecode(callbackBytes(d), string).value == "pong:" & msg
|
||||
|
||||
|
||||
type SimpleLib = object
|
||||
value: int
|
||||
|
||||
@ -372,7 +371,6 @@ suite "ffiCtor macro":
|
||||
|
||||
check SimpleLibFFIPool.destroyFFIContext(ctx).isOk()
|
||||
|
||||
|
||||
type SendConfig {.ffi.} = object
|
||||
message: string
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user