From d555881f210d3b82375c3a211000f9311e196deb Mon Sep 17 00:00:00 2001 From: Michael Lenz <35996377+mk1nz@users.noreply.github.com> Date: Sat, 11 Jan 2025 13:45:54 +0400 Subject: [PATCH 1/7] Fixes for typedthreads data race issues Modifications have been tested with examples in the open issue. All seem to work except one with Thread[Socket] because of ref SocketImpl. Needs testing in Linux and Windows, probably better with some existing software that uses typedthreads to make sure I have not broken it. --- lib/std/private/threadtypes.nim | 9 +++++---- lib/std/typedthreads.nim | 22 +++++++++++----------- lib/system/threadimpl.nim | 23 ++++++++++++++--------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/lib/std/private/threadtypes.nim b/lib/std/private/threadtypes.nim index a1cdf21dc0f77..f380ec40d84cb 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 @@ -163,13 +164,13 @@ const hasAllocStack* = defined(zephyr) # maybe freertos too? type Thread*[TArg] = object - core*: PGcThread + core*: Atomic[PGcThread] sys*: SysThread when TArg is void: - dataFn*: proc () {.nimcall, gcsafe.} + dataFn*: Atomic[proc () {.nimcall, gcsafe.}] else: - dataFn*: proc (m: TArg) {.nimcall, gcsafe.} - data*: TArg + dataFn*: Atomic[proc (m: TArg) {.nimcall, gcsafe.}] + data*: Atomic[TArg] when hasAllocStack: rawStack*: pointer diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 494baa8abf9f3..e1735ebe2ff87 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,9 @@ 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 + result = t.dataFn.load() != nil proc handle*[TArg](t: Thread[TArg]): SysThread {.inline.} = ## Returns the thread handle of `t`. @@ -202,7 +202,7 @@ when false: else: discard pthread_cancel(t.sys) when declared(registerThread): unregisterThread(addr(t)) - t.dataFn = nil + t.dataFn.store nil ## if thread `t` already exited, `t.core` will be `null`. if not isNil(t.core): deallocThreadStorage(t.core) @@ -219,8 +219,8 @@ when hostOS == "windows": ## don't need to pass any data to the thread. t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) - when TArg isnot void: t.data = param - t.dataFn = tp + when TArg isnot void: t.data.store param + t.dataFn.store tp when hasSharedHeap: t.core.stackSize = ThreadStackSize var dummyThreadId: int32 = 0'i32 t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg], @@ -244,8 +244,8 @@ elif defined(genode): param: TArg) = t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread))) - when TArg isnot void: t.data = param - t.dataFn = tp + when TArg isnot void: t.data.store param + t.dataFn.store tp when hasSharedHeap: t.stackSize = ThreadStackSize t.sys.initThread( runtimeEnv, @@ -266,10 +266,10 @@ 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))) + t.core.store cast[PGcThread](allocThreadStorage(sizeof(GcThread))) - when TArg isnot void: t.data = param - t.dataFn = tp + when TArg isnot void: t.data.store param + t.dataFn.store tp when hasSharedHeap: t.core.stackSize = ThreadStackSize var a {.noinit.}: Pthread_attr doAssert pthread_attr_init(a) == 0 diff --git a/lib/system/threadimpl.nim b/lib/system/threadimpl.nim index 093a920a1d536..80651919ac322 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,11 @@ when defined(boehmgc): boehmGC_register_my_thread(sb) try: let thrd = cast[ptr Thread[TArg]](thrd) + let dataFn = thrd.dataFn.load() when TArg is void: - thrd.dataFn() + dataFn() else: - thrd.dataFn(thrd.data) + dataFn(thrd.data) except: threadTrouble() finally: @@ -62,15 +65,16 @@ when defined(boehmgc): else: proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = try: + let dataFn = thrd.dataFn.load() when TArg is void: - thrd.dataFn() + dataFn() else: when defined(nimV2): - thrd.dataFn(thrd.data) + dataFn(thrd.data.load()) else: var x: TArg = default(TArg) - deepCopy(x, thrd.data) - thrd.dataFn(x) + deepCopy(x, thrd.data.load()) + dataFn(x) except: threadTrouble() finally: @@ -95,7 +99,8 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = threadProcWrapDispatch(thrd) template nimThreadProcWrapperBody*(closure: untyped): untyped = - var thrd = cast[ptr Thread[TArg]](closure) + var thr = cast[Atomic[ptr Thread[TArg]]](closure) + var thrd = thr.load() var core = thrd.core when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core) threadProcWrapStackFrame(thrd) @@ -106,6 +111,6 @@ template nimThreadProcWrapperBody*(closure: untyped): untyped = # page! # mark as not running anymore: - thrd.core = nil - thrd.dataFn = nil + thrd.core.store nil + thrd.dataFn.store nil deallocThreadStorage(cast[pointer](core)) From 268622983bdc56cccf18beb25cb4de3223070618 Mon Sep 17 00:00:00 2001 From: Michael Lenz <35996377+mk1nz@users.noreply.github.com> Date: Sat, 11 Jan 2025 16:17:04 +0400 Subject: [PATCH 2/7] Update typedthreads.nim --- lib/std/typedthreads.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index e1735ebe2ff87..08526aecded7b 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -217,7 +217,7 @@ 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))) + t.core.store cast[PGcThread](allocThreadStorage(sizeof(GcThread))) when TArg isnot void: t.data.store param t.dataFn.store tp @@ -242,7 +242,7 @@ 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))) + t.core.store cast[PGcThread](allocThreadStorage(sizeof(GcThread))) when TArg isnot void: t.data.store param t.dataFn.store tp From d0b1070db2c34431a846730afc1404d310b81eea Mon Sep 17 00:00:00 2001 From: Michael Lenz <35996377+mk1nz@users.noreply.github.com> Date: Sun, 12 Jan 2025 14:18:01 +0400 Subject: [PATCH 3/7] fixed type mismatch errors to work with Boehm --- lib/std/typedthreads.nim | 5 ++++- lib/system/threadimpl.nim | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 08526aecded7b..e57edcfdbbdb0 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -270,7 +270,10 @@ else: when TArg isnot void: t.data.store param t.dataFn.store tp - when hasSharedHeap: t.core.stackSize = ThreadStackSize + when hasSharedHeap: + var stackSize: PGcThread = t.core.load() + stacksize.stackSize = ThreadStackSize + t.core.store stacksize 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 80651919ac322..e20f8e68bcd9d 100644 --- a/lib/system/threadimpl.nim +++ b/lib/system/threadimpl.nim @@ -56,7 +56,7 @@ when defined(boehmgc): when TArg is void: dataFn() else: - dataFn(thrd.data) + dataFn(thrd.data.load()) except: threadTrouble() finally: @@ -102,7 +102,7 @@ template nimThreadProcWrapperBody*(closure: untyped): untyped = var thr = cast[Atomic[ptr Thread[TArg]]](closure) var thrd = thr.load() var core = thrd.core - when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core) + when declared(globalsSlot): threadVarSetValue(globalsSlot, core.load()) 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 From 621c77e5316e7b6435b8def9897d38fa55dc92dd Mon Sep 17 00:00:00 2001 From: Michael Lenz <35996377+mk1nz@users.noreply.github.com> Date: Sun, 12 Jan 2025 21:36:21 +0400 Subject: [PATCH 4/7] Update threadimpl.nim --- lib/system/threadimpl.nim | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/system/threadimpl.nim b/lib/system/threadimpl.nim index e20f8e68bcd9d..0becceee3f886 100644 --- a/lib/system/threadimpl.nim +++ b/lib/system/threadimpl.nim @@ -99,18 +99,17 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = threadProcWrapDispatch(thrd) template nimThreadProcWrapperBody*(closure: untyped): untyped = - var thr = cast[Atomic[ptr Thread[TArg]]](closure) - var thrd = thr.load() - var core = thrd.core - when declared(globalsSlot): threadVarSetValue(globalsSlot, core.load()) + var thrd = cast[ptr Thread[TArg]](closure) + var core = thrd.core.load() + when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core.load()) 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 - # exception is tried to be re-raised by the code-gen after the ``finally``! + # no need for a `try finally` here, nor would it be correct: The current + # exception is tried to be re-raised by the code-gen after the `finally`! # However this is doomed to fail, because we already unmapped every heap # page! # mark as not running anymore: thrd.core.store nil thrd.dataFn.store nil - deallocThreadStorage(cast[pointer](core)) + deallocThreadStorage(core) From 5d21a3199d5c21321399ec962d5c4f7acbd0a32f Mon Sep 17 00:00:00 2001 From: Michael Lenz <35996377+mk1nz@users.noreply.github.com> Date: Sat, 18 Jan 2025 20:12:56 +0400 Subject: [PATCH 5/7] Update For the cpp target, the old flow without atomics is retained. With --tlsEmulation:on enabled, GC calls GetThreadLocalVars() before it calls initThreadVarsEmulation from threadlocalstorage.nim, causing a segfault. This should be fixed to make this PR pass tests. --- lib/std/private/threadtypes.nim | 35 ++++++--- lib/std/typedthreads.nim | 121 +++++++++++++++++++++++++------- lib/system/threadimpl.nim | 63 +++++++++++++---- 3 files changed, 168 insertions(+), 51 deletions(-) diff --git a/lib/std/private/threadtypes.nim b/lib/std/private/threadtypes.nim index f380ec40d84cb..0caa12e3790da 100644 --- a/lib/std/private/threadtypes.nim +++ b/lib/std/private/threadtypes.nim @@ -162,16 +162,29 @@ type const hasAllocStack* = defined(zephyr) # maybe freertos too? -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 +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 e57edcfdbbdb0..6a21b81bc43aa 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -149,9 +149,12 @@ else: nimThreadProcWrapperBody(closure) {.pop.} -proc running*[TArg](t: var Thread[TArg]): bool {.inline.} = +proc running*[TArg](t: Thread[TArg]): bool {.inline.} = ## Returns true if `t` is running. - result = t.dataFn.load() != 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.store 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.store 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.store param - t.dataFn.store 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.store 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 TArg isnot void: t.data.store param - t.dataFn.store tp - when hasSharedHeap: t.stackSize = ThreadStackSize + 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,14 +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.store cast[PGcThread](allocThreadStorage(sizeof(GcThread))) - - when TArg isnot void: t.data.store param - t.dataFn.store tp - when hasSharedHeap: - var stackSize: PGcThread = t.core.load() - stacksize.stackSize = ThreadStackSize - t.core.store stacksize + 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 + var a {.noinit.}: Pthread_attr doAssert pthread_attr_init(a) == 0 when hasAllocStack: @@ -288,9 +354,14 @@ else: when not defined(ios): # This fails on iOS doAssert(setstacksizeResult == 0) - if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: - raise newException(ResourceExhaustedError, "cannot create thread") - doAssert pthread_attr_destroy(a) == 0 + when not defined(cpp): + if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: + raise newException(ResourceExhaustedError, "cannot create thread") + doAssert pthread_attr_destroy(a) == 0 + else: + if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: + raise newException(ResourceExhaustedError, "cannot create thread") + doAssert pthread_attr_destroy(a) == 0 proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = ## Pins a thread to a `CPU`:idx:. diff --git a/lib/system/threadimpl.nim b/lib/system/threadimpl.nim index 0becceee3f886..e429509f14bdc 100644 --- a/lib/system/threadimpl.nim +++ b/lib/system/threadimpl.nim @@ -52,11 +52,19 @@ when defined(boehmgc): boehmGC_register_my_thread(sb) try: let thrd = cast[ptr Thread[TArg]](thrd) - let dataFn = thrd.dataFn.load() + when not defined(cpp): + let dataFn = thrd.dataFn.load(moAcquireRelease) when TArg is void: - dataFn() + when not defined(cpp): + dataFn() + else: + thrd.dataFn() else: - dataFn(thrd.data.load()) + when not defined(cpp): + let data = thrd.data.load(moAcquireRelease) + dataFn(data) + else: + thrd.dataFn(thrd.data) except: threadTrouble() finally: @@ -65,16 +73,28 @@ when defined(boehmgc): else: proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = try: - let dataFn = thrd.dataFn.load() + when not defined(cpp): + let dataFn = thrd.dataFn.load(moAcquireRelease) when TArg is void: - dataFn() + when not defined(cpp): + dataFn() + else: + thrd.dataFn() else: when defined(nimV2): - dataFn(thrd.data.load()) + 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.load()) - 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: @@ -100,16 +120,29 @@ proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = template nimThreadProcWrapperBody*(closure: untyped): untyped = var thrd = cast[ptr Thread[TArg]](closure) - var core = thrd.core.load() - when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core.load()) + 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 - # exception is tried to be re-raised by the code-gen after the `finally`! + # no need for a ``try finally`` here, nor would it be correct: The current + # exception is tried to be re-raised by the code-gen after the ``finally``! # However this is doomed to fail, because we already unmapped every heap # page! # mark as not running anymore: - thrd.core.store nil - thrd.dataFn.store nil - deallocThreadStorage(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)) From af0d68f7a9ccb9ec233374f4e4baac8d8b2871da Mon Sep 17 00:00:00 2001 From: Michael Lenz <35996377+mk1nz@users.noreply.github.com> Date: Sat, 18 Jan 2025 20:36:57 +0400 Subject: [PATCH 6/7] Update typedthreads.nim --- lib/std/typedthreads.nim | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 6a21b81bc43aa..2308684054328 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -354,14 +354,9 @@ else: when not defined(ios): # This fails on iOS doAssert(setstacksizeResult == 0) - when not defined(cpp): - if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: - raise newException(ResourceExhaustedError, "cannot create thread") - doAssert pthread_attr_destroy(a) == 0 - else: - if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: - raise newException(ResourceExhaustedError, "cannot create thread") - doAssert pthread_attr_destroy(a) == 0 + if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: + raise newException(ResourceExhaustedError, "cannot create thread") + doAssert pthread_attr_destroy(a) == 0 proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) = ## Pins a thread to a `CPU`:idx:. From 17e7e9a00e3759c81ff664af60264c67f686c39a Mon Sep 17 00:00:00 2001 From: Michael Lenz <35996377+mk1nz@users.noreply.github.com> Date: Sat, 18 Jan 2025 21:38:54 +0400 Subject: [PATCH 7/7] Update typedthreads.nim --- lib/std/typedthreads.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 2308684054328..a0748377a5c6f 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -149,7 +149,7 @@ 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. when not defined(cpp): result = t.dataFn.load(moAcquireRelease) != nil