Threads work again

This commit is contained in:
Araq 2014-09-11 09:36:22 +02:00
commit 24afab2a95

View file

@ -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] =