mirror of
https://github.com/nim-lang/Nim.git
synced 2026-07-20 07:51:32 +00:00
Merge pull request #2995 from avsej/fix-tls-dlopen-issue
Fix #2672. Do not define globalsSlot for native TLS
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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.}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user