Mayby, the better solution is just to do explicit "eager" initialisation of
threadvars, assuming the dev can insert code at all thread creation points.
This seems to work:
import locks
type
InitThreadLocalsProc* = proc(): void {.nimcall, gcsafe.}
## Type of a proc that initialises threadvars of some module in a new
Thread.
const MAX_INIT_PROCS = 100
## Maximum number of registered InitThreadLocalsProc.
var allInitThreadLocalsProcs: array[MAX_INIT_PROCS,InitThreadLocalsProc]
## All registered InitThreadLocalsProc
var registeredThreadLocalsProcs: int
## Number of registered InitThreadLocalsProc.
var used: bool
## Was some thread already initialised?
var arrayLock: Lock
## Lock to allow safe multi-threaded use.
initLock(arrayLock)
proc registerThreadLocalInitialiser*(init: InitThreadLocalsProc) =
## Registers a InitThreadLocalsProc.
## This cannot be called anymore, after the first call to
## runThreadLocalInitialisers().
if init.isNil:
raise newException(Exception, "init is nil!")
acquire(arrayLock)
try:
if used:
raise newException(Exception, "New Threads already initialised!")
if registeredThreadLocalsProcs == MAX_INIT_PROCS:
raise newException(Exception, "Too many ThreadLocal Initialisers!")
allInitThreadLocalsProcs[registeredThreadLocalsProcs] = init
inc registeredThreadLocalsProcs
finally:
release(arrayLock)
proc runThreadLocalInitialisers*() =
## Runs all InitThreadLocalsProcs.
## Only call this after all initialisers have been registered.
var initialisers: array[MAX_INIT_PROCS,InitThreadLocalsProc]
var count: int
acquire(arrayLock)
try:
used = true
initialisers = allInitThreadLocalsProcs
count = registeredThreadLocalsProcs
finally:
release(arrayLock)
for i in 0..<count:
initialisers[i]()
And a test:
import threadlocal
proc initA() {.gcsafe.} =
echo("Running initA() in thread ",getThreadId())
proc initB() {.gcsafe.} =
echo("Running initB() in thread ",getThreadId())
registerThreadLocalInitialiser(initA)
registerThreadLocalInitialiser(initB)
proc whatever() {.thread.} =
echo("In Thread ",getThreadId())
runThreadLocalInitialisers()
echo("Thread ",getThreadId(), " done")
var thread: Thread[void]
createThread[void](thread, whatever)
joinThread(thread)
echo("DONE")