diff --git a/lib/std/private/threadtypes.nim b/lib/std/private/threadtypes.nim index a1cdf21dc0f7..0caa12e3790d 100644 --- a/lib/std/private/threadtypes.nim +++ b/lib/std/private/threadtypes.nim @@ -1,4 +1,5 @@ include system/inclrtl +import std/atomics const hasSharedHeap* = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own @@ -161,16 +162,29 @@ type const hasAllocStack* = defined(zephyr) # maybe freertos too? -type - Thread*[TArg] = object - core*: PGcThread - sys*: SysThread - when TArg is void: - dataFn*: proc () {.nimcall, gcsafe.} - else: - dataFn*: proc (m: TArg) {.nimcall, gcsafe.} - data*: TArg - when hasAllocStack: - rawStack*: pointer +when not defined(cpp): + type + Thread*[TArg] = object + core*: Atomic[PGcThread] + sys*: SysThread + when TArg is void: + dataFn*: Atomic[proc () {.nimcall, gcsafe.}] + else: + dataFn*: Atomic[proc (m: TArg) {.nimcall, gcsafe.}] + data*: Atomic[TArg] + when hasAllocStack: + rawStack*: pointer +else: + type + Thread*[TArg] = object + core*: PGcThread + sys*: SysThread + when TArg is void: + dataFn*: proc () {.nimcall, gcsafe.} + else: + dataFn*: proc (m: TArg) {.nimcall, gcsafe.} + data*: TArg + when hasAllocStack: + rawStack*: pointer proc `=copy`*[TArg](x: var Thread[TArg], y: Thread[TArg]) {.error.} diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 494baa8abf9f..a0748377a5c6 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -75,7 +75,7 @@ deinitLock(l) ``` ]## - +import std/atomics import std/private/[threadtypes] export Thread @@ -149,9 +149,12 @@ else: nimThreadProcWrapperBody(closure) {.pop.} -proc running*[TArg](t: Thread[TArg]): bool {.inline.} = +proc running*[TArg](t: var Thread[TArg]): bool {.inline.} = ## Returns true if `t` is running. - result = t.dataFn != nil + when not defined(cpp): + result = t.dataFn.load(moAcquireRelease) != nil + else: + result = t.dataFn != nil proc handle*[TArg](t: Thread[TArg]): SysThread {.inline.} = ## Returns the thread handle of `t`. @@ -202,11 +205,20 @@ when false: else: discard pthread_cancel(t.sys) when declared(registerThread): unregisterThread(addr(t)) - t.dataFn = nil + when not defined(cpp): + t.dataFn.store(nil, moAcquireRelease) + else: + t.dataFn = nil ## if thread `t` already exited, `t.core` will be `null`. - if not isNil(t.core): - deallocThreadStorage(t.core) - t.core = nil + when not defined(cpp): + var coreTmp = t.core.load(moAcquireRelease) + if not isNil(coreTmp): + deallocThreadStorage(coreTmp) + t.core.store(nil, moAcquireRelease) + else: + if not isNil(t.core): + deallocThreadStorage(t.core) + t.core = nil when hostOS == "windows": proc createThread*[TArg](t: var Thread[TArg], @@ -217,11 +229,30 @@ when hostOS == "windows": ## Entry point is the proc `tp`. ## `param` is passed to `tp`. `TArg` can be `void` if you ## don't need to pass any data to the thread. - t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) + when not defined(cpp): + t.core.store(cast[PGcThread](allocThreadStorage(sizeof(GcThread))), moAcquireRelease) + else: + t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) + + when TArg isnot void: + when not defined(cpp): + t.data.store(param, moAcquireRelease) + else: + t.data = param + + when not defined(cpp): + t.dataFn.store(tp, moAcquireRelease) + else: + t.dataFn = tp + + when hasSharedHeap: + when not defined(cpp): + var core = cast[PGcThread](t.core.load(moAcquireRelease)) + core.stackSize = ThreadStackSize + t.core.store(core, moAcquireRelease) + else: + t.core.stackSize = ThreadStackSize - when TArg isnot void: t.data = param - t.dataFn = tp - when hasSharedHeap: t.core.stackSize = ThreadStackSize var dummyThreadId: int32 = 0'i32 t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg], addr(t), 0'i32, dummyThreadId) @@ -242,11 +273,30 @@ elif defined(genode): proc createThread*[TArg](t: var Thread[TArg], tp: proc (arg: TArg) {.thread, nimcall.}, param: TArg) = - t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) + when not defined(cpp): + t.core.store(cast[PGcThread](allocThreadStorage(sizeof(GcThread))), moAcquireRelease) + else: + t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) + + when TArg isnot void: + when not defined(cpp): + t.data.store(param, moAcquireRelease) + else: + t.data = param - when TArg isnot void: t.data = param - t.dataFn = tp - when hasSharedHeap: t.stackSize = ThreadStackSize + when not defined(cpp): + t.dataFn.store(tp, moAcquireRelease) + else: + t.dataFn = tp + + when hasSharedHeap: + when not defined(cpp): + var core = cast[PGcThread](t.core.load(moAcquireRelease)) + core.stackSize = ThreadStackSize + t.core.store(core, moAcquireRelease) + else: + t.core.stackSize = ThreadStackSize + t.sys.initThread( runtimeEnv, ThreadStackSize.culonglong, @@ -266,11 +316,30 @@ else: ## Entry point is the proc `tp`. `param` is passed to `tp`. ## `TArg` can be `void` if you ## don't need to pass any data to the thread. - t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) + when not defined(cpp): + t.core.store(cast[PGcThread](allocThreadStorage(sizeof(GcThread))), moAcquireRelease) + else: + t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) + + when TArg isnot void: + when not defined(cpp): + t.data.store(param, moAcquireRelease) + else: + t.data = param + + when not defined(cpp): + t.dataFn.store(tp, moAcquireRelease) + else: + t.dataFn = tp + + when hasSharedHeap: + when not defined(cpp): + var core = cast[PGcThread](t.core.load(moAcquireRelease)) + core.stackSize = ThreadStackSize + t.core.store(core, moAcquireRelease) + else: + t.core.stackSize = ThreadStackSize - when TArg isnot void: t.data = param - t.dataFn = tp - when hasSharedHeap: t.core.stackSize = ThreadStackSize var a {.noinit.}: Pthread_attr doAssert pthread_attr_init(a) == 0 when hasAllocStack: diff --git a/lib/system/threadimpl.nim b/lib/system/threadimpl.nim index 093a920a1d53..e429509f14bd 100644 --- a/lib/system/threadimpl.nim +++ b/lib/system/threadimpl.nim @@ -1,3 +1,5 @@ +import std/atomics + var nimThreadDestructionHandlers* {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}] when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions): @@ -50,10 +52,19 @@ when defined(boehmgc): boehmGC_register_my_thread(sb) try: let thrd = cast[ptr Thread[TArg]](thrd) + when not defined(cpp): + let dataFn = thrd.dataFn.load(moAcquireRelease) when TArg is void: - thrd.dataFn() + when not defined(cpp): + dataFn() + else: + thrd.dataFn() else: - thrd.dataFn(thrd.data) + when not defined(cpp): + let data = thrd.data.load(moAcquireRelease) + dataFn(data) + else: + thrd.dataFn(thrd.data) except: threadTrouble() finally: @@ -62,15 +73,28 @@ when defined(boehmgc): else: proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = try: + when not defined(cpp): + let dataFn = thrd.dataFn.load(moAcquireRelease) when TArg is void: - thrd.dataFn() + when not defined(cpp): + dataFn() + else: + thrd.dataFn() else: when defined(nimV2): - thrd.dataFn(thrd.data) + when not defined(cpp): + let data = thrd.data.load(moAcquireRelease) + dataFn(data) + else: + thrd.dataFn(thrd.data) else: var x: TArg = default(TArg) - deepCopy(x, thrd.data) - thrd.dataFn(x) + when not defined(cpp): + deepCopy(x, thrd.data.load(moAcquireRelease)) + dataFn(x) + else: + deepCopy(x, thrd.data) + thrd.dataFn(x) except: threadTrouble() finally: @@ -96,8 +120,16 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = template nimThreadProcWrapperBody*(closure: untyped): untyped = var thrd = cast[ptr Thread[TArg]](closure) - var core = thrd.core - when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core) + when not defined(cpp): + # Segfault after this line with --tlsEmulation:on + var core = cast[PGcThread](thrd.core.load(moAcquireRelease)) + else: + var core = thrd.core + when declared(globalsSlot): + when not defined(cpp): + threadVarSetValue(globalsSlot, cast[PGcThread](thrd.core.load(moAcquireRelease))) + else: + threadVarSetValue(globalsSlot, thrd.core) threadProcWrapStackFrame(thrd) # Since an unhandled exception terminates the whole process (!), there is # no need for a ``try finally`` here, nor would it be correct: The current @@ -106,6 +138,11 @@ template nimThreadProcWrapperBody*(closure: untyped): untyped = # page! # mark as not running anymore: - thrd.core = nil - thrd.dataFn = nil - deallocThreadStorage(cast[pointer](core)) + when not defined(cpp): + thrd.core.store(nil, moAcquireRelease) + thrd.dataFn.store(nil, moAcquireRelease) + deallocThreadStorage(cast[pointer](core)) + else: + thrd.core = nil + thrd.dataFn = nil + deallocThreadStorage(cast[pointer](core))