Threads work again
This commit is contained in:
parent
dafa8ccaf3
commit
24afab2a95
1 changed files with 46 additions and 32 deletions
|
|
@ -1,17 +1,17 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Nimrod's Runtime Library
|
# Nim's Runtime Library
|
||||||
# (c) Copyright 2012 Andreas Rumpf
|
# (c) Copyright 2012 Andreas Rumpf
|
||||||
#
|
#
|
||||||
# See the file "copying.txt", included in this
|
# See the file "copying.txt", included in this
|
||||||
# distribution, for details about the copyright.
|
# distribution, for details about the copyright.
|
||||||
#
|
#
|
||||||
|
|
||||||
## Thread support for Nimrod. **Note**: This is part of the system module.
|
## Thread support for Nim. **Note**: This is part of the system module.
|
||||||
## Do not import it directly. To activate thread support you need to compile
|
## Do not import it directly. To activate thread support you need to compile
|
||||||
## with the ``--threads:on`` command line switch.
|
## with the ``--threads:on`` command line switch.
|
||||||
##
|
##
|
||||||
## Nimrod's memory model for threads is quite different from other common
|
## Nim's memory model for threads is quite different from other common
|
||||||
## programming languages (C, Pascal): Each thread has its own
|
## programming languages (C, Pascal): Each thread has its own
|
||||||
## (garbage collected) heap and sharing of memory is restricted. This helps
|
## (garbage collected) heap and sharing of memory is restricted. This helps
|
||||||
## to prevent race conditions and improves efficiency. See `the manual for
|
## to prevent race conditions and improves efficiency. See `the manual for
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
##
|
##
|
||||||
## Example:
|
## Example:
|
||||||
##
|
##
|
||||||
## .. code-block:: nimrod
|
## .. code-block:: Nim
|
||||||
##
|
##
|
||||||
## import locks
|
## import locks
|
||||||
##
|
##
|
||||||
|
|
@ -190,7 +190,7 @@ var globalsSlot = threadVarAlloc()
|
||||||
|
|
||||||
when emulatedThreadVars:
|
when emulatedThreadVars:
|
||||||
proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} =
|
proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} =
|
||||||
result = addr(cast[PGcThread](ThreadVarGetValue(globalsSlot)).tls)
|
result = addr(cast[PGcThread](threadVarGetValue(globalsSlot)).tls)
|
||||||
|
|
||||||
when useStackMaskHack:
|
when useStackMaskHack:
|
||||||
proc maskStackPointer(offset: int): pointer {.compilerRtl, inl.} =
|
proc maskStackPointer(offset: int): pointer {.compilerRtl, inl.} =
|
||||||
|
|
@ -210,7 +210,7 @@ when not defined(useNimRtl):
|
||||||
initGC()
|
initGC()
|
||||||
|
|
||||||
when emulatedThreadVars:
|
when emulatedThreadVars:
|
||||||
if NimThreadVarsSize() > sizeof(TThreadLocalStorage):
|
if nimThreadVarsSize() > sizeof(TThreadLocalStorage):
|
||||||
echo "too large thread local storage size requested"
|
echo "too large thread local storage size requested"
|
||||||
quit 1
|
quit 1
|
||||||
|
|
||||||
|
|
@ -245,14 +245,14 @@ when not defined(useNimRtl):
|
||||||
# the GC can examine the stacks?
|
# the GC can examine the stacks?
|
||||||
proc stopTheWord() = discard
|
proc stopTheWord() = discard
|
||||||
|
|
||||||
# We jump through some hops here to ensure that Nimrod thread procs can have
|
# We jump through some hops here to ensure that Nim thread procs can have
|
||||||
# the Nimrod calling convention. This is needed because thread procs are
|
# the Nim calling convention. This is needed because thread procs are
|
||||||
# ``stdcall`` on Windows and ``noconv`` on UNIX. Alternative would be to just
|
# ``stdcall`` on Windows and ``noconv`` on UNIX. Alternative would be to just
|
||||||
# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
|
# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
|
||||||
|
|
||||||
type
|
type
|
||||||
TThread* {.pure, final.}[TArg] =
|
TThread* {.pure, final.}[TArg] =
|
||||||
object of TGcThread ## Nimrod thread. A thread is a heavy object (~14K)
|
object of TGcThread ## Nim thread. A thread is a heavy object (~14K)
|
||||||
## that **must not** be part of a message! Use
|
## that **must not** be part of a message! Use
|
||||||
## a ``TThreadId`` for that.
|
## a ``TThreadId`` for that.
|
||||||
when TArg is void:
|
when TArg is void:
|
||||||
|
|
@ -267,7 +267,7 @@ when not defined(boehmgc) and not hasSharedHeap:
|
||||||
proc deallocOsPages()
|
proc deallocOsPages()
|
||||||
|
|
||||||
template threadProcWrapperBody(closure: expr) {.immediate.} =
|
template threadProcWrapperBody(closure: expr) {.immediate.} =
|
||||||
when declared(globalsSlot): ThreadVarSetValue(globalsSlot, closure)
|
when declared(globalsSlot): threadVarSetValue(globalsSlot, closure)
|
||||||
var t = cast[ptr TThread[TArg]](closure)
|
var t = cast[ptr TThread[TArg]](closure)
|
||||||
when useStackMaskHack:
|
when useStackMaskHack:
|
||||||
var tls: TThreadLocalStorage
|
var tls: TThreadLocalStorage
|
||||||
|
|
@ -305,22 +305,26 @@ proc running*[TArg](t: TThread[TArg]): bool {.inline.} =
|
||||||
## returns true if `t` is running.
|
## returns true if `t` is running.
|
||||||
result = t.dataFn != nil
|
result = t.dataFn != nil
|
||||||
|
|
||||||
proc joinThread*[TArg](t: TThread[TArg]) {.inline.} =
|
when hostOS == "windows":
|
||||||
|
proc joinThread*[TArg](t: TThread[TArg]) {.inline.} =
|
||||||
## waits for the thread `t` to finish.
|
## waits for the thread `t` to finish.
|
||||||
when hostOS == "windows":
|
|
||||||
discard waitForSingleObject(t.sys, -1'i32)
|
discard waitForSingleObject(t.sys, -1'i32)
|
||||||
else:
|
|
||||||
discard pthread_join(t.sys, nil)
|
|
||||||
|
|
||||||
proc joinThreads*[TArg](t: varargs[TThread[TArg]]) =
|
proc joinThreads*[TArg](t: varargs[TThread[TArg]]) =
|
||||||
## waits for every thread in `t` to finish.
|
## waits for every thread in `t` to finish.
|
||||||
when hostOS == "windows":
|
|
||||||
var a: array[0..255, TSysThread]
|
var a: array[0..255, TSysThread]
|
||||||
sysAssert a.len >= t.len, "a.len >= t.len"
|
sysAssert a.len >= t.len, "a.len >= t.len"
|
||||||
for i in 0..t.high: a[i] = t[i].sys
|
for i in 0..t.high: a[i] = t[i].sys
|
||||||
discard waitForMultipleObjects(t.len.int32,
|
discard waitForMultipleObjects(t.len.int32,
|
||||||
cast[ptr TSysThread](addr(a)), 1, -1)
|
cast[ptr TSysThread](addr(a)), 1, -1)
|
||||||
else:
|
|
||||||
|
else:
|
||||||
|
proc joinThread*[TArg](t: TThread[TArg]) {.inline.} =
|
||||||
|
## waits for the thread `t` to finish.
|
||||||
|
discard pthread_join(t.sys, nil)
|
||||||
|
|
||||||
|
proc joinThreads*[TArg](t: varargs[TThread[TArg]]) =
|
||||||
|
## waits for every thread in `t` to finish.
|
||||||
for i in 0..t.high: joinThread(t[i])
|
for i in 0..t.high: joinThread(t[i])
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -335,7 +339,8 @@ when false:
|
||||||
when declared(registerThread): unregisterThread(addr(t))
|
when declared(registerThread): unregisterThread(addr(t))
|
||||||
t.dataFn = nil
|
t.dataFn = nil
|
||||||
|
|
||||||
proc createThread*[TArg](t: var TThread[TArg],
|
when hostOS == "windows":
|
||||||
|
proc createThread*[TArg](t: var TThread[TArg],
|
||||||
tp: proc (arg: TArg) {.thread.},
|
tp: proc (arg: TArg) {.thread.},
|
||||||
param: TArg) =
|
param: TArg) =
|
||||||
## creates a new thread `t` and starts its execution. Entry point is the
|
## creates a new thread `t` and starts its execution. Entry point is the
|
||||||
|
|
@ -344,13 +349,22 @@ proc createThread*[TArg](t: var TThread[TArg],
|
||||||
when TArg isnot void: t.data = param
|
when TArg isnot void: t.data = param
|
||||||
t.dataFn = tp
|
t.dataFn = tp
|
||||||
when hasSharedHeap: t.stackSize = ThreadStackSize
|
when hasSharedHeap: t.stackSize = ThreadStackSize
|
||||||
when hostOS == "windows":
|
|
||||||
var dummyThreadId: int32
|
var dummyThreadId: int32
|
||||||
t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg],
|
t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg],
|
||||||
addr(t), 0'i32, dummyThreadId)
|
addr(t), 0'i32, dummyThreadId)
|
||||||
if t.sys <= 0:
|
if t.sys <= 0:
|
||||||
raise newException(EResourceExhausted, "cannot create thread")
|
raise newException(EResourceExhausted, "cannot create thread")
|
||||||
else:
|
|
||||||
|
else:
|
||||||
|
proc createThread*[TArg](t: var TThread[TArg],
|
||||||
|
tp: proc (arg: TArg) {.thread.},
|
||||||
|
param: TArg) =
|
||||||
|
## creates a new thread `t` and starts its execution. 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.
|
||||||
|
when TArg isnot void: t.data = param
|
||||||
|
t.dataFn = tp
|
||||||
|
when hasSharedHeap: t.stackSize = ThreadStackSize
|
||||||
var a {.noinit.}: Tpthread_attr
|
var a {.noinit.}: Tpthread_attr
|
||||||
pthread_attr_init(a)
|
pthread_attr_init(a)
|
||||||
pthread_attr_setstacksize(a, ThreadStackSize)
|
pthread_attr_setstacksize(a, ThreadStackSize)
|
||||||
|
|
@ -364,7 +378,7 @@ proc threadId*[TArg](t: var TThread[TArg]): TThreadId[TArg] {.inline.} =
|
||||||
proc myThreadId*[TArg](): TThreadId[TArg] =
|
proc myThreadId*[TArg](): TThreadId[TArg] =
|
||||||
## returns the thread ID of the thread that calls this proc. This is unsafe
|
## returns the thread ID of the thread that calls this proc. This is unsafe
|
||||||
## because the type ``TArg`` is not checked for consistency!
|
## because the type ``TArg`` is not checked for consistency!
|
||||||
result = cast[TThreadId[TArg]](ThreadVarGetValue(globalsSlot))
|
result = cast[TThreadId[TArg]](threadVarGetValue(globalsSlot))
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
proc mainThreadId*[TArg](): TThreadId[TArg] =
|
proc mainThreadId*[TArg](): TThreadId[TArg] =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue