threadlocalstorage.nim 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. when defined(windows):
  2. type
  3. SysThread* = Handle
  4. WinThreadProc = proc (x: pointer): int32 {.stdcall.}
  5. proc createThread(lpThreadAttributes: pointer, dwStackSize: int32,
  6. lpStartAddress: WinThreadProc,
  7. lpParameter: pointer,
  8. dwCreationFlags: int32,
  9. lpThreadId: var int32): SysThread {.
  10. stdcall, dynlib: "kernel32", importc: "CreateThread".}
  11. proc winSuspendThread(hThread: SysThread): int32 {.
  12. stdcall, dynlib: "kernel32", importc: "SuspendThread".}
  13. proc winResumeThread(hThread: SysThread): int32 {.
  14. stdcall, dynlib: "kernel32", importc: "ResumeThread".}
  15. proc waitForSingleObject(hHandle: SysThread, dwMilliseconds: int32): int32 {.
  16. stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
  17. proc waitForMultipleObjects(nCount: int32,
  18. lpHandles: ptr SysThread,
  19. bWaitAll: int32,
  20. dwMilliseconds: int32): int32 {.
  21. stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
  22. proc terminateThread(hThread: SysThread, dwExitCode: int32): int32 {.
  23. stdcall, dynlib: "kernel32", importc: "TerminateThread".}
  24. type
  25. ThreadVarSlot = distinct int32
  26. when true:
  27. proc threadVarAlloc(): ThreadVarSlot {.
  28. importc: "TlsAlloc", stdcall, header: "<windows.h>".}
  29. proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
  30. importc: "TlsSetValue", stdcall, header: "<windows.h>".}
  31. proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
  32. importc: "TlsGetValue", stdcall, header: "<windows.h>".}
  33. proc getLastError(): uint32 {.
  34. importc: "GetLastError", stdcall, header: "<windows.h>".}
  35. proc setLastError(x: uint32) {.
  36. importc: "SetLastError", stdcall, header: "<windows.h>".}
  37. proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer =
  38. let realLastError = getLastError()
  39. result = tlsGetValue(dwTlsIndex)
  40. setLastError(realLastError)
  41. else:
  42. proc threadVarAlloc(): ThreadVarSlot {.
  43. importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
  44. proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
  45. importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
  46. proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
  47. importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
  48. proc setThreadAffinityMask(hThread: SysThread, dwThreadAffinityMask: uint) {.
  49. importc: "SetThreadAffinityMask", stdcall, header: "<windows.h>".}
  50. elif defined(genode):
  51. import genode/env
  52. const
  53. GenodeHeader = "genode_cpp/threads.h"
  54. type
  55. SysThread* {.importcpp: "Nim::SysThread",
  56. header: GenodeHeader, final, pure.} = object
  57. GenodeThreadProc = proc (x: pointer) {.noconv.}
  58. ThreadVarSlot = int
  59. proc initThread(s: var SysThread,
  60. env: GenodeEnv,
  61. stackSize: culonglong,
  62. entry: GenodeThreadProc,
  63. arg: pointer,
  64. affinity: cuint) {.
  65. importcpp: "#.initThread(@)".}
  66. proc threadVarAlloc(): ThreadVarSlot = 0
  67. proc offMainThread(): bool {.
  68. importcpp: "Nim::SysThread::offMainThread",
  69. header: GenodeHeader.}
  70. proc threadVarSetValue(value: pointer) {.
  71. importcpp: "Nim::SysThread::threadVarSetValue(@)",
  72. header: GenodeHeader.}
  73. proc threadVarGetValue(): pointer {.
  74. importcpp: "Nim::SysThread::threadVarGetValue()",
  75. header: GenodeHeader.}
  76. var mainTls: pointer
  77. proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
  78. if offMainThread():
  79. threadVarSetValue(value);
  80. else:
  81. mainTls = value
  82. proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
  83. if offMainThread():
  84. threadVarGetValue();
  85. else:
  86. mainTls
  87. else:
  88. when not (defined(macosx) or defined(haiku)):
  89. {.passl: "-pthread".}
  90. when not defined(haiku):
  91. {.passc: "-pthread".}
  92. const
  93. schedh = "#define _GNU_SOURCE\n#include <sched.h>"
  94. pthreadh = "#define _GNU_SOURCE\n#include <pthread.h>"
  95. when not declared(Time):
  96. when defined(linux):
  97. type Time = clong
  98. else:
  99. type Time = int
  100. when (defined(linux) or defined(nintendoswitch)) and defined(amd64):
  101. type
  102. SysThread* {.importc: "pthread_t",
  103. header: "<sys/types.h>" .} = distinct culong
  104. Pthread_attr {.importc: "pthread_attr_t",
  105. header: "<sys/types.h>".} = object
  106. abi: array[56 div sizeof(clong), clong]
  107. ThreadVarSlot {.importc: "pthread_key_t",
  108. header: "<sys/types.h>".} = distinct cuint
  109. elif defined(openbsd) and defined(amd64):
  110. type
  111. SysThread* {.importc: "pthread_t", header: "<pthread.h>".} = object
  112. Pthread_attr {.importc: "pthread_attr_t",
  113. header: "<pthread.h>".} = object
  114. ThreadVarSlot {.importc: "pthread_key_t",
  115. header: "<pthread.h>".} = cint
  116. else:
  117. type
  118. SysThread* {.importc: "pthread_t", header: "<sys/types.h>".} = int
  119. Pthread_attr {.importc: "pthread_attr_t",
  120. header: "<sys/types.h>".} = object
  121. ThreadVarSlot {.importc: "pthread_key_t",
  122. header: "<sys/types.h>".} = object
  123. type
  124. Timespec {.importc: "struct timespec", header: "<time.h>".} = object
  125. tv_sec: Time
  126. tv_nsec: clong
  127. proc pthread_attr_init(a1: var Pthread_attr): cint {.
  128. importc, header: pthreadh.}
  129. proc pthread_attr_setstack*(a1: ptr Pthread_attr, a2: pointer, a3: int): cint {.
  130. importc, header: pthreadh.}
  131. proc pthread_attr_setstacksize(a1: var Pthread_attr, a2: int): cint {.
  132. importc, header: pthreadh.}
  133. proc pthread_attr_destroy(a1: var Pthread_attr): cint {.
  134. importc, header: pthreadh.}
  135. proc pthread_create(a1: var SysThread, a2: var Pthread_attr,
  136. a3: proc (x: pointer): pointer {.noconv.},
  137. a4: pointer): cint {.importc: "pthread_create",
  138. header: pthreadh.}
  139. proc pthread_join(a1: SysThread, a2: ptr pointer): cint {.
  140. importc, header: pthreadh.}
  141. proc pthread_cancel(a1: SysThread): cint {.
  142. importc: "pthread_cancel", header: pthreadh.}
  143. proc pthread_getspecific(a1: ThreadVarSlot): pointer {.
  144. importc: "pthread_getspecific", header: pthreadh.}
  145. proc pthread_key_create(a1: ptr ThreadVarSlot,
  146. destruct: proc (x: pointer) {.noconv.}): int32 {.
  147. importc: "pthread_key_create", header: pthreadh.}
  148. proc pthread_key_delete(a1: ThreadVarSlot): int32 {.
  149. importc: "pthread_key_delete", header: pthreadh.}
  150. proc pthread_setspecific(a1: ThreadVarSlot, a2: pointer): int32 {.
  151. importc: "pthread_setspecific", header: pthreadh.}
  152. proc threadVarAlloc(): ThreadVarSlot {.inline.} =
  153. discard pthread_key_create(addr(result), nil)
  154. proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
  155. discard pthread_setspecific(s, value)
  156. proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
  157. result = pthread_getspecific(s)
  158. type CpuSet {.importc: "cpu_set_t", header: schedh.} = object
  159. when defined(linux) and defined(amd64):
  160. abi: array[1024 div (8 * sizeof(culong)), culong]
  161. proc cpusetZero(s: var CpuSet) {.importc: "CPU_ZERO", header: schedh.}
  162. proc cpusetIncl(cpu: cint; s: var CpuSet) {.
  163. importc: "CPU_SET", header: schedh.}
  164. when defined(android):
  165. # libc of android doesn't implement pthread_setaffinity_np,
  166. # it exposes pthread_gettid_np though, so we can use that in combination
  167. # with sched_setaffinity to set the thread affinity.
  168. type Pid {.importc: "pid_t", header: "<sys/types.h>".} = int32 # From posix_other.nim
  169. proc setAffinityTID(tid: Pid; setsize: csize_t; s: var CpuSet) {.
  170. importc: "sched_setaffinity", header: schedh.}
  171. proc pthread_gettid_np(thread: SysThread): Pid {.
  172. importc: "pthread_gettid_np", header: pthreadh.}
  173. proc setAffinity(thread: SysThread; setsize: csize_t; s: var CpuSet) =
  174. setAffinityTID(pthread_gettid_np(thread), setsize, s)
  175. else:
  176. proc setAffinity(thread: SysThread; setsize: csize_t; s: var CpuSet) {.
  177. importc: "pthread_setaffinity_np", header: pthreadh.}
  178. const
  179. emulatedThreadVars = compileOption("tlsEmulation")
  180. when emulatedThreadVars:
  181. # the compiler generates this proc for us, so that we can get the size of
  182. # the thread local var block; we use this only for sanity checking though
  183. proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
  184. # we preallocate a fixed size for thread local storage, so that no heap
  185. # allocations are needed. Currently less than 16K are used on a 64bit machine.
  186. # We use `float` for proper alignment:
  187. const nimTlsSize {.intdefine.} = 16000
  188. type
  189. ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
  190. PGcThread = ptr GcThread
  191. GcThread {.pure, inheritable.} = object
  192. when emulatedThreadVars:
  193. tls: ThreadLocalStorage
  194. else:
  195. nil
  196. when hasSharedHeap:
  197. next, prev: PGcThread
  198. stackBottom, stackTop: pointer
  199. stackSize: int
  200. else:
  201. nil
  202. when emulatedThreadVars:
  203. var globalsSlot: ThreadVarSlot
  204. when not defined(useNimRtl):
  205. var mainThread: GcThread
  206. proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} =
  207. result = addr(cast[PGcThread](threadVarGetValue(globalsSlot)).tls)
  208. proc initThreadVarsEmulation() {.compilerproc, inline.} =
  209. when not defined(useNimRtl):
  210. globalsSlot = threadVarAlloc()
  211. when declared(mainThread):
  212. threadVarSetValue(globalsSlot, addr(mainThread))
  213. when not defined(useNimRtl):
  214. when emulatedThreadVars:
  215. if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
  216. c_fprintf(cstderr, """too large thread local storage size requested,
  217. use -d:\"nimTlsSize=X\" to setup even more or stop using unittest.nim""")
  218. quit 1