atomics.nim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # Atomic operations for Nim.
  10. {.push stackTrace:off.}
  11. const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang)
  12. when someGcc and hasThreadSupport:
  13. type AtomMemModel* = distinct cint
  14. var ATOMIC_RELAXED* {.importc: "__ATOMIC_RELAXED", nodecl.}: AtomMemModel
  15. ## No barriers or synchronization.
  16. var ATOMIC_CONSUME* {.importc: "__ATOMIC_CONSUME", nodecl.}: AtomMemModel
  17. ## Data dependency only for both barrier and
  18. ## synchronization with another thread.
  19. var ATOMIC_ACQUIRE* {.importc: "__ATOMIC_ACQUIRE", nodecl.}: AtomMemModel
  20. ## Barrier to hoisting of code and synchronizes with
  21. ## release (or stronger)
  22. ## semantic stores from another thread.
  23. var ATOMIC_RELEASE* {.importc: "__ATOMIC_RELEASE", nodecl.}: AtomMemModel
  24. ## Barrier to sinking of code and synchronizes with
  25. ## acquire (or stronger)
  26. ## semantic loads from another thread.
  27. var ATOMIC_ACQ_REL* {.importc: "__ATOMIC_ACQ_REL", nodecl.}: AtomMemModel
  28. ## Full barrier in both directions and synchronizes
  29. ## with acquire loads
  30. ## and release stores in another thread.
  31. var ATOMIC_SEQ_CST* {.importc: "__ATOMIC_SEQ_CST", nodecl.}: AtomMemModel
  32. ## Full barrier in both directions and synchronizes
  33. ## with acquire loads
  34. ## and release stores in all threads.
  35. type
  36. AtomType* = SomeNumber|pointer|ptr|char|bool
  37. ## Type Class representing valid types for use with atomic procs
  38. proc atomicLoadN*[T: AtomType](p: ptr T, mem: AtomMemModel): T {.
  39. importc: "__atomic_load_n", nodecl.}
  40. ## This proc implements an atomic load operation. It returns the contents at p.
  41. ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_CONSUME.
  42. proc atomicLoad*[T: AtomType](p, ret: ptr T, mem: AtomMemModel) {.
  43. importc: "__atomic_load", nodecl.}
  44. ## This is the generic version of an atomic load. It returns the contents at p in ret.
  45. proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel) {.
  46. importc: "__atomic_store_n", nodecl.}
  47. ## This proc implements an atomic store operation. It writes val at p.
  48. ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, and ATOMIC_RELEASE.
  49. proc atomicStore*[T: AtomType](p, val: ptr T, mem: AtomMemModel) {.
  50. importc: "__atomic_store", nodecl.}
  51. ## This is the generic version of an atomic store. It stores the value of val at p
  52. proc atomicExchangeN*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  53. importc: "__atomic_exchange_n", nodecl.}
  54. ## This proc implements an atomic exchange operation. It writes val at p,
  55. ## and returns the previous contents at p.
  56. ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_ACQUIRE, ATOMIC_RELEASE, ATOMIC_ACQ_REL
  57. proc atomicExchange*[T: AtomType](p, val, ret: ptr T, mem: AtomMemModel) {.
  58. importc: "__atomic_exchange", nodecl.}
  59. ## This is the generic version of an atomic exchange. It stores the contents at val at p.
  60. ## The original value at p is copied into ret.
  61. proc atomicCompareExchangeN*[T: AtomType](p, expected: ptr T, desired: T,
  62. weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
  63. importc: "__atomic_compare_exchange_n ", nodecl.}
  64. ## This proc implements an atomic compare and exchange operation. This compares the
  65. ## contents at p with the contents at expected and if equal, writes desired at p.
  66. ## If they are not equal, the current contents at p is written into expected.
  67. ## Weak is true for weak compare_exchange, and false for the strong variation.
  68. ## Many targets only offer the strong variation and ignore the parameter.
  69. ## When in doubt, use the strong variation.
  70. ## True is returned if desired is written at p and the execution is considered
  71. ## to conform to the memory model specified by success_memmodel. There are no
  72. ## restrictions on what memory model can be used here. False is returned otherwise,
  73. ## and the execution is considered to conform to failure_memmodel. This memory model
  74. ## cannot be __ATOMIC_RELEASE nor __ATOMIC_ACQ_REL. It also cannot be a stronger model
  75. ## than that specified by success_memmodel.
  76. proc atomicCompareExchange*[T: AtomType](p, expected, desired: ptr T,
  77. weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {.
  78. importc: "__atomic_compare_exchange", nodecl.}
  79. ## This proc implements the generic version of atomic_compare_exchange.
  80. ## The proc is virtually identical to atomic_compare_exchange_n, except the desired
  81. ## value is also a pointer.
  82. ## Perform the operation return the new value, all memory models are valid
  83. proc atomicAddFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  84. importc: "__atomic_add_fetch", nodecl.}
  85. proc atomicSubFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  86. importc: "__atomic_sub_fetch", nodecl.}
  87. proc atomicOrFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  88. importc: "__atomic_or_fetch ", nodecl.}
  89. proc atomicAndFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  90. importc: "__atomic_and_fetch", nodecl.}
  91. proc atomicXorFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  92. importc: "__atomic_xor_fetch", nodecl.}
  93. proc atomicNandFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  94. importc: "__atomic_nand_fetch ", nodecl.}
  95. ## Perform the operation return the old value, all memory models are valid
  96. proc atomicFetchAdd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  97. importc: "__atomic_fetch_add", nodecl.}
  98. proc atomicFetchSub*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  99. importc: "__atomic_fetch_sub", nodecl.}
  100. proc atomicFetchOr*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  101. importc: "__atomic_fetch_or", nodecl.}
  102. proc atomicFetchAnd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  103. importc: "__atomic_fetch_and", nodecl.}
  104. proc atomicFetchXor*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  105. importc: "__atomic_fetch_xor", nodecl.}
  106. proc atomicFetchNand*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {.
  107. importc: "__atomic_fetch_nand", nodecl.}
  108. proc atomicTestAndSet*(p: pointer, mem: AtomMemModel): bool {.
  109. importc: "__atomic_test_and_set", nodecl.}
  110. ## This built-in function performs an atomic test-and-set operation on the byte at p.
  111. ## The byte is set to some implementation defined nonzero “set” value and the return
  112. ## value is true if and only if the previous contents were “set”.
  113. ## All memory models are valid.
  114. proc atomicClear*(p: pointer, mem: AtomMemModel) {.
  115. importc: "__atomic_clear", nodecl.}
  116. ## This built-in function performs an atomic clear operation at p.
  117. ## After the operation, at p contains 0.
  118. ## ATOMIC_RELAXED, ATOMIC_SEQ_CST, ATOMIC_RELEASE
  119. proc atomicThreadFence*(mem: AtomMemModel) {.
  120. importc: "__atomic_thread_fence", nodecl.}
  121. ## This built-in function acts as a synchronization fence between threads based
  122. ## on the specified memory model. All memory orders are valid.
  123. proc atomicSignalFence*(mem: AtomMemModel) {.
  124. importc: "__atomic_signal_fence", nodecl.}
  125. ## This built-in function acts as a synchronization fence between a thread and
  126. ## signal handlers based in the same thread. All memory orders are valid.
  127. proc atomicAlwaysLockFree*(size: int, p: pointer): bool {.
  128. importc: "__atomic_always_lock_free", nodecl.}
  129. ## This built-in function returns true if objects of size bytes always generate
  130. ## lock free atomic instructions for the target architecture. size must resolve
  131. ## to a compile-time constant and the result also resolves to a compile-time constant.
  132. ## ptr is an optional pointer to the object that may be used to determine alignment.
  133. ## A value of 0 indicates typical alignment should be used. The compiler may also
  134. ## ignore this parameter.
  135. proc atomicIsLockFree*(size: int, p: pointer): bool {.
  136. importc: "__atomic_is_lock_free", nodecl.}
  137. ## This built-in function returns true if objects of size bytes always generate
  138. ## lock free atomic instructions for the target architecture. If it is not known
  139. ## to be lock free a call is made to a runtime routine named __atomic_is_lock_free.
  140. ## ptr is an optional pointer to the object that may be used to determine alignment.
  141. ## A value of 0 indicates typical alignment should be used. The compiler may also
  142. ## ignore this parameter.
  143. template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
  144. elif defined(vcc) and hasThreadSupport:
  145. when defined(cpp):
  146. when sizeof(int) == 8:
  147. proc addAndFetch*(p: ptr int, val: int): int {.
  148. importcpp: "_InterlockedExchangeAdd64(static_cast<NI volatile *>(#), #)",
  149. header: "<intrin.h>".}
  150. else:
  151. proc addAndFetch*(p: ptr int, val: int): int {.
  152. importcpp: "_InterlockedExchangeAdd(reinterpret_cast<long volatile *>(#), static_cast<long>(#))",
  153. header: "<intrin.h>".}
  154. else:
  155. when sizeof(int) == 8:
  156. proc addAndFetch*(p: ptr int, val: int): int {.
  157. importc: "_InterlockedExchangeAdd64", header: "<intrin.h>".}
  158. else:
  159. proc addAndFetch*(p: ptr int, val: int): int {.
  160. importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
  161. proc fence*() {.importc: "_ReadWriteBarrier", header: "<intrin.h>".}
  162. else:
  163. proc addAndFetch*(p: ptr int, val: int): int {.inline.} =
  164. inc(p[], val)
  165. result = p[]
  166. proc atomicInc*(memLoc: var int, x: int = 1): int =
  167. when someGcc and hasThreadSupport:
  168. result = atomic_add_fetch(memLoc.addr, x, ATOMIC_RELAXED)
  169. elif defined(vcc) and hasThreadSupport:
  170. result = addAndFetch(memLoc.addr, x)
  171. inc(result, x)
  172. else:
  173. inc(memLoc, x)
  174. result = memLoc
  175. proc atomicDec*(memLoc: var int, x: int = 1): int =
  176. when someGcc and hasThreadSupport:
  177. when declared(atomic_sub_fetch):
  178. result = atomic_sub_fetch(memLoc.addr, x, ATOMIC_RELAXED)
  179. else:
  180. result = atomic_add_fetch(memLoc.addr, -x, ATOMIC_RELAXED)
  181. elif defined(vcc) and hasThreadSupport:
  182. result = addAndFetch(memLoc.addr, -x)
  183. dec(result, x)
  184. else:
  185. dec(memLoc, x)
  186. result = memLoc
  187. when defined(vcc):
  188. when defined(cpp):
  189. proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
  190. {.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
  191. proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
  192. {.importcpp: "_InterlockedCompareExchange(static_cast<NI volatile *>(#), #, #)", header: "<intrin.h>".}
  193. proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
  194. {.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".}
  195. else:
  196. proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
  197. {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
  198. proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
  199. {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
  200. proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
  201. {.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
  202. proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
  203. when sizeof(T) == 8:
  204. interlockedCompareExchange64(p, cast[int64](newValue), cast[int64](oldValue)) ==
  205. cast[int64](oldValue)
  206. elif sizeof(T) == 4:
  207. interlockedCompareExchange32(p, cast[int32](newValue), cast[int32](oldValue)) ==
  208. cast[int32](oldValue)
  209. elif sizeof(T) == 1:
  210. interlockedCompareExchange8(p, cast[byte](newValue), cast[byte](oldValue)) ==
  211. cast[byte](oldValue)
  212. else:
  213. {.error: "invalid CAS instruction".}
  214. elif defined(tcc):
  215. when defined(amd64):
  216. {.emit:"""
  217. static int __tcc_cas(int *ptr, int oldVal, int newVal)
  218. {
  219. unsigned char ret;
  220. __asm__ __volatile__ (
  221. " lock\n"
  222. " cmpxchgq %2,%1\n"
  223. " sete %0\n"
  224. : "=q" (ret), "=m" (*ptr)
  225. : "r" (newVal), "m" (*ptr), "a" (oldVal)
  226. : "memory");
  227. if (ret)
  228. return 0;
  229. else
  230. return 1;
  231. }
  232. """.}
  233. else:
  234. #assert sizeof(int) == 4
  235. {.emit:"""
  236. static int __tcc_cas(int *ptr, int oldVal, int newVal)
  237. {
  238. unsigned char ret;
  239. __asm__ __volatile__ (
  240. " lock\n"
  241. " cmpxchgl %2,%1\n"
  242. " sete %0\n"
  243. : "=q" (ret), "=m" (*ptr)
  244. : "r" (newVal), "m" (*ptr), "a" (oldVal)
  245. : "memory");
  246. if (ret)
  247. return 0;
  248. else
  249. return 1;
  250. }
  251. """.}
  252. proc tcc_cas(p: ptr int; oldValue, newValue: int): bool
  253. {.importc: "__tcc_cas", nodecl.}
  254. proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
  255. tcc_cas(cast[ptr int](p), cast[int](oldValue), cast[int](newValue))
  256. elif declared(atomicCompareExchangeN):
  257. proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
  258. atomicCompareExchangeN(p, oldValue.unsafeAddr, newValue, false, ATOMIC_SEQ_CST, ATOMIC_SEQ_CST)
  259. else:
  260. # this is valid for GCC and Intel C++
  261. proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool
  262. {.importc: "__sync_bool_compare_and_swap", nodecl.}
  263. # XXX is this valid for 'int'?
  264. when (defined(x86) or defined(amd64)) and defined(vcc):
  265. proc cpuRelax* {.importc: "YieldProcessor", header: "<windows.h>".}
  266. elif (defined(x86) or defined(amd64)) and (someGcc or defined(bcc)):
  267. proc cpuRelax* {.inline.} =
  268. {.emit: """asm volatile("pause" ::: "memory");""".}
  269. elif someGcc or defined(tcc):
  270. proc cpuRelax* {.inline.} =
  271. {.emit: """asm volatile("" ::: "memory");""".}
  272. elif defined(icl):
  273. proc cpuRelax* {.importc: "_mm_pause", header: "xmmintrin.h".}
  274. elif false:
  275. from os import sleep
  276. proc cpuRelax* {.inline.} = os.sleep(1)
  277. when not declared(fence) and hasThreadSupport:
  278. # XXX fixme
  279. proc fence*() {.inline.} =
  280. var dummy: bool
  281. discard cas(addr dummy, false, true)
  282. {.pop.}