diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 3f8387459..dea6fff1c 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -307,6 +307,35 @@ type ## a pointer as a thread ID. {.deprecated: [TThread: Thread, TThreadId: ThreadId].} +var + threadCreationHandlers: array[60, proc () {.nimcall, gcsafe.}] + countThreadCreationHandlers: int + +proc onThreadCreation*(handler: proc () {.nimcall, gcsafe.}) = + ## Registers a global handler that is called at thread creation. + ## This can be used to initialize thread local variables properly. + ## Note that the handler has to be .gcafe and so the typical usage + ## looks like: + ## + ## .. code-block:: nim + ## + ## var + ## someGlobal: string = "some string here" + ## perThread {.threadvar.}: string + ## + ## proc setPerThread() = + ## {.gcsafe.}: + ## deepCopy(perThread, someGlobal) + ## + ## **Note**: The registration is currently not threadsafe! Better + ## call ``onThreadCreation`` before any thread started its work! + threadCreationHandlers[countThreadCreationHandlers] = handler + inc countThreadCreationHandlers + +template beforeThreadRuns() = + for i in 0..countThreadCreationHandlers-1: + threadCreationHandlers[i]() + when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcstack): proc deallocOsPages() @@ -321,6 +350,7 @@ when defined(boehmgc): proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv.} = boehmGC_register_my_thread(sb) + beforeThreadRuns() let thrd = cast[ptr Thread[TArg]](thrd) when TArg is void: thrd.dataFn() @@ -329,6 +359,7 @@ when defined(boehmgc): boehmGC_unregister_my_thread() else: proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) = + beforeThreadRuns() when TArg is void: thrd.dataFn() else: diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index c1aebedfa..a7e0909c2 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -200,6 +200,7 @@ proc threadTests(r: var TResults, cat: Category, options: string) = test "tthreadanalysis2" #test "tthreadanalysis3" test "tthreadheapviolation1" + test "tonthreadcreation" # ------------------------- IO tests ------------------------------------------ diff --git a/tests/threads/tonthreadcreation.nim b/tests/threads/tonthreadcreation.nim new file mode 100644 index 000000000..c96e86d4d --- /dev/null +++ b/tests/threads/tonthreadcreation.nim @@ -0,0 +1,22 @@ +discard """ + output: '''some string here''' +""" + +var + someGlobal: string = "some string here" + perThread {.threadvar.}: string + +proc setPerThread() = + {.gcsafe.}: + deepCopy(perThread, someGlobal) + +proc foo() {.thread.} = + echo perThread + +proc main = + onThreadCreation setPerThread + var t: Thread[void] + createThread[void](t, foo) + t.joinThread() + +main()