mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-05-17 15:49:27 +00:00
try to fix test crash on linux
This commit is contained in:
parent
be9f0ac1b1
commit
7e051e0eb6
@ -169,26 +169,12 @@ suite "destroyFFIContext does not hang when event loop is blocked":
|
|||||||
check false
|
check false
|
||||||
return
|
return
|
||||||
|
|
||||||
# NOTE on userData lifetime when destroyFFIContext returns err:
|
# CallbackData and ctx are kept alive past destroyFFIContext: the leaked
|
||||||
#
|
# FFI thread is still inside os.sleep(5_000) and will eventually wake,
|
||||||
# When the event loop is blocked, destroyFFIContext bails out after a
|
# run handleRes, fire testCallback, and exit normally. We wait for that
|
||||||
# bounded wait, returns err, and intentionally leaks ctx and the FFI
|
# to happen at the end of the test so the leaked thread cannot race with
|
||||||
# thread (see ffi_context.nim:destroyFFIContext). The thread is still
|
# subsequent tests' createFFIContext on Linux/Windows. Heap allocation
|
||||||
# alive inside the blocking section -- here, os.sleep(5_000) -- and will
|
# ensures the late callback's userData is still valid when it fires.
|
||||||
# eventually return, run handleRes, and invoke the per-request callback
|
|
||||||
# with the original userData pointer.
|
|
||||||
#
|
|
||||||
# That late callback fires *after* this test scope has already exited.
|
|
||||||
# If `d` were a stack variable with `defer: deinitCallbackData(d)`, its
|
|
||||||
# memory would be deinitialized and the stack frame reused before the
|
|
||||||
# callback runs -- the late call into testCallback would dereference
|
|
||||||
# garbage and segfault.
|
|
||||||
#
|
|
||||||
# Implicit contract surfaced by this test: callers of destroyFFIContext
|
|
||||||
# must keep `userData` for any in-flight request alive even after destroy
|
|
||||||
# returns err, because the FFI thread may still invoke the callback
|
|
||||||
# later. We honor that contract here by allocating on the shared heap
|
|
||||||
# and intentionally leaking, mirroring the leak of ctx itself.
|
|
||||||
let d = createShared(CallbackData)
|
let d = createShared(CallbackData)
|
||||||
initCallbackData(d[])
|
initCallbackData(d[])
|
||||||
|
|
||||||
@ -207,6 +193,22 @@ suite "destroyFFIContext does not hang when event loop is blocked":
|
|||||||
check destroyFFIContext(ctx).isErr()
|
check destroyFFIContext(ctx).isErr()
|
||||||
check (Moment.now() - t0) < 3.seconds
|
check (Moment.now() - t0) < 3.seconds
|
||||||
|
|
||||||
|
# Drain the leaked thread before the test scope ends.
|
||||||
|
# 1. waitCallback blocks until os.sleep(5_000) returns and handleRes
|
||||||
|
# invokes testCallback (~3.5s after destroy returned), which proves
|
||||||
|
# the leaked thread has reached the end of processRequest.
|
||||||
|
# 2. Yield briefly so the thread can finish iterating its while loop,
|
||||||
|
# fire threadExitSignal in its defer, and return. Without this, on
|
||||||
|
# Linux/Windows the still-live thread can race with the next test's
|
||||||
|
# createFFIContext under --mm:orc and segfault.
|
||||||
|
# ctx.cleanUpResources is intentionally NOT called: destroyFFIContext
|
||||||
|
# skipped it for a reason, and the signal fds are reclaimed by the OS
|
||||||
|
# at process exit.
|
||||||
|
waitCallback(d[])
|
||||||
|
os.sleep(200)
|
||||||
|
deinitCallbackData(d[])
|
||||||
|
freeShared(d)
|
||||||
|
|
||||||
suite "destroyFFIContext refc workaround":
|
suite "destroyFFIContext refc workaround":
|
||||||
## Documents the refc-specific workaround in cleanUpResources.
|
## Documents the refc-specific workaround in cleanUpResources.
|
||||||
##
|
##
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user