Merge pull request #2995 from avsej/fix-tls-dlopen-issue

Fix #2672. Do not define globalsSlot for native TLS
This commit is contained in:
Andreas Rumpf
2015-06-26 10:24:04 +02:00
3 changed files with 53 additions and 60 deletions

View File

@@ -8,8 +8,8 @@
#
## This module implements the ability to access symbols from shared
## libraries. On POSIX this uses the ``dlsym`` mechanism, on
## Windows ``LoadLibrary``.
## libraries. On POSIX this uses the ``dlsym`` mechanism, on
## Windows ``LoadLibrary``.
type
LibHandle* = pointer ## a handle to a dynamically loaded library
@@ -17,11 +17,11 @@ type
{.deprecated: [TLibHandle: LibHandle].}
proc loadLib*(path: string, global_symbols=false): LibHandle
## loads a library from `path`. Returns nil if the library could not
## loads a library from `path`. Returns nil if the library could not
## be loaded.
proc loadLib*(): LibHandle
## gets the handle from the current executable. Returns nil if the
## gets the handle from the current executable. Returns nil if the
## library could not be loaded.
proc unloadLib*(lib: LibHandle)
@@ -63,13 +63,13 @@ when defined(posix):
proc dlsym(lib: LibHandle, name: cstring): pointer {.
importc, header: "<dlfcn.h>".}
proc loadLib(path: string, global_symbols=false): LibHandle =
proc loadLib(path: string, global_symbols=false): LibHandle =
var flags = RTLD_NOW
if global_symbols: flags = flags or RTLD_GLOBAL
return dlopen(path, flags)
proc loadLib(): LibHandle = return dlopen(nil, RTLD_NOW)
proc unloadLib(lib: LibHandle) = dlclose(lib)
proc symAddr(lib: LibHandle, name: cstring): pointer =
proc symAddr(lib: LibHandle, name: cstring): pointer =
return dlsym(lib, name)
elif defined(windows) or defined(dos):

View File

@@ -17,7 +17,7 @@
const
NilLibHandle: LibHandle = nil
proc rawWrite(f: File, s: string) =
proc rawWrite(f: File, s: string) =
# we cannot throw an exception here!
discard writeBuffer(f, cstring(s), s.len)
@@ -145,6 +145,5 @@ elif defined(mac):
else:
{.error: "no implementation for dyncalls".}
{.pop.}
{.pop.}

View File

@@ -11,7 +11,7 @@
## Do not import it directly. To activate thread support you need to compile
## with the ``--threads:on`` command line switch.
##
## Nim'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
## (garbage collected) heap and sharing of memory is restricted. This helps
## to prevent race conditions and improves efficiency. See `the manual for
@@ -26,7 +26,7 @@
## var
## thr: array [0..4, Thread[tuple[a,b: int]]]
## L: Lock
##
##
## proc threadFunc(interval: tuple[a,b: int]) {.thread.} =
## for i in interval.a..interval.b:
## acquire(L) # lock stdout
@@ -38,8 +38,8 @@
## for i in 0..high(thr):
## createThread(thr[i], threadFunc, (i*10, i*10+5))
## joinThreads(thr)
when not declared(NimString):
when not declared(NimString):
{.error: "You must not import this module explicitly".}
const
@@ -56,15 +56,15 @@ when defined(windows):
{.deprecated: [TSysThread: SysThread, TWinThreadProc: WinThreadProc].}
proc createThread(lpThreadAttributes: pointer, dwStackSize: int32,
lpStartAddress: WinThreadProc,
lpStartAddress: WinThreadProc,
lpParameter: pointer,
dwCreationFlags: int32,
dwCreationFlags: int32,
lpThreadId: var int32): SysThread {.
stdcall, dynlib: "kernel32", importc: "CreateThread".}
proc winSuspendThread(hThread: SysThread): int32 {.
stdcall, dynlib: "kernel32", importc: "SuspendThread".}
proc winResumeThread(hThread: SysThread): int32 {.
stdcall, dynlib: "kernel32", importc: "ResumeThread".}
@@ -76,7 +76,7 @@ when defined(windows):
proc terminateThread(hThread: SysThread, dwExitCode: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "TerminateThread".}
type
ThreadVarSlot = distinct int32
@@ -104,7 +104,7 @@ when defined(windows):
importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
else:
when not defined(macosx):
{.passL: "-pthread".}
@@ -116,7 +116,7 @@ else:
final, pure.} = object
Pthread_attr {.importc: "pthread_attr_t",
header: "<sys/types.h>", final, pure.} = object
Timespec {.importc: "struct timespec",
header: "<time.h>", final, pure.} = object
tv_sec: int
@@ -130,8 +130,8 @@ else:
importc, header: "<pthread.h>".}
proc pthread_create(a1: var SysThread, a2: var PthreadAttr,
a3: proc (x: pointer): pointer {.noconv.},
a4: pointer): cint {.importc: "pthread_create",
a3: proc (x: pointer): pointer {.noconv.},
a4: pointer): cint {.importc: "pthread_create",
header: "<pthread.h>".}
proc pthread_join(a1: SysThread, a2: ptr pointer): cint {.
importc, header: "<pthread.h>".}
@@ -146,7 +146,7 @@ else:
proc pthread_getspecific(a1: ThreadVarSlot): pointer {.
importc: "pthread_getspecific", header: "<pthread.h>".}
proc pthread_key_create(a1: ptr ThreadVarSlot,
proc pthread_key_create(a1: ptr ThreadVarSlot,
destruct: proc (x: pointer) {.noconv.}): int32 {.
importc: "pthread_key_create", header: "<pthread.h>".}
proc pthread_key_delete(a1: ThreadVarSlot): int32 {.
@@ -154,7 +154,7 @@ else:
proc pthread_setspecific(a1: ThreadVarSlot, a2: pointer): int32 {.
importc: "pthread_setspecific", header: "<pthread.h>".}
proc threadVarAlloc(): ThreadVarSlot {.inline.} =
discard pthread_key_create(addr(result), nil)
proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
@@ -196,33 +196,33 @@ type
nil
{.deprecated: [TThreadLocalStorage: ThreadLocalStorage, TGcThread: GcThread].}
# XXX it'd be more efficient to not use a global variable for the
# thread storage slot, but to rely on the implementation to assign slot X
# for us... ;-)
var globalsSlot: ThreadVarSlot
when not defined(useNimRtl):
when not useStackMaskHack:
var mainThread: GcThread
proc initThreadVarsEmulation() {.compilerProc, inline.} =
when not defined(useNimRtl):
globalsSlot = threadVarAlloc()
when declared(mainThread):
threadVarSetValue(globalsSlot, addr(mainThread))
#const globalsSlot = ThreadVarSlot(0)
#sysAssert checkSlot.int == globalsSlot.int
when emulatedThreadVars:
# XXX it'd be more efficient to not use a global variable for the
# thread storage slot, but to rely on the implementation to assign slot X
# for us... ;-)
var globalsSlot: ThreadVarSlot
proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} =
result = addr(cast[PGcThread](threadVarGetValue(globalsSlot)).tls)
proc initThreadVarsEmulation() {.compilerProc, inline.} =
when not defined(useNimRtl):
globalsSlot = threadVarAlloc()
when declared(mainThread):
threadVarSetValue(globalsSlot, addr(mainThread))
when useStackMaskHack:
proc maskStackPointer(offset: int): pointer {.compilerRtl, inl.} =
var x {.volatile.}: pointer
x = addr(x)
result = cast[pointer]((cast[int](x) and not ThreadStackMask) +%
result = cast[pointer]((cast[int](x) and not ThreadStackMask) +%
(0) +% offset)
# create for the main thread. Note: do not insert this data into the list
@@ -231,27 +231,27 @@ when not defined(useNimRtl):
when not useStackMaskHack:
#when not defined(createNimRtl): initStackBottom()
initGC()
when emulatedThreadVars:
if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
echo "too large thread local storage size requested"
quit 1
when hasSharedHeap and not defined(boehmgc) and not defined(gogc) and not defined(nogc):
var
threadList: PGcThread
proc registerThread(t: PGcThread) =
proc registerThread(t: PGcThread) =
# we need to use the GC global lock here!
acquireSys(HeapLock)
t.prev = nil
t.next = threadList
if threadList != nil:
if threadList != nil:
sysAssert(threadList.prev == nil, "threadList.prev == nil")
threadList.prev = t
threadList = t
releaseSys(HeapLock)
proc unregisterThread(t: PGcThread) =
# we need to use the GC global lock here!
acquireSys(HeapLock)
@@ -263,13 +263,13 @@ when not defined(useNimRtl):
t.next = nil
t.prev = nil
releaseSys(HeapLock)
# on UNIX, the GC uses ``SIGFREEZE`` to tell every thread to stop so that
# the GC can examine the stacks?
proc stopTheWord() = discard
# We jump through some hops here to ensure that Nim thread procs can have
# the Nim 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
# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
@@ -311,30 +311,30 @@ template threadProcWrapperBody(closure: expr) {.immediate.} =
# 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:
t.dataFn = nil
{.push stack_trace:off.}
when defined(windows):
proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} =
proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} =
threadProcWrapperBody(closure)
# implicitly return 0
else:
proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} =
proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} =
threadProcWrapperBody(closure)
{.pop.}
proc running*[TArg](t: Thread[TArg]): bool {.inline.} =
proc running*[TArg](t: Thread[TArg]): bool {.inline.} =
## returns true if `t` is running.
result = t.dataFn != nil
when hostOS == "windows":
proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
## waits for the thread `t` to finish.
discard waitForSingleObject(t.sys, -1'i32)
proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
## waits for every thread in `t` to finish.
var a: array[0..255, SysThread]
sysAssert a.len >= t.len, "a.len >= t.len"
@@ -365,7 +365,7 @@ when false:
when hostOS == "windows":
proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread.},
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
@@ -379,8 +379,8 @@ when hostOS == "windows":
if t.sys <= 0:
raise newException(ResourceExhaustedError, "cannot create thread")
else:
proc createThread*[TArg](t: var Thread[TArg],
tp: proc (arg: TArg) {.thread.},
proc createThread*[TArg](t: var Thread[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
@@ -398,11 +398,6 @@ proc threadId*[TArg](t: var Thread[TArg]): ThreadId[TArg] {.inline.} =
## returns the thread ID of `t`.
result = addr(t)
proc myThreadId*[TArg](): ThreadId[TArg] =
## returns the thread ID of the thread that calls this proc. This is unsafe
## because the type ``TArg`` is not checked for consistency!
result = cast[ThreadId[TArg]](threadVarGetValue(globalsSlot))
when false:
proc mainThreadId*[TArg](): ThreadId[TArg] =
## returns the thread ID of the main thread.
@@ -413,4 +408,3 @@ when useStackMaskHack:
var mainThread: Thread[pointer]
createThread(mainThread, tp)
joinThread(mainThread)