when (NimMajor,NimMinor,NimPatch) >= (0,20,0): import std/concurrency/atomics export atomics else: # Backport of Nim 0.20 atomics to earlier versions # # # 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. when defined(cpp) or defined(nimdoc): # For the C++ backend, types and operations map directly to C++11 atomics. {.push, header: "".} 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 garantees that all threads observe the same total ordering ## with other moSequentiallyConsistent operations. type Atomic* {.importcpp: "std::atomic".} [T] = object ## An atomic object with underlying type `T`. AtomicFlag* {.importcpp: "std::atomic_flag".} = 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 | 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: "".} # 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: "".} type MemoryOrder* {.importc: "memory_order".} = enum moRelaxed moConsume moAcquire moRelease moAcquireRelease moSequentiallyConsistent type # Atomic* {.importcpp: "_Atomic('0)".} [T] = object AtomicInt8 {.importc: "_Atomic NI8".} = object AtomicInt16 {.importc: "_Atomic NI16".} = object AtomicInt32 {.importc: "_Atomic NI32".} = object AtomicInt64 {.importc: "_Atomic NI64".} = 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".} = 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 body 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: return false 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.} = withLock(location, success): if location.nonAtomicValue != expected: return false swap(location.nonAtomicValue, expected) return true 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)