123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 |
- #
- #
- # 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.
- runnableExamples:
- # Atomic
- var loc: Atomic[int]
- loc.store(4)
- assert loc.load == 4
- loc.store(2)
- assert loc.load(moRelaxed) == 2
- loc.store(9)
- assert loc.load(moAcquire) == 9
- loc.store(0, moRelease)
- assert loc.load == 0
- assert loc.exchange(7) == 0
- assert loc.load == 7
- var expected = 7
- assert loc.compareExchange(expected, 5, moRelaxed, moRelaxed)
- assert expected == 7
- assert loc.load == 5
- assert not loc.compareExchange(expected, 12, moRelaxed, moRelaxed)
- assert expected == 5
- assert loc.load == 5
- assert loc.fetchAdd(1) == 5
- assert loc.fetchAdd(2) == 6
- assert loc.fetchSub(3) == 8
- loc.atomicInc(1)
- assert loc.load == 6
- # AtomicFlag
- var flag: AtomicFlag
- assert not flag.testAndSet
- assert flag.testAndSet
- flag.clear(moRelaxed)
- assert not flag.testAndSet
- 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: "_InterlockedExchange16".}
- proc interlockedExchange(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange".}
- 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*[T] {.importcpp: "_Atomic('0)".} = object
- AtomicInt8 {.importc: "_Atomic NI8".} = int8
- AtomicInt16 {.importc: "_Atomic NI16".} = int16
- AtomicInt32 {.importc: "_Atomic NI32".} = int32
- AtomicInt64 {.importc: "_Atomic NI64".} = int64
- type
- AtomicFlag* {.importc: "atomic_flag", size: 1.} = object
- Atomic*[T] = object
- when T is Trivial:
- # Maps the size of a trivial type to it's internal atomic type
- when sizeof(T) == 1: value: AtomicInt8
- elif sizeof(T) == 2: value: AtomicInt16
- elif sizeof(T) == 4: value: AtomicInt32
- elif sizeof(T) == 8: value: AtomicInt64
- 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), typeof(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 testAndSet(location.guard, moAcquire): discard
- try:
- body
- finally:
- clear(location.guard, 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)
|