atomics.nim 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2018 Jörg Wollenschläger
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Types and operations for atomic operations and lockless algorithms.
  10. ##
  11. ## Unstable API.
  12. runnableExamples:
  13. # Atomic
  14. var loc: Atomic[int]
  15. loc.store(4)
  16. assert loc.load == 4
  17. loc.store(2)
  18. assert loc.load(moRelaxed) == 2
  19. loc.store(9)
  20. assert loc.load(moAcquire) == 9
  21. loc.store(0, moRelease)
  22. assert loc.load == 0
  23. assert loc.exchange(7) == 0
  24. assert loc.load == 7
  25. var expected = 7
  26. assert loc.compareExchange(expected, 5, moRelaxed, moRelaxed)
  27. assert expected == 7
  28. assert loc.load == 5
  29. assert not loc.compareExchange(expected, 12, moRelaxed, moRelaxed)
  30. assert expected == 5
  31. assert loc.load == 5
  32. assert loc.fetchAdd(1) == 5
  33. assert loc.fetchAdd(2) == 6
  34. assert loc.fetchSub(3) == 8
  35. loc.atomicInc(1)
  36. assert loc.load == 6
  37. # AtomicFlag
  38. var flag: AtomicFlag
  39. assert not flag.testAndSet
  40. assert flag.testAndSet
  41. flag.clear(moRelaxed)
  42. assert not flag.testAndSet
  43. when defined(cpp) or defined(nimdoc):
  44. # For the C++ backend, types and operations map directly to C++11 atomics.
  45. {.push, header: "<atomic>".}
  46. type
  47. MemoryOrder* {.importcpp: "std::memory_order".} = enum
  48. ## Specifies how non-atomic operations can be reordered around atomic
  49. ## operations.
  50. moRelaxed
  51. ## No ordering constraints. Only the atomicity and ordering against
  52. ## other atomic operations is guaranteed.
  53. moConsume
  54. ## This ordering is currently discouraged as it's semantics are
  55. ## being revised. Acquire operations should be preferred.
  56. moAcquire
  57. ## When applied to a load operation, no reads or writes in the
  58. ## current thread can be reordered before this operation.
  59. moRelease
  60. ## When applied to a store operation, no reads or writes in the
  61. ## current thread can be reorderd after this operation.
  62. moAcquireRelease
  63. ## When applied to a read-modify-write operation, this behaves like
  64. ## both an acquire and a release operation.
  65. moSequentiallyConsistent
  66. ## Behaves like Acquire when applied to load, like Release when
  67. ## applied to a store and like AcquireRelease when applied to a
  68. ## read-modify-write operation.
  69. ## Also guarantees that all threads observe the same total ordering
  70. ## with other moSequentiallyConsistent operations.
  71. type
  72. Atomic*[T] {.importcpp: "std::atomic", completeStruct.} = object
  73. ## An atomic object with underlying type `T`.
  74. raw: T
  75. AtomicFlag* {.importcpp: "std::atomic_flag", size: 1.} = object
  76. ## An atomic boolean state.
  77. # Access operations
  78. proc load*[T](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.load(@)".}
  79. ## Atomically obtains the value of the atomic object.
  80. proc store*[T](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importcpp: "#.store(@)".}
  81. ## Atomically replaces the value of the atomic object with the `desired`
  82. ## value.
  83. proc exchange*[T](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.exchange(@)".}
  84. ## Atomically replaces the value of the atomic object with the `desired`
  85. ## value and returns the old value.
  86. proc compareExchange*[T](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.importcpp: "#.compare_exchange_strong(@)".}
  87. ## Atomically compares the value of the atomic object with the `expected`
  88. ## value and performs exchange with the `desired` one if equal or load if
  89. ## not. Returns true if the exchange was successful.
  90. proc compareExchange*[T](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.importcpp: "#.compare_exchange_strong(@)".}
  91. ## Same as above, but allows for different memory orders for success and
  92. ## failure.
  93. proc compareExchangeWeak*[T](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.importcpp: "#.compare_exchange_weak(@)".}
  94. ## Same as above, but is allowed to fail spuriously.
  95. proc compareExchangeWeak*[T](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.importcpp: "#.compare_exchange_weak(@)".}
  96. ## Same as above, but allows for different memory orders for success and
  97. ## failure.
  98. # Numerical operations
  99. proc fetchAdd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_add(@)".}
  100. ## Atomically adds a `value` to the atomic integer and returns the
  101. ## original value.
  102. proc fetchSub*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_sub(@)".}
  103. ## Atomically subtracts a `value` to the atomic integer and returns the
  104. ## original value.
  105. proc fetchAnd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_and(@)".}
  106. ## Atomically replaces the atomic integer with it's bitwise AND
  107. ## with the specified `value` and returns the original value.
  108. proc fetchOr*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_or(@)".}
  109. ## Atomically replaces the atomic integer with it's bitwise OR
  110. ## with the specified `value` and returns the original value.
  111. proc fetchXor*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importcpp: "#.fetch_xor(@)".}
  112. ## Atomically replaces the atomic integer with it's bitwise XOR
  113. ## with the specified `value` and returns the original value.
  114. # Flag operations
  115. proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importcpp: "#.test_and_set(@)".}
  116. ## Atomically sets the atomic flag to true and returns the original value.
  117. proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importcpp: "#.clear(@)".}
  118. ## Atomically sets the value of the atomic flag to false.
  119. proc fence*(order: MemoryOrder) {.importcpp: "std::atomic_thread_fence(@)".}
  120. ## Ensures memory ordering without using atomic operations.
  121. proc signalFence*(order: MemoryOrder) {.importcpp: "std::atomic_signal_fence(@)".}
  122. ## Prevents reordering of accesses by the compiler as would fence, but
  123. ## inserts no CPU instructions for memory ordering.
  124. {.pop.}
  125. else:
  126. # For the C backend, atomics map to C11 built-ins on GCC and Clang for
  127. # trivial Nim types. Other types are implemented using spin locks.
  128. # This could be overcome by supporting advanced importc-patterns.
  129. # Since MSVC does not implement C11, we fall back to MS intrinsics
  130. # where available.
  131. type
  132. Trivial = SomeNumber | bool | enum | ptr | pointer
  133. # A type that is known to be atomic and whose size is known at
  134. # compile time to be 8 bytes or less
  135. template nonAtomicType*(T: typedesc[Trivial]): untyped =
  136. # Maps types to integers of the same size
  137. when sizeof(T) == 1: int8
  138. elif sizeof(T) == 2: int16
  139. elif sizeof(T) == 4: int32
  140. elif sizeof(T) == 8: int64
  141. when defined(vcc):
  142. # TODO: Trivial types should be volatile and use VC's special volatile
  143. # semantics for store and loads.
  144. type
  145. MemoryOrder* = enum
  146. moRelaxed
  147. moConsume
  148. moAcquire
  149. moRelease
  150. moAcquireRelease
  151. moSequentiallyConsistent
  152. Atomic*[T] = object
  153. when T is Trivial:
  154. value: T.nonAtomicType
  155. else:
  156. nonAtomicValue: T
  157. guard: AtomicFlag
  158. AtomicFlag* = distinct int8
  159. {.push header: "<intrin.h>".}
  160. # MSVC intrinsics
  161. proc interlockedExchange(location: pointer; desired: int8): int8 {.importc: "_InterlockedExchange8".}
  162. proc interlockedExchange(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange16".}
  163. proc interlockedExchange(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange".}
  164. proc interlockedExchange(location: pointer; desired: int64): int64 {.importc: "_InterlockedExchange64".}
  165. proc interlockedCompareExchange(location: pointer; desired, expected: int8): int8 {.importc: "_InterlockedCompareExchange8".}
  166. proc interlockedCompareExchange(location: pointer; desired, expected: int16): int16 {.importc: "_InterlockedCompareExchange16".}
  167. proc interlockedCompareExchange(location: pointer; desired, expected: int32): int32 {.importc: "_InterlockedCompareExchange".}
  168. proc interlockedCompareExchange(location: pointer; desired, expected: int64): int64 {.importc: "_InterlockedCompareExchange64".}
  169. proc interlockedAnd(location: pointer; value: int8): int8 {.importc: "_InterlockedAnd8".}
  170. proc interlockedAnd(location: pointer; value: int16): int16 {.importc: "_InterlockedAnd16".}
  171. proc interlockedAnd(location: pointer; value: int32): int32 {.importc: "_InterlockedAnd".}
  172. proc interlockedAnd(location: pointer; value: int64): int64 {.importc: "_InterlockedAnd64".}
  173. proc interlockedOr(location: pointer; value: int8): int8 {.importc: "_InterlockedOr8".}
  174. proc interlockedOr(location: pointer; value: int16): int16 {.importc: "_InterlockedOr16".}
  175. proc interlockedOr(location: pointer; value: int32): int32 {.importc: "_InterlockedOr".}
  176. proc interlockedOr(location: pointer; value: int64): int64 {.importc: "_InterlockedOr64".}
  177. proc interlockedXor(location: pointer; value: int8): int8 {.importc: "_InterlockedXor8".}
  178. proc interlockedXor(location: pointer; value: int16): int16 {.importc: "_InterlockedXor16".}
  179. proc interlockedXor(location: pointer; value: int32): int32 {.importc: "_InterlockedXor".}
  180. proc interlockedXor(location: pointer; value: int64): int64 {.importc: "_InterlockedXor64".}
  181. proc fence(order: MemoryOrder): int64 {.importc: "_ReadWriteBarrier()".}
  182. proc signalFence(order: MemoryOrder): int64 {.importc: "_ReadWriteBarrier()".}
  183. {.pop.}
  184. proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool =
  185. interlockedOr(addr(location), 1'i8) == 1'i8
  186. proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) =
  187. discard interlockedAnd(addr(location), 0'i8)
  188. proc load*[T: Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  189. cast[T](interlockedOr(addr(location.value), (nonAtomicType(T))0))
  190. proc store*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} =
  191. discard interlockedExchange(addr(location.value), cast[nonAtomicType(T)](desired))
  192. proc exchange*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  193. cast[T](interlockedExchange(addr(location.value), cast[int64](desired)))
  194. proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
  195. cast[T](interlockedCompareExchange(addr(location.value), cast[nonAtomicType(T)](desired), cast[nonAtomicType(T)](expected))) == expected
  196. proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
  197. compareExchange(location, expected, desired, order, order)
  198. proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
  199. compareExchange(location, expected, desired, success, failure)
  200. proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
  201. compareExchangeWeak(location, expected, desired, order, order)
  202. proc fetchAdd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  203. var currentValue = location.load()
  204. while not compareExchangeWeak(location, currentValue, currentValue + value): discard
  205. proc fetchSub*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  206. fetchAdd(location, -value, order)
  207. proc fetchAnd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  208. cast[T](interlockedAnd(addr(location.value), cast[nonAtomicType(T)](value)))
  209. proc fetchOr*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  210. cast[T](interlockedOr(addr(location.value), cast[nonAtomicType(T)](value)))
  211. proc fetchXor*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  212. cast[T](interlockedXor(addr(location.value), cast[nonAtomicType(T)](value)))
  213. else:
  214. {.push, header: "<stdatomic.h>".}
  215. type
  216. MemoryOrder* {.importc: "memory_order".} = enum
  217. moRelaxed
  218. moConsume
  219. moAcquire
  220. moRelease
  221. moAcquireRelease
  222. moSequentiallyConsistent
  223. type
  224. # Atomic*[T] {.importcpp: "_Atomic('0)".} = object
  225. AtomicInt8 {.importc: "_Atomic NI8".} = int8
  226. AtomicInt16 {.importc: "_Atomic NI16".} = int16
  227. AtomicInt32 {.importc: "_Atomic NI32".} = int32
  228. AtomicInt64 {.importc: "_Atomic NI64".} = int64
  229. type
  230. AtomicFlag* {.importc: "atomic_flag", size: 1.} = object
  231. Atomic*[T] = object
  232. when T is Trivial:
  233. # Maps the size of a trivial type to it's internal atomic type
  234. when sizeof(T) == 1: value: AtomicInt8
  235. elif sizeof(T) == 2: value: AtomicInt16
  236. elif sizeof(T) == 4: value: AtomicInt32
  237. elif sizeof(T) == 8: value: AtomicInt64
  238. else:
  239. nonAtomicValue: T
  240. guard: AtomicFlag
  241. #proc init*[T](location: var Atomic[T]; value: T): T {.importcpp: "atomic_init(@)".}
  242. proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc.}
  243. proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc.}
  244. proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
  245. proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
  246. proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
  247. # Numerical operations
  248. proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
  249. proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
  250. proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
  251. proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
  252. proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
  253. # Flag operations
  254. # var ATOMIC_FLAG_INIT {.importc, nodecl.}: AtomicFlag
  255. # proc init*(location: var AtomicFlag) {.inline.} = location = ATOMIC_FLAG_INIT
  256. proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".}
  257. proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".}
  258. proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".}
  259. proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".}
  260. {.pop.}
  261. proc load*[T: Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  262. cast[T](atomic_load_explicit[nonAtomicType(T), typeof(location.value)](addr(location.value), order))
  263. proc store*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} =
  264. atomic_store_explicit(addr(location.value), cast[nonAtomicType(T)](desired), order)
  265. proc exchange*[T: Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  266. cast[T](atomic_exchange_explicit(addr(location.value), cast[nonAtomicType(T)](desired), order))
  267. proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
  268. atomic_compare_exchange_strong_explicit(addr(location.value), cast[ptr nonAtomicType(T)](addr(expected)), cast[nonAtomicType(T)](desired), success, failure)
  269. proc compareExchange*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
  270. compareExchange(location, expected, desired, order, order)
  271. proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
  272. atomic_compare_exchange_weak_explicit(addr(location.value), cast[ptr nonAtomicType(T)](addr(expected)), cast[nonAtomicType(T)](desired), success, failure)
  273. proc compareExchangeWeak*[T: Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
  274. compareExchangeWeak(location, expected, desired, order, order)
  275. # Numerical operations
  276. proc fetchAdd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  277. cast[T](atomic_fetch_add_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
  278. proc fetchSub*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  279. cast[T](atomic_fetch_sub_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
  280. proc fetchAnd*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  281. cast[T](atomic_fetch_and_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
  282. proc fetchOr*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  283. cast[T](atomic_fetch_or_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
  284. proc fetchXor*[T: SomeInteger](location: var Atomic[T]; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  285. cast[T](atomic_fetch_xor_explicit(addr(location.value), cast[nonAtomicType(T)](value), order))
  286. template withLock[T: not Trivial](location: var Atomic[T]; order: MemoryOrder; body: untyped): untyped =
  287. while testAndSet(location.guard, moAcquire): discard
  288. try:
  289. body
  290. finally:
  291. clear(location.guard, moRelease)
  292. proc load*[T: not Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  293. withLock(location, order):
  294. result = location.nonAtomicValue
  295. proc store*[T: not Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.inline.} =
  296. withLock(location, order):
  297. location.nonAtomicValue = desired
  298. proc exchange*[T: not Trivial](location: var Atomic[T]; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} =
  299. withLock(location, order):
  300. result = location.nonAtomicValue
  301. location.nonAtomicValue = desired
  302. proc compareExchange*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
  303. withLock(location, success):
  304. if location.nonAtomicValue != expected:
  305. expected = location.nonAtomicValue
  306. return false
  307. expected = desired
  308. swap(location.nonAtomicValue, expected)
  309. return true
  310. proc compareExchangeWeak*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} =
  311. compareExchange(location, expected, desired, success, failure)
  312. proc compareExchange*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
  313. compareExchange(location, expected, desired, order, order)
  314. proc compareExchangeWeak*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} =
  315. compareExchangeWeak(location, expected, desired, order, order)
  316. proc atomicInc*[T: SomeInteger](location: var Atomic[T]; value: T = 1) {.inline.} =
  317. ## Atomically increments the atomic integer by some `value`.
  318. discard location.fetchAdd(value)
  319. proc atomicDec*[T: SomeInteger](location: var Atomic[T]; value: T = 1) {.inline.} =
  320. ## Atomically decrements the atomic integer by some `value`.
  321. discard location.fetchSub(value)
  322. proc `+=`*[T: SomeInteger](location: var Atomic[T]; value: T) {.inline.} =
  323. ## Atomically increments the atomic integer by some `value`.
  324. discard location.fetchAdd(value)
  325. proc `-=`*[T: SomeInteger](location: var Atomic[T]; value: T) {.inline.} =
  326. ## Atomically decrements the atomic integer by some `value`.
  327. discard location.fetchSub(value)