threads.nim 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Thread support for Nim. **Note**: This is part of the system module.
  10. ## Do not import it directly. To activate thread support you need to compile
  11. ## with the ``--threads:on`` command line switch.
  12. ##
  13. ## Nim's memory model for threads is quite different from other common
  14. ## programming languages (C, Pascal): Each thread has its own
  15. ## (garbage collected) heap and sharing of memory is restricted. This helps
  16. ## to prevent race conditions and improves efficiency. See `the manual for
  17. ## details of this memory model <manual.html#threads>`_.
  18. ##
  19. ## Example:
  20. ##
  21. ## .. code-block:: Nim
  22. ##
  23. ## import locks
  24. ##
  25. ## var
  26. ## thr: array[0..4, Thread[tuple[a,b: int]]]
  27. ## L: Lock
  28. ##
  29. ## proc threadFunc(interval: tuple[a,b: int]) {.thread.} =
  30. ## for i in interval.a..interval.b:
  31. ## acquire(L) # lock stdout
  32. ## echo i
  33. ## release(L)
  34. ##
  35. ## initLock(L)
  36. ##
  37. ## for i in 0..high(thr):
  38. ## createThread(thr[i], threadFunc, (i*10, i*10+5))
  39. ## joinThreads(thr)
  40. when not declared(NimString):
  41. {.error: "You must not import this module explicitly".}
  42. const
  43. maxRegisters = 256 # don't think there is an arch with more registers
  44. useStackMaskHack = false ## use the stack mask hack for better performance
  45. StackGuardSize = 4096
  46. ThreadStackMask =
  47. when defined(genode):
  48. 1024*64*sizeof(int)-1
  49. else:
  50. 1024*256*sizeof(int)-1
  51. ThreadStackSize = ThreadStackMask+1 - StackGuardSize
  52. when defined(windows):
  53. type
  54. SysThread* = Handle
  55. WinThreadProc = proc (x: pointer): int32 {.stdcall.}
  56. proc createThread(lpThreadAttributes: pointer, dwStackSize: int32,
  57. lpStartAddress: WinThreadProc,
  58. lpParameter: pointer,
  59. dwCreationFlags: int32,
  60. lpThreadId: var int32): SysThread {.
  61. stdcall, dynlib: "kernel32", importc: "CreateThread".}
  62. proc winSuspendThread(hThread: SysThread): int32 {.
  63. stdcall, dynlib: "kernel32", importc: "SuspendThread".}
  64. proc winResumeThread(hThread: SysThread): int32 {.
  65. stdcall, dynlib: "kernel32", importc: "ResumeThread".}
  66. proc waitForMultipleObjects(nCount: int32,
  67. lpHandles: ptr SysThread,
  68. bWaitAll: int32,
  69. dwMilliseconds: int32): int32 {.
  70. stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
  71. proc terminateThread(hThread: SysThread, dwExitCode: int32): int32 {.
  72. stdcall, dynlib: "kernel32", importc: "TerminateThread".}
  73. proc getCurrentThreadId(): int32 {.
  74. stdcall, dynlib: "kernel32", importc: "GetCurrentThreadId".}
  75. type
  76. ThreadVarSlot = distinct int32
  77. when true:
  78. proc threadVarAlloc(): ThreadVarSlot {.
  79. importc: "TlsAlloc", stdcall, header: "<windows.h>".}
  80. proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
  81. importc: "TlsSetValue", stdcall, header: "<windows.h>".}
  82. proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
  83. importc: "TlsGetValue", stdcall, header: "<windows.h>".}
  84. proc getLastError(): uint32 {.
  85. importc: "GetLastError", stdcall, header: "<windows.h>".}
  86. proc setLastError(x: uint32) {.
  87. importc: "SetLastError", stdcall, header: "<windows.h>".}
  88. proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer =
  89. let realLastError = getLastError()
  90. result = tlsGetValue(dwTlsIndex)
  91. setLastError(realLastError)
  92. else:
  93. proc threadVarAlloc(): ThreadVarSlot {.
  94. importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
  95. proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
  96. importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
  97. proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
  98. importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
  99. proc setThreadAffinityMask(hThread: SysThread, dwThreadAffinityMask: uint) {.
  100. importc: "SetThreadAffinityMask", stdcall, header: "<windows.h>".}
  101. elif defined(genode):
  102. import genode/env
  103. const
  104. GenodeHeader = "genode_cpp/threads.h"
  105. type
  106. SysThread* {.importcpp: "Nim::SysThread",
  107. header: GenodeHeader, final, pure.} = object
  108. GenodeThreadProc = proc (x: pointer) {.noconv.}
  109. ThreadVarSlot = int
  110. proc initThread(s: var SysThread,
  111. env: GenodeEnv,
  112. stackSize: culonglong,
  113. entry: GenodeThreadProc,
  114. arg: pointer,
  115. affinity: cuint) {.
  116. importcpp: "#.initThread(@)".}
  117. proc threadVarAlloc(): ThreadVarSlot = 0
  118. proc offMainThread(): bool {.
  119. importcpp: "Nim::SysThread::offMainThread",
  120. header: GenodeHeader.}
  121. proc threadVarSetValue(value: pointer) {.
  122. importcpp: "Nim::SysThread::threadVarSetValue(@)",
  123. header: GenodeHeader.}
  124. proc threadVarGetValue(): pointer {.
  125. importcpp: "Nim::SysThread::threadVarGetValue()",
  126. header: GenodeHeader.}
  127. var mainTls: pointer
  128. proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
  129. if offMainThread():
  130. threadVarSetValue(value);
  131. else:
  132. mainTls = value
  133. proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
  134. if offMainThread():
  135. threadVarGetValue();
  136. else:
  137. mainTls
  138. else:
  139. when not (defined(macosx) or defined(haiku)):
  140. {.passL: "-pthread".}
  141. when not defined(haiku):
  142. {.passC: "-pthread".}
  143. const
  144. schedh = "#define _GNU_SOURCE\n#include <sched.h>"
  145. pthreadh = "#define _GNU_SOURCE\n#include <pthread.h>"
  146. when not declared(Time):
  147. when defined(linux):
  148. type Time = clong
  149. else:
  150. type Time = int
  151. when (defined(linux) or defined(nintendoswitch)) and defined(amd64):
  152. type
  153. SysThread* {.importc: "pthread_t",
  154. header: "<sys/types.h>" .} = distinct culong
  155. Pthread_attr {.importc: "pthread_attr_t",
  156. header: "<sys/types.h>".} = object
  157. abi: array[56 div sizeof(clong), clong]
  158. ThreadVarSlot {.importc: "pthread_key_t",
  159. header: "<sys/types.h>".} = distinct cuint
  160. else:
  161. type
  162. SysThread* {.importc: "pthread_t", header: "<sys/types.h>".} = object
  163. Pthread_attr {.importc: "pthread_attr_t",
  164. header: "<sys/types.h>".} = object
  165. ThreadVarSlot {.importc: "pthread_key_t",
  166. header: "<sys/types.h>".} = object
  167. type
  168. Timespec {.importc: "struct timespec", header: "<time.h>".} = object
  169. tv_sec: Time
  170. tv_nsec: clong
  171. proc pthread_attr_init(a1: var PthreadAttr) {.
  172. importc, header: pthreadh.}
  173. proc pthread_attr_setstacksize(a1: var PthreadAttr, a2: int) {.
  174. importc, header: pthreadh.}
  175. proc pthread_create(a1: var SysThread, a2: var PthreadAttr,
  176. a3: proc (x: pointer): pointer {.noconv.},
  177. a4: pointer): cint {.importc: "pthread_create",
  178. header: pthreadh.}
  179. proc pthread_join(a1: SysThread, a2: ptr pointer): cint {.
  180. importc, header: pthreadh.}
  181. proc pthread_cancel(a1: SysThread): cint {.
  182. importc: "pthread_cancel", header: pthreadh.}
  183. proc pthread_getspecific(a1: ThreadVarSlot): pointer {.
  184. importc: "pthread_getspecific", header: pthreadh.}
  185. proc pthread_key_create(a1: ptr ThreadVarSlot,
  186. destruct: proc (x: pointer) {.noconv.}): int32 {.
  187. importc: "pthread_key_create", header: pthreadh.}
  188. proc pthread_key_delete(a1: ThreadVarSlot): int32 {.
  189. importc: "pthread_key_delete", header: pthreadh.}
  190. proc pthread_setspecific(a1: ThreadVarSlot, a2: pointer): int32 {.
  191. importc: "pthread_setspecific", header: pthreadh.}
  192. proc threadVarAlloc(): ThreadVarSlot {.inline.} =
  193. discard pthread_key_create(addr(result), nil)
  194. proc threadVarSetValue(s: ThreadVarSlot, value: pointer) {.inline.} =
  195. discard pthread_setspecific(s, value)
  196. proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
  197. result = pthread_getspecific(s)
  198. when useStackMaskHack:
  199. proc pthread_attr_setstack(attr: var PthreadAttr, stackaddr: pointer,
  200. size: int): cint {.
  201. importc: "pthread_attr_setstack", header: pthreadh.}
  202. type CpuSet {.importc: "cpu_set_t", header: schedh.} = object
  203. when defined(linux) and defined(amd64):
  204. abi: array[1024 div (8 * sizeof(culong)), culong]
  205. proc cpusetZero(s: var CpuSet) {.importc: "CPU_ZERO", header: schedh.}
  206. proc cpusetIncl(cpu: cint; s: var CpuSet) {.
  207. importc: "CPU_SET", header: schedh.}
  208. proc setAffinity(thread: SysThread; setsize: csize; s: var CpuSet) {.
  209. importc: "pthread_setaffinity_np", header: pthreadh.}
  210. const
  211. emulatedThreadVars = compileOption("tlsEmulation")
  212. when emulatedThreadVars:
  213. # the compiler generates this proc for us, so that we can get the size of
  214. # the thread local var block; we use this only for sanity checking though
  215. proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
  216. # we preallocate a fixed size for thread local storage, so that no heap
  217. # allocations are needed. Currently less than 16K are used on a 64bit machine.
  218. # We use ``float`` for proper alignment:
  219. const nimTlsSize {.intdefine.} = 16000
  220. type
  221. ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
  222. PGcThread = ptr GcThread
  223. GcThread {.pure, inheritable.} = object
  224. when emulatedThreadVars and not useStackMaskHack:
  225. tls: ThreadLocalStorage
  226. else:
  227. nil
  228. when hasSharedHeap:
  229. next, prev: PGcThread
  230. stackBottom, stackTop: pointer
  231. stackSize: int
  232. else:
  233. nil
  234. when not defined(useNimRtl):
  235. when not useStackMaskHack:
  236. var mainThread: GcThread
  237. #const globalsSlot = ThreadVarSlot(0)
  238. #sysAssert checkSlot.int == globalsSlot.int
  239. when emulatedThreadVars:
  240. # XXX it'd be more efficient to not use a global variable for the
  241. # thread storage slot, but to rely on the implementation to assign slot X
  242. # for us... ;-)
  243. var globalsSlot: ThreadVarSlot
  244. proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} =
  245. result = addr(cast[PGcThread](threadVarGetValue(globalsSlot)).tls)
  246. proc initThreadVarsEmulation() {.compilerProc, inline.} =
  247. when not defined(useNimRtl):
  248. globalsSlot = threadVarAlloc()
  249. when declared(mainThread):
  250. threadVarSetValue(globalsSlot, addr(mainThread))
  251. when useStackMaskHack:
  252. proc maskStackPointer(offset: int): pointer {.compilerRtl, inl.} =
  253. var x {.volatile.}: pointer
  254. x = addr(x)
  255. result = cast[pointer]((cast[int](x) and not ThreadStackMask) +%
  256. (0) +% offset)
  257. # create for the main thread. Note: do not insert this data into the list
  258. # of all threads; it's not to be stopped etc.
  259. when not defined(useNimRtl):
  260. when not useStackMaskHack:
  261. #when not defined(createNimRtl): initStackBottom()
  262. when declared(initGC):
  263. initGC()
  264. when not emulatedThreadVars:
  265. type ThreadType {.pure.} = enum
  266. None = 0,
  267. NimThread = 1,
  268. ForeignThread = 2
  269. var
  270. threadType {.rtlThreadVar.}: ThreadType
  271. threadType = ThreadType.NimThread
  272. when emulatedThreadVars:
  273. if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
  274. echo "too large thread local storage size requested ",
  275. "(", nimThreadVarsSize(), "/", sizeof(ThreadLocalStorage), "). ",
  276. "Use -d:\"nimTlsSize=", nimThreadVarsSize(),
  277. "\" to preallocate sufficient storage."
  278. quit 1
  279. when hasSharedHeap and not defined(boehmgc) and not defined(gogc) and not defined(nogc):
  280. var
  281. threadList: PGcThread
  282. proc registerThread(t: PGcThread) =
  283. # we need to use the GC global lock here!
  284. acquireSys(HeapLock)
  285. t.prev = nil
  286. t.next = threadList
  287. if threadList != nil:
  288. sysAssert(threadList.prev == nil, "threadList.prev == nil")
  289. threadList.prev = t
  290. threadList = t
  291. releaseSys(HeapLock)
  292. proc unregisterThread(t: PGcThread) =
  293. # we need to use the GC global lock here!
  294. acquireSys(HeapLock)
  295. if t == threadList: threadList = t.next
  296. if t.next != nil: t.next.prev = t.prev
  297. if t.prev != nil: t.prev.next = t.next
  298. # so that a thread can be unregistered twice which might happen if the
  299. # code executes `destroyThread`:
  300. t.next = nil
  301. t.prev = nil
  302. releaseSys(HeapLock)
  303. # on UNIX, the GC uses ``SIGFREEZE`` to tell every thread to stop so that
  304. # the GC can examine the stacks?
  305. proc stopTheWord() = discard
  306. # We jump through some hops here to ensure that Nim thread procs can have
  307. # the Nim calling convention. This is needed because thread procs are
  308. # ``stdcall`` on Windows and ``noconv`` on UNIX. Alternative would be to just
  309. # use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
  310. type
  311. Thread* {.pure, final.}[TArg] = object
  312. core: PGcThread
  313. sys: SysThread
  314. when TArg is void:
  315. dataFn: proc () {.nimcall, gcsafe.}
  316. else:
  317. dataFn: proc (m: TArg) {.nimcall, gcsafe.}
  318. data: TArg
  319. var
  320. threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe.}]
  321. proc onThreadDestruction*(handler: proc () {.closure, gcsafe.}) =
  322. ## Registers a *thread local* handler that is called at the thread's
  323. ## destruction.
  324. ## A thread is destructed when the ``.thread`` proc returns
  325. ## normally or when it raises an exception. Note that unhandled exceptions
  326. ## in a thread nevertheless cause the whole process to die.
  327. when not defined(nimNoNilSeqs):
  328. if threadDestructionHandlers.isNil:
  329. threadDestructionHandlers = @[]
  330. threadDestructionHandlers.add handler
  331. template afterThreadRuns() =
  332. for i in countdown(threadDestructionHandlers.len-1, 0):
  333. threadDestructionHandlers[i]()
  334. when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
  335. proc deallocOsPages() {.rtl.}
  336. when defined(boehmgc):
  337. type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.}
  338. proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer)
  339. {.importc: "GC_call_with_stack_base", boehmGC.}
  340. proc boehmGC_register_my_thread(sb: pointer)
  341. {.importc: "GC_register_my_thread", boehmGC.}
  342. proc boehmGC_unregister_my_thread()
  343. {.importc: "GC_unregister_my_thread", boehmGC.}
  344. proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv.} =
  345. boehmGC_register_my_thread(sb)
  346. try:
  347. let thrd = cast[ptr Thread[TArg]](thrd)
  348. when TArg is void:
  349. thrd.dataFn()
  350. else:
  351. thrd.dataFn(thrd.data)
  352. finally:
  353. afterThreadRuns()
  354. boehmGC_unregister_my_thread()
  355. else:
  356. proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) =
  357. try:
  358. when TArg is void:
  359. thrd.dataFn()
  360. else:
  361. var x: TArg
  362. deepCopy(x, thrd.data)
  363. thrd.dataFn(x)
  364. finally:
  365. afterThreadRuns()
  366. proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) =
  367. when defined(boehmgc):
  368. boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
  369. elif not defined(nogc) and not defined(gogc) and not defined(gcRegions):
  370. var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall.} =
  371. threadProcWrapDispatch[TArg]
  372. when not hasSharedHeap:
  373. # init the GC for refc/markandsweep
  374. nimGC_setStackBottom(addr(p))
  375. initGC()
  376. when declared(threadType):
  377. threadType = ThreadType.NimThread
  378. when declared(registerThread):
  379. thrd.core.stackBottom = addr(thrd)
  380. registerThread(thrd.core)
  381. p(thrd)
  382. when declared(registerThread): unregisterThread(thrd.core)
  383. when declared(deallocOsPages): deallocOsPages()
  384. else:
  385. threadProcWrapDispatch(thrd)
  386. template threadProcWrapperBody(closure: untyped): untyped =
  387. var thrd = cast[ptr Thread[TArg]](closure)
  388. var core = thrd.core
  389. when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core)
  390. when declared(initAllocator):
  391. initAllocator()
  392. threadProcWrapStackFrame(thrd)
  393. # Since an unhandled exception terminates the whole process (!), there is
  394. # no need for a ``try finally`` here, nor would it be correct: The current
  395. # exception is tried to be re-raised by the code-gen after the ``finally``!
  396. # However this is doomed to fail, because we already unmapped every heap
  397. # page!
  398. # mark as not running anymore:
  399. thrd.core = nil
  400. thrd.dataFn = nil
  401. deallocShared(cast[pointer](core))
  402. {.push stack_trace:off.}
  403. when defined(windows):
  404. proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} =
  405. threadProcWrapperBody(closure)
  406. # implicitly return 0
  407. elif defined(genode):
  408. proc threadProcWrapper[TArg](closure: pointer) {.noconv.} =
  409. threadProcWrapperBody(closure)
  410. else:
  411. proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} =
  412. threadProcWrapperBody(closure)
  413. {.pop.}
  414. proc running*[TArg](t: Thread[TArg]): bool {.inline.} =
  415. ## returns true if `t` is running.
  416. result = t.dataFn != nil
  417. proc handle*[TArg](t: Thread[TArg]): SysThread {.inline.} =
  418. ## returns the thread handle of `t`.
  419. result = t.sys
  420. when hostOS == "windows":
  421. const MAXIMUM_WAIT_OBJECTS = 64
  422. proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
  423. ## waits for the thread `t` to finish.
  424. discard waitForSingleObject(t.sys, -1'i32)
  425. proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
  426. ## waits for every thread in `t` to finish.
  427. var a: array[MAXIMUM_WAIT_OBJECTS, SysThread]
  428. var k = 0
  429. while k < len(t):
  430. var count = min(len(t) - k, MAXIMUM_WAIT_OBJECTS)
  431. for i in 0..(count - 1): a[i] = t[i + k].sys
  432. discard waitForMultipleObjects(int32(count),
  433. cast[ptr SysThread](addr(a)), 1, -1)
  434. inc(k, MAXIMUM_WAIT_OBJECTS)
  435. elif defined(genode):
  436. proc joinThread*[TArg](t: Thread[TArg]) {.importcpp.}
  437. ## waits for the thread `t` to finish.
  438. proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
  439. ## waits for every thread in `t` to finish.
  440. for i in 0..t.high: joinThread(t[i])
  441. else:
  442. proc joinThread*[TArg](t: Thread[TArg]) {.inline.} =
  443. ## waits for the thread `t` to finish.
  444. discard pthread_join(t.sys, nil)
  445. proc joinThreads*[TArg](t: varargs[Thread[TArg]]) =
  446. ## waits for every thread in `t` to finish.
  447. for i in 0..t.high: joinThread(t[i])
  448. when false:
  449. # XXX a thread should really release its heap here somehow:
  450. proc destroyThread*[TArg](t: var Thread[TArg]) =
  451. ## forces the thread `t` to terminate. This is potentially dangerous if
  452. ## you don't have full control over `t` and its acquired resources.
  453. when hostOS == "windows":
  454. discard TerminateThread(t.sys, 1'i32)
  455. else:
  456. discard pthread_cancel(t.sys)
  457. when declared(registerThread): unregisterThread(addr(t))
  458. t.dataFn = nil
  459. ## if thread `t` already exited, `t.core` will be `null`.
  460. if not isNil(t.core):
  461. deallocShared(t.core)
  462. t.core = nil
  463. when hostOS == "windows":
  464. proc createThread*[TArg](t: var Thread[TArg],
  465. tp: proc (arg: TArg) {.thread, nimcall.},
  466. param: TArg) =
  467. ## creates a new thread `t` and starts its execution. Entry point is the
  468. ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
  469. ## don't need to pass any data to the thread.
  470. t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
  471. when TArg isnot void: t.data = param
  472. t.dataFn = tp
  473. when hasSharedHeap: t.core.stackSize = ThreadStackSize
  474. var dummyThreadId: int32
  475. t.sys = createThread(nil, ThreadStackSize, threadProcWrapper[TArg],
  476. addr(t), 0'i32, dummyThreadId)
  477. if t.sys <= 0:
  478. raise newException(ResourceExhaustedError, "cannot create thread")
  479. proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
  480. ## pins a thread to a `CPU`:idx:. In other words sets a
  481. ## thread's `affinity`:idx:. If you don't know what this means, you
  482. ## shouldn't use this proc.
  483. setThreadAffinityMask(t.sys, uint(1 shl cpu))
  484. elif defined(genode):
  485. var affinityOffset: cuint = 1
  486. ## CPU affinity offset for next thread, safe to roll-over
  487. proc createThread*[TArg](t: var Thread[TArg],
  488. tp: proc (arg: TArg) {.thread, nimcall.},
  489. param: TArg) =
  490. t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
  491. when TArg isnot void: t.data = param
  492. t.dataFn = tp
  493. when hasSharedHeap: t.stackSize = ThreadStackSize
  494. t.sys.initThread(
  495. runtimeEnv,
  496. ThreadStackSize.culonglong,
  497. threadProcWrapper[TArg], addr(t), affinityOffset)
  498. inc affinityOffset
  499. proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
  500. {.hint: "cannot change Genode thread CPU affinity after initialization".}
  501. discard
  502. else:
  503. proc createThread*[TArg](t: var Thread[TArg],
  504. tp: proc (arg: TArg) {.thread, nimcall.},
  505. param: TArg) =
  506. ## creates a new thread `t` and starts its execution. Entry point is the
  507. ## proc `tp`. `param` is passed to `tp`. `TArg` can be ``void`` if you
  508. ## don't need to pass any data to the thread.
  509. t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
  510. when TArg isnot void: t.data = param
  511. t.dataFn = tp
  512. when hasSharedHeap: t.core.stackSize = ThreadStackSize
  513. var a {.noinit.}: PthreadAttr
  514. pthread_attr_init(a)
  515. pthread_attr_setstacksize(a, ThreadStackSize)
  516. if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0:
  517. raise newException(ResourceExhaustedError, "cannot create thread")
  518. proc pinToCpu*[Arg](t: var Thread[Arg]; cpu: Natural) =
  519. ## pins a thread to a `CPU`:idx:. In other words sets a
  520. ## thread's `affinity`:idx:. If you don't know what this means, you
  521. ## shouldn't use this proc.
  522. when not defined(macosx):
  523. var s {.noinit.}: CpuSet
  524. cpusetZero(s)
  525. cpusetIncl(cpu.cint, s)
  526. setAffinity(t.sys, sizeof(s), s)
  527. proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
  528. createThread[void](t, tp)
  529. when false:
  530. proc mainThreadId*[TArg](): ThreadId[TArg] =
  531. ## returns the thread ID of the main thread.
  532. result = cast[ThreadId[TArg]](addr(mainThread))
  533. when useStackMaskHack:
  534. proc runMain(tp: proc () {.thread.}) {.compilerproc.} =
  535. var mainThread: Thread[pointer]
  536. createThread(mainThread, tp)
  537. joinThread(mainThread)
  538. ## we need to cache current threadId to not perform syscall all the time
  539. var threadId {.threadvar.}: int
  540. when defined(windows):
  541. proc getThreadId*(): int =
  542. ## get the ID of the currently running thread.
  543. if threadId == 0:
  544. threadId = int(getCurrentThreadId())
  545. result = threadId
  546. elif defined(linux):
  547. proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "<unistd.h>".}
  548. when defined(amd64):
  549. const NR_gettid = clong(186)
  550. else:
  551. var NR_gettid {.importc: "__NR_gettid", header: "<sys/syscall.h>".}: clong
  552. proc getThreadId*(): int =
  553. ## get the ID of the currently running thread.
  554. if threadId == 0:
  555. threadId = int(syscall(NR_gettid))
  556. result = threadId
  557. elif defined(dragonfly):
  558. proc lwp_gettid(): int32 {.importc, header: "unistd.h".}
  559. proc getThreadId*(): int =
  560. ## get the ID of the currently running thread.
  561. if threadId == 0:
  562. threadId = int(lwp_gettid())
  563. result = threadId
  564. elif defined(openbsd):
  565. proc getthrid(): int32 {.importc: "getthrid", header: "<unistd.h>".}
  566. proc getThreadId*(): int =
  567. ## get the ID of the currently running thread.
  568. if threadId == 0:
  569. threadId = int(getthrid())
  570. result = threadId
  571. elif defined(netbsd):
  572. proc lwp_self(): int32 {.importc: "_lwp_self", header: "<lwp.h>".}
  573. proc getThreadId*(): int =
  574. ## get the ID of the currently running thread.
  575. if threadId == 0:
  576. threadId = int(lwp_self())
  577. result = threadId
  578. elif defined(freebsd):
  579. proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
  580. var SYS_thr_self {.importc:"SYS_thr_self", header:"<sys/syscall.h>"}: cint
  581. proc getThreadId*(): int =
  582. ## get the ID of the currently running thread.
  583. var tid = 0.cint
  584. if threadId == 0:
  585. discard syscall(SYS_thr_self, addr tid)
  586. threadId = tid
  587. result = threadId
  588. elif defined(macosx):
  589. proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "<unistd.h>".}
  590. var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"<sys/syscall.h>".}: cint
  591. proc getThreadId*(): int =
  592. ## get the ID of the currently running thread.
  593. if threadId == 0:
  594. threadId = int(syscall(SYS_thread_selfid))
  595. result = threadId
  596. elif defined(solaris):
  597. type thread_t {.importc: "thread_t", header: "<thread.h>".} = distinct int
  598. proc thr_self(): thread_t {.importc, header: "<thread.h>".}
  599. proc getThreadId*(): int =
  600. ## get the ID of the currently running thread.
  601. if threadId == 0:
  602. threadId = int(thr_self())
  603. result = threadId
  604. elif defined(haiku):
  605. type thr_id {.importc: "thread_id", header: "<OS.h>".} = distinct int32
  606. proc find_thread(name: cstring): thr_id {.importc, header: "<OS.h>".}
  607. proc getThreadId*(): int =
  608. ## get the ID of the currently running thread.
  609. if threadId == 0:
  610. threadId = int(find_thread(nil))
  611. result = threadId