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