|  | /** | 
|  | * The atomic module provides basic support for lock-free | 
|  | * concurrent programming. | 
|  | * | 
|  | * Copyright: Copyright Sean Kelly 2005 - 2016. | 
|  | * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | 
|  | * Authors:   Sean Kelly, Alex Rønne Petersen, Manu Evans | 
|  | * Source:    $(DRUNTIMESRC core/_atomic.d) | 
|  | */ | 
|  |  | 
|  | module core.atomic; | 
|  |  | 
|  | import core.internal.atomic; | 
|  | import core.internal.attributes : betterC; | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  |  | 
|  | /** | 
|  | * Specifies the memory ordering semantics of an atomic operation. | 
|  | * | 
|  | * See_Also: | 
|  | *     $(HTTP en.cppreference.com/w/cpp/atomic/memory_order) | 
|  | */ | 
|  | enum MemoryOrder | 
|  | { | 
|  | /** | 
|  | * Not sequenced. | 
|  | * Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#monotonic, LLVM AtomicOrdering.Monotonic) | 
|  | * and C++11/C11 `memory_order_relaxed`. | 
|  | */ | 
|  | raw = 0, | 
|  | /** | 
|  | * Hoist-load + hoist-store barrier. | 
|  | * Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#acquire, LLVM AtomicOrdering.Acquire) | 
|  | * and C++11/C11 `memory_order_acquire`. | 
|  | */ | 
|  | acq = 2, | 
|  | /** | 
|  | * Sink-load + sink-store barrier. | 
|  | * Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#release, LLVM AtomicOrdering.Release) | 
|  | * and C++11/C11 `memory_order_release`. | 
|  | */ | 
|  | rel = 3, | 
|  | /** | 
|  | * Acquire + release barrier. | 
|  | * Corresponds to $(LINK2 https://llvm.org/docs/Atomics.html#acquirerelease, LLVM AtomicOrdering.AcquireRelease) | 
|  | * and C++11/C11 `memory_order_acq_rel`. | 
|  | */ | 
|  | acq_rel = 4, | 
|  | /** | 
|  | * Fully sequenced (acquire + release). Corresponds to | 
|  | * $(LINK2 https://llvm.org/docs/Atomics.html#sequentiallyconsistent, LLVM AtomicOrdering.SequentiallyConsistent) | 
|  | * and C++11/C11 `memory_order_seq_cst`. | 
|  | */ | 
|  | seq = 5, | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Loads 'val' from memory and returns it.  The memory barrier specified | 
|  | * by 'ms' is applied to the operation, which is fully sequenced by | 
|  | * default.  Valid memory orders are MemoryOrder.raw, MemoryOrder.acq, | 
|  | * and MemoryOrder.seq. | 
|  | * | 
|  | * Params: | 
|  | *  val = The target variable. | 
|  | * | 
|  | * Returns: | 
|  | *  The value of 'val'. | 
|  | */ | 
|  | T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope const T val) pure nothrow @nogc @trusted | 
|  | if (!is(T == shared U, U) && !is(T == shared inout U, U) && !is(T == shared const U, U)) | 
|  | { | 
|  | static if (__traits(isFloating, T)) | 
|  | { | 
|  | alias IntTy = IntForFloat!T; | 
|  | IntTy r = core.internal.atomic.atomicLoad!ms(cast(IntTy*)&val); | 
|  | return *cast(T*)&r; | 
|  | } | 
|  | else | 
|  | return core.internal.atomic.atomicLoad!ms(cast(T*)&val); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope shared const T val) pure nothrow @nogc @trusted | 
|  | if (!hasUnsharedIndirections!T) | 
|  | { | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  | static assert(!hasUnsharedIndirections!T, "Copying `" ~ shared(const(T)).stringof ~ "` would violate shared."); | 
|  |  | 
|  | return atomicLoad!ms(*cast(T*)&val); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | TailShared!T atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(ref shared const T val) pure nothrow @nogc @trusted | 
|  | if (hasUnsharedIndirections!T) | 
|  | { | 
|  | // HACK: DEPRECATE THIS FUNCTION, IT IS INVALID TO DO ATOMIC LOAD OF SHARED CLASS | 
|  | // this is here because code exists in the wild that does this... | 
|  |  | 
|  | return core.internal.atomic.atomicLoad!ms(cast(TailShared!T*)&val); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Writes 'newval' into 'val'.  The memory barrier specified by 'ms' is | 
|  | * applied to the operation, which is fully sequenced by default. | 
|  | * Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and | 
|  | * MemoryOrder.seq. | 
|  | * | 
|  | * Params: | 
|  | *  val    = The target variable. | 
|  | *  newval = The value to store. | 
|  | */ | 
|  | void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) pure nothrow @nogc @trusted | 
|  | if (!is(T == shared) && !is(V == shared)) | 
|  | { | 
|  | import core.internal.traits : hasElaborateCopyConstructor; | 
|  | static assert (!hasElaborateCopyConstructor!T, "`T` may not have an elaborate copy: atomic operations override regular copying semantics."); | 
|  |  | 
|  | // resolve implicit conversions | 
|  | T arg = newval; | 
|  |  | 
|  | static if (__traits(isFloating, T)) | 
|  | { | 
|  | alias IntTy = IntForFloat!T; | 
|  | core.internal.atomic.atomicStore!ms(cast(IntTy*)&val, *cast(IntTy*)&arg); | 
|  | } | 
|  | else | 
|  | core.internal.atomic.atomicStore!ms(&val, arg); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref shared T val, V newval) pure nothrow @nogc @trusted | 
|  | if (!is(T == class)) | 
|  | { | 
|  | static if (is (V == shared U, U)) | 
|  | alias Thunk = U; | 
|  | else | 
|  | { | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  | static assert(!hasUnsharedIndirections!V, "Copying argument `" ~ V.stringof ~ " newval` to `" ~ shared(T).stringof ~ " here` would violate shared."); | 
|  | alias Thunk = V; | 
|  | } | 
|  | atomicStore!ms(*cast(T*)&val, *cast(Thunk*)&newval); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | void atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref shared T val, shared V newval) pure nothrow @nogc @trusted | 
|  | if (is(T == class)) | 
|  | { | 
|  | static assert (is (V : T), "Can't assign `newval` of type `shared " ~ V.stringof ~ "` to `shared " ~ T.stringof ~ "`."); | 
|  |  | 
|  | core.internal.atomic.atomicStore!ms(cast(T*)&val, cast(V)newval); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Atomically adds `mod` to the value referenced by `val` and returns the value `val` held previously. | 
|  | * This operation is both lock-free and atomic. | 
|  | * | 
|  | * Params: | 
|  | *  val = Reference to the value to modify. | 
|  | *  mod = The value to add. | 
|  | * | 
|  | * Returns: | 
|  | *  The value held previously by `val`. | 
|  | */ | 
|  | T atomicFetchAdd(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope T val, size_t mod) pure nothrow @nogc @trusted | 
|  | if ((__traits(isIntegral, T) || is(T == U*, U)) && !is(T == shared)) | 
|  | in (atomicValueIsProperlyAligned(val)) | 
|  | { | 
|  | static if (is(T == U*, U)) | 
|  | return cast(T)core.internal.atomic.atomicFetchAdd!ms(cast(size_t*)&val, mod * U.sizeof); | 
|  | else | 
|  | return core.internal.atomic.atomicFetchAdd!ms(&val, cast(T)mod); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | T atomicFetchAdd(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope shared T val, size_t mod) pure nothrow @nogc @trusted | 
|  | if (__traits(isIntegral, T) || is(T == U*, U)) | 
|  | in (atomicValueIsProperlyAligned(val)) | 
|  | { | 
|  | return atomicFetchAdd!ms(*cast(T*)&val, mod); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Atomically subtracts `mod` from the value referenced by `val` and returns the value `val` held previously. | 
|  | * This operation is both lock-free and atomic. | 
|  | * | 
|  | * Params: | 
|  | *  val = Reference to the value to modify. | 
|  | *  mod = The value to subtract. | 
|  | * | 
|  | * Returns: | 
|  | *  The value held previously by `val`. | 
|  | */ | 
|  | T atomicFetchSub(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope T val, size_t mod) pure nothrow @nogc @trusted | 
|  | if ((__traits(isIntegral, T) || is(T == U*, U)) && !is(T == shared)) | 
|  | in (atomicValueIsProperlyAligned(val)) | 
|  | { | 
|  | static if (is(T == U*, U)) | 
|  | return cast(T)core.internal.atomic.atomicFetchSub!ms(cast(size_t*)&val, mod * U.sizeof); | 
|  | else | 
|  | return core.internal.atomic.atomicFetchSub!ms(&val, cast(T)mod); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | T atomicFetchSub(MemoryOrder ms = MemoryOrder.seq, T)(ref return scope shared T val, size_t mod) pure nothrow @nogc @trusted | 
|  | if (__traits(isIntegral, T) || is(T == U*, U)) | 
|  | in (atomicValueIsProperlyAligned(val)) | 
|  | { | 
|  | return atomicFetchSub!ms(*cast(T*)&val, mod); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Exchange `exchangeWith` with the memory referenced by `here`. | 
|  | * This operation is both lock-free and atomic. | 
|  | * | 
|  | * Params: | 
|  | *  here         = The address of the destination variable. | 
|  | *  exchangeWith = The value to exchange. | 
|  | * | 
|  | * Returns: | 
|  | *  The value held previously by `here`. | 
|  | */ | 
|  | T atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)(T* here, V exchangeWith) pure nothrow @nogc @trusted | 
|  | if (!is(T == shared) && !is(V == shared)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | // resolve implicit conversions | 
|  | T arg = exchangeWith; | 
|  |  | 
|  | static if (__traits(isFloating, T)) | 
|  | { | 
|  | alias IntTy = IntForFloat!T; | 
|  | IntTy r = core.internal.atomic.atomicExchange!ms(cast(IntTy*)here, *cast(IntTy*)&arg); | 
|  | return *cast(shared(T)*)&r; | 
|  | } | 
|  | else | 
|  | return core.internal.atomic.atomicExchange!ms(here, arg); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | TailShared!T atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)(shared(T)* here, V exchangeWith) pure nothrow @nogc @trusted | 
|  | if (!is(T == class) && !is(T == interface)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | static if (is (V == shared U, U)) | 
|  | alias Thunk = U; | 
|  | else | 
|  | { | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  | static assert(!hasUnsharedIndirections!V, "Copying `exchangeWith` of type `" ~ V.stringof ~ "` to `" ~ shared(T).stringof ~ "` would violate shared."); | 
|  | alias Thunk = V; | 
|  | } | 
|  | return atomicExchange!ms(cast(T*)here, *cast(Thunk*)&exchangeWith); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)(shared(T)* here, shared(V) exchangeWith) pure nothrow @nogc @trusted | 
|  | if (is(T == class) || is(T == interface)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | static assert (is (V : T), "Can't assign `exchangeWith` of type `" ~ shared(V).stringof ~ "` to `" ~ shared(T).stringof ~ "`."); | 
|  |  | 
|  | return cast(shared)core.internal.atomic.atomicExchange!ms(cast(T*)here, cast(V)exchangeWith); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Performs either compare-and-set or compare-and-swap (or exchange). | 
|  | * | 
|  | * There are two categories of overloads in this template: | 
|  | * The first category does a simple compare-and-set. | 
|  | * The comparison value (`ifThis`) is treated as an rvalue. | 
|  | * | 
|  | * The second category does a compare-and-swap (a.k.a. compare-and-exchange), | 
|  | * and expects `ifThis` to be a pointer type, where the previous value | 
|  | * of `here` will be written. | 
|  | * | 
|  | * This operation is both lock-free and atomic. | 
|  | * | 
|  | * Params: | 
|  | *  here      = The address of the destination variable. | 
|  | *  writeThis = The value to store. | 
|  | *  ifThis    = The comparison value. | 
|  | * | 
|  | * Returns: | 
|  | *  true if the store occurred, false if not. | 
|  | */ | 
|  | template cas(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq) | 
|  | { | 
|  | /// Compare-and-set for non-shared values | 
|  | bool cas(T, V1, V2)(T* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted | 
|  | if (!is(T == shared) && is(T : V1)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | // resolve implicit conversions | 
|  | const T arg1 = ifThis; | 
|  | T arg2 = writeThis; | 
|  |  | 
|  | static if (__traits(isFloating, T)) | 
|  | { | 
|  | alias IntTy = IntForFloat!T; | 
|  | return atomicCompareExchangeStrongNoResult!(succ, fail)( | 
|  | cast(IntTy*)here, *cast(IntTy*)&arg1, *cast(IntTy*)&arg2); | 
|  | } | 
|  | else | 
|  | return atomicCompareExchangeStrongNoResult!(succ, fail)(here, arg1, arg2); | 
|  | } | 
|  |  | 
|  | /// Compare-and-set for shared value type | 
|  | bool cas(T, V1, V2)(shared(T)* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted | 
|  | if (!is(T == class) && (is(T : V1) || is(shared T : V1))) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | static if (is (V1 == shared U1, U1)) | 
|  | alias Thunk1 = U1; | 
|  | else | 
|  | alias Thunk1 = V1; | 
|  | static if (is (V2 == shared U2, U2)) | 
|  | alias Thunk2 = U2; | 
|  | else | 
|  | { | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  | static assert(!hasUnsharedIndirections!V2, | 
|  | "Copying `" ~ V2.stringof ~ "* writeThis` to `" ~ | 
|  | shared(T).stringof ~ "* here` would violate shared."); | 
|  | alias Thunk2 = V2; | 
|  | } | 
|  | return cas(cast(T*)here, *cast(Thunk1*)&ifThis, *cast(Thunk2*)&writeThis); | 
|  | } | 
|  |  | 
|  | /// Compare-and-set for `shared` reference type (`class`) | 
|  | bool cas(T, V1, V2)(shared(T)* here, shared(V1) ifThis, shared(V2) writeThis) | 
|  | pure nothrow @nogc @trusted | 
|  | if (is(T == class)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | return atomicCompareExchangeStrongNoResult!(succ, fail)( | 
|  | cast(T*)here, cast(V1)ifThis, cast(V2)writeThis); | 
|  | } | 
|  |  | 
|  | /// Compare-and-exchange for non-`shared` types | 
|  | bool cas(T, V)(T* here, T* ifThis, V writeThis) pure nothrow @nogc @trusted | 
|  | if (!is(T == shared) && !is(V == shared)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | // resolve implicit conversions | 
|  | T arg1 = writeThis; | 
|  |  | 
|  | static if (__traits(isFloating, T)) | 
|  | { | 
|  | alias IntTy = IntForFloat!T; | 
|  | return atomicCompareExchangeStrong!(succ, fail)( | 
|  | cast(IntTy*)here, cast(IntTy*)ifThis, *cast(IntTy*)&writeThis); | 
|  | } | 
|  | else | 
|  | return atomicCompareExchangeStrong!(succ, fail)(here, ifThis, writeThis); | 
|  | } | 
|  |  | 
|  | /// Compare and exchange for mixed-`shared`ness types | 
|  | bool cas(T, V1, V2)(shared(T)* here, V1* ifThis, V2 writeThis) pure nothrow @nogc @trusted | 
|  | if (!is(T == class) && (is(T : V1) || is(shared T : V1))) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | static if (is (V1 == shared U1, U1)) | 
|  | alias Thunk1 = U1; | 
|  | else | 
|  | { | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  | static assert(!hasUnsharedIndirections!V1, | 
|  | "Copying `" ~ shared(T).stringof ~ "* here` to `" ~ | 
|  | V1.stringof ~ "* ifThis` would violate shared."); | 
|  | alias Thunk1 = V1; | 
|  | } | 
|  | static if (is (V2 == shared U2, U2)) | 
|  | alias Thunk2 = U2; | 
|  | else | 
|  | { | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  | static assert(!hasUnsharedIndirections!V2, | 
|  | "Copying `" ~ V2.stringof ~ "* writeThis` to `" ~ | 
|  | shared(T).stringof ~ "* here` would violate shared."); | 
|  | alias Thunk2 = V2; | 
|  | } | 
|  | static assert (is(T : Thunk1), | 
|  | "Mismatching types for `here` and `ifThis`: `" ~ | 
|  | shared(T).stringof ~ "` and `" ~ V1.stringof ~ "`."); | 
|  | return cas(cast(T*)here, cast(Thunk1*)ifThis, *cast(Thunk2*)&writeThis); | 
|  | } | 
|  |  | 
|  | /// Compare-and-exchange for `class` | 
|  | bool cas(T, V)(shared(T)* here, shared(T)* ifThis, shared(V) writeThis) | 
|  | pure nothrow @nogc @trusted | 
|  | if (is(T == class)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | return atomicCompareExchangeStrong!(succ, fail)( | 
|  | cast(T*)here, cast(T*)ifThis, cast(V)writeThis); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Stores 'writeThis' to the memory referenced by 'here' if the value | 
|  | * referenced by 'here' is equal to 'ifThis'. | 
|  | * The 'weak' version of cas may spuriously fail. It is recommended to | 
|  | * use `casWeak` only when `cas` would be used in a loop. | 
|  | * This operation is both | 
|  | * lock-free and atomic. | 
|  | * | 
|  | * Params: | 
|  | *  here      = The address of the destination variable. | 
|  | *  writeThis = The value to store. | 
|  | *  ifThis    = The comparison value. | 
|  | * | 
|  | * Returns: | 
|  | *  true if the store occurred, false if not. | 
|  | */ | 
|  | bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(T* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted | 
|  | if (!is(T == shared) && is(T : V1)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | // resolve implicit conversions | 
|  | T arg1 = ifThis; | 
|  | T arg2 = writeThis; | 
|  |  | 
|  | static if (__traits(isFloating, T)) | 
|  | { | 
|  | alias IntTy = IntForFloat!T; | 
|  | return atomicCompareExchangeWeakNoResult!(succ, fail)(cast(IntTy*)here, *cast(IntTy*)&arg1, *cast(IntTy*)&arg2); | 
|  | } | 
|  | else | 
|  | return atomicCompareExchangeWeakNoResult!(succ, fail)(here, arg1, arg2); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(shared(T)* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted | 
|  | if (!is(T == class) && (is(T : V1) || is(shared T : V1))) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | static if (is (V1 == shared U1, U1)) | 
|  | alias Thunk1 = U1; | 
|  | else | 
|  | alias Thunk1 = V1; | 
|  | static if (is (V2 == shared U2, U2)) | 
|  | alias Thunk2 = U2; | 
|  | else | 
|  | { | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  | static assert(!hasUnsharedIndirections!V2, "Copying `" ~ V2.stringof ~ "* writeThis` to `" ~ shared(T).stringof ~ "* here` would violate shared."); | 
|  | alias Thunk2 = V2; | 
|  | } | 
|  | return casWeak!(succ, fail)(cast(T*)here, *cast(Thunk1*)&ifThis, *cast(Thunk2*)&writeThis); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(shared(T)* here, shared(V1) ifThis, shared(V2) writeThis) pure nothrow @nogc @trusted | 
|  | if (is(T == class)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | return atomicCompareExchangeWeakNoResult!(succ, fail)(cast(T*)here, cast(V1)ifThis, cast(V2)writeThis); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Stores 'writeThis' to the memory referenced by 'here' if the value | 
|  | * referenced by 'here' is equal to the value referenced by 'ifThis'. | 
|  | * The prior value referenced by 'here' is written to `ifThis` and | 
|  | * returned to the user. | 
|  | * The 'weak' version of cas may spuriously fail. It is recommended to | 
|  | * use `casWeak` only when `cas` would be used in a loop. | 
|  | * This operation is both lock-free and atomic. | 
|  | * | 
|  | * Params: | 
|  | *  here      = The address of the destination variable. | 
|  | *  writeThis = The value to store. | 
|  | *  ifThis    = The address of the value to compare, and receives the prior value of `here` as output. | 
|  | * | 
|  | * Returns: | 
|  | *  true if the store occurred, false if not. | 
|  | */ | 
|  | bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V)(T* here, T* ifThis, V writeThis) pure nothrow @nogc @trusted | 
|  | if (!is(T == shared S, S) && !is(V == shared U, U)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | // resolve implicit conversions | 
|  | T arg1 = writeThis; | 
|  |  | 
|  | static if (__traits(isFloating, T)) | 
|  | { | 
|  | alias IntTy = IntForFloat!T; | 
|  | return atomicCompareExchangeWeak!(succ, fail)(cast(IntTy*)here, cast(IntTy*)ifThis, *cast(IntTy*)&writeThis); | 
|  | } | 
|  | else | 
|  | return atomicCompareExchangeWeak!(succ, fail)(here, ifThis, writeThis); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(shared(T)* here, V1* ifThis, V2 writeThis) pure nothrow @nogc @trusted | 
|  | if (!is(T == class) && (is(T : V1) || is(shared T : V1))) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | static if (is (V1 == shared U1, U1)) | 
|  | alias Thunk1 = U1; | 
|  | else | 
|  | { | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  | static assert(!hasUnsharedIndirections!V1, "Copying `" ~ shared(T).stringof ~ "* here` to `" ~ V1.stringof ~ "* ifThis` would violate shared."); | 
|  | alias Thunk1 = V1; | 
|  | } | 
|  | static if (is (V2 == shared U2, U2)) | 
|  | alias Thunk2 = U2; | 
|  | else | 
|  | { | 
|  | import core.internal.traits : hasUnsharedIndirections; | 
|  | static assert(!hasUnsharedIndirections!V2, "Copying `" ~ V2.stringof ~ "* writeThis` to `" ~ shared(T).stringof ~ "* here` would violate shared."); | 
|  | alias Thunk2 = V2; | 
|  | } | 
|  | static assert (is(T : Thunk1), "Mismatching types for `here` and `ifThis`: `" ~ shared(T).stringof ~ "` and `" ~ V1.stringof ~ "`."); | 
|  | return casWeak!(succ, fail)(cast(T*)here, cast(Thunk1*)ifThis, *cast(Thunk2*)&writeThis); | 
|  | } | 
|  |  | 
|  | /// Ditto | 
|  | bool casWeak(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V)(shared(T)* here, shared(T)* ifThis, shared(V) writeThis) pure nothrow @nogc @trusted | 
|  | if (is(T == class)) | 
|  | in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") | 
|  | { | 
|  | return atomicCompareExchangeWeak!(succ, fail)(cast(T*)here, cast(T*)ifThis, cast(V)writeThis); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Inserts a full load/store memory fence (on platforms that need it). This ensures | 
|  | * that all loads and stores before a call to this function are executed before any | 
|  | * loads and stores after the call. | 
|  | */ | 
|  | void atomicFence(MemoryOrder order = MemoryOrder.seq)() pure nothrow @nogc @safe | 
|  | { | 
|  | core.internal.atomic.atomicFence!order(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Gives a hint to the processor that the calling thread is in a 'spin-wait' loop, | 
|  | * allowing to more efficiently allocate resources. | 
|  | */ | 
|  | void pause() pure nothrow @nogc @safe | 
|  | { | 
|  | core.internal.atomic.pause(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Performs the binary operation 'op' on val using 'mod' as the modifier. | 
|  | * | 
|  | * Params: | 
|  | *  val = The target variable. | 
|  | *  mod = The modifier to apply. | 
|  | * | 
|  | * Returns: | 
|  | *  The result of the operation. | 
|  | */ | 
|  | TailShared!T atomicOp(string op, T, V1)(ref shared T val, V1 mod) pure nothrow @nogc @safe | 
|  | if (__traits(compiles, mixin("*cast(T*)&val" ~ op ~ "mod"))) | 
|  | in (atomicValueIsProperlyAligned(val)) | 
|  | { | 
|  | // binary operators | 
|  | // | 
|  | // +    -   *   /   %   ^^  & | 
|  | // |    ^   <<  >>  >>> ~   in | 
|  | // ==   !=  <   <=  >   >= | 
|  | static if (op == "+"  || op == "-"  || op == "*"  || op == "/"   || | 
|  | op == "%"  || op == "^^" || op == "&"  || op == "|"   || | 
|  | op == "^"  || op == "<<" || op == ">>" || op == ">>>" || | 
|  | op == "~"  || // skip "in" | 
|  | op == "==" || op == "!=" || op == "<"  || op == "<="  || | 
|  | op == ">"  || op == ">=") | 
|  | { | 
|  | T get = atomicLoad!(MemoryOrder.raw, T)(val); | 
|  | mixin("return get " ~ op ~ " mod;"); | 
|  | } | 
|  | else | 
|  | // assignment operators | 
|  | // | 
|  | // +=   -=  *=  /=  %=  ^^= &= | 
|  | // |=   ^=  <<= >>= >>>=    ~= | 
|  | static if (op == "+=" && __traits(isIntegral, T) && __traits(isIntegral, V1) && T.sizeof <= size_t.sizeof && V1.sizeof <= size_t.sizeof) | 
|  | { | 
|  | return cast(T)(atomicFetchAdd(val, mod) + mod); | 
|  | } | 
|  | else static if (op == "-=" && __traits(isIntegral, T) && __traits(isIntegral, V1) && T.sizeof <= size_t.sizeof && V1.sizeof <= size_t.sizeof) | 
|  | { | 
|  | return cast(T)(atomicFetchSub(val, mod) - mod); | 
|  | } | 
|  | else static if (op == "+=" || op == "-="  || op == "*="  || op == "/=" || | 
|  | op == "%=" || op == "^^=" || op == "&="  || op == "|=" || | 
|  | op == "^=" || op == "<<=" || op == ">>=" || op == ">>>=") // skip "~=" | 
|  | { | 
|  | T set, get = atomicLoad!(MemoryOrder.raw, T)(val); | 
|  | do | 
|  | { | 
|  | set = get; | 
|  | mixin("set " ~ op ~ " mod;"); | 
|  | } while (!casWeakByRef(val, get, set)); | 
|  | return set; | 
|  | } | 
|  | else | 
|  | { | 
|  | static assert(false, "Operation not supported."); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | version (D_InlineAsm_X86) | 
|  | { | 
|  | enum has64BitXCHG = false; | 
|  | enum has64BitCAS = true; | 
|  | enum has128BitCAS = false; | 
|  | } | 
|  | else version (D_InlineAsm_X86_64) | 
|  | { | 
|  | enum has64BitXCHG = true; | 
|  | enum has64BitCAS = true; | 
|  | enum has128BitCAS = true; | 
|  | } | 
|  | else version (GNU) | 
|  | { | 
|  | import gcc.config; | 
|  | enum has64BitCAS = GNU_Have_64Bit_Atomics; | 
|  | enum has64BitXCHG = GNU_Have_64Bit_Atomics; | 
|  | enum has128BitCAS = GNU_Have_LibAtomic; | 
|  | } | 
|  | else | 
|  | { | 
|  | enum has64BitXCHG = false; | 
|  | enum has64BitCAS = false; | 
|  | enum has128BitCAS = false; | 
|  | } | 
|  |  | 
|  | private | 
|  | { | 
|  | bool atomicValueIsProperlyAligned(T)(ref T val) pure nothrow @nogc @trusted | 
|  | { | 
|  | return atomicPtrIsProperlyAligned(&val); | 
|  | } | 
|  |  | 
|  | bool atomicPtrIsProperlyAligned(T)(T* ptr) pure nothrow @nogc @safe | 
|  | { | 
|  | // NOTE: Strictly speaking, the x86 supports atomic operations on | 
|  | //       unaligned values.  However, this is far slower than the | 
|  | //       common case, so such behavior should be prohibited. | 
|  | static if (T.sizeof > size_t.sizeof) | 
|  | { | 
|  | version (X86) | 
|  | { | 
|  | // cmpxchg8b only requires 4-bytes alignment | 
|  | return cast(size_t)ptr % size_t.sizeof == 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | // e.g., x86_64 cmpxchg16b requires 16-bytes alignment | 
|  | return cast(size_t)ptr % T.sizeof == 0; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | return cast(size_t)ptr % T.sizeof == 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | template IntForFloat(F) | 
|  | if (__traits(isFloating, F)) | 
|  | { | 
|  | static if (F.sizeof == 4) | 
|  | alias IntForFloat = uint; | 
|  | else static if (F.sizeof == 8) | 
|  | alias IntForFloat = ulong; | 
|  | else | 
|  | static assert (false, "Invalid floating point type: " ~ F.stringof ~ ", only support `float` and `double`."); | 
|  | } | 
|  |  | 
|  | template IntForStruct(S) | 
|  | if (is(S == struct)) | 
|  | { | 
|  | static if (S.sizeof == 1) | 
|  | alias IntForFloat = ubyte; | 
|  | else static if (F.sizeof == 2) | 
|  | alias IntForFloat = ushort; | 
|  | else static if (F.sizeof == 4) | 
|  | alias IntForFloat = uint; | 
|  | else static if (F.sizeof == 8) | 
|  | alias IntForFloat = ulong; | 
|  | else static if (F.sizeof == 16) | 
|  | alias IntForFloat = ulong[2]; // TODO: what's the best type here? slice/delegates pass in registers... | 
|  | else | 
|  | static assert (ValidateStruct!S); | 
|  | } | 
|  |  | 
|  | template ValidateStruct(S) | 
|  | if (is(S == struct)) | 
|  | { | 
|  | import core.internal.traits : hasElaborateAssign; | 
|  |  | 
|  | // `(x & (x-1)) == 0` checks that x is a power of 2. | 
|  | static assert (S.sizeof <= size_t.sizeof * 2 | 
|  | && (S.sizeof & (S.sizeof - 1)) == 0, | 
|  | S.stringof ~ " has invalid size for atomic operations."); | 
|  | static assert (!hasElaborateAssign!S, S.stringof ~ " may not have an elaborate assignment when used with atomic operations."); | 
|  |  | 
|  | enum ValidateStruct = true; | 
|  | } | 
|  |  | 
|  | // TODO: it'd be nice if we had @trusted scopes; we could remove this... | 
|  | bool casWeakByRef(T,V1,V2)(ref T value, ref V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted | 
|  | { | 
|  | return casWeak(&value, &ifThis, writeThis); | 
|  | } | 
|  |  | 
|  | /* Construct a type with a shared tail, and if possible with an unshared | 
|  | head. */ | 
|  | template TailShared(U) if (!is(U == shared)) | 
|  | { | 
|  | alias TailShared = .TailShared!(shared U); | 
|  | } | 
|  | template TailShared(S) if (is(S == shared)) | 
|  | { | 
|  | // Get the unshared variant of S. | 
|  | static if (is(S U == shared U)) {} | 
|  | else static assert(false, "Should never be triggered. The `static " ~ | 
|  | "if` declares `U` as the unshared version of the shared type " ~ | 
|  | "`S`. `S` is explicitly declared as shared, so getting `U` " ~ | 
|  | "should always work."); | 
|  |  | 
|  | static if (is(S : U)) | 
|  | alias TailShared = U; | 
|  | else static if (is(S == struct)) | 
|  | { | 
|  | enum implName = () { | 
|  | /* Start with "_impl". If S has a field with that name, append | 
|  | underscores until the clash is resolved. */ | 
|  | string name = "_impl"; | 
|  | string[] fieldNames; | 
|  | static foreach (alias field; S.tupleof) | 
|  | { | 
|  | fieldNames ~= __traits(identifier, field); | 
|  | } | 
|  | static bool canFind(string[] haystack, string needle) | 
|  | { | 
|  | foreach (candidate; haystack) | 
|  | { | 
|  | if (candidate == needle) return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  | while (canFind(fieldNames, name)) name ~= "_"; | 
|  | return name; | 
|  | } (); | 
|  | struct TailShared | 
|  | { | 
|  | static foreach (i, alias field; S.tupleof) | 
|  | { | 
|  | /* On @trusted: This is casting the field from shared(Foo) | 
|  | to TailShared!Foo. The cast is safe because the field has | 
|  | been loaded and is not shared anymore. */ | 
|  | mixin(" | 
|  | @trusted @property | 
|  | ref " ~ __traits(identifier, field) ~ "() | 
|  | { | 
|  | alias R = TailShared!(typeof(field)); | 
|  | return * cast(R*) &" ~ implName ~ ".tupleof[i]; | 
|  | } | 
|  | "); | 
|  | } | 
|  | mixin(" | 
|  | S " ~ implName ~ "; | 
|  | alias " ~ implName ~ " this; | 
|  | "); | 
|  | } | 
|  | } | 
|  | else | 
|  | alias TailShared = S; | 
|  | } | 
|  | @safe unittest | 
|  | { | 
|  | // No tail (no indirections) -> fully unshared. | 
|  |  | 
|  | static assert(is(TailShared!int == int)); | 
|  | static assert(is(TailShared!(shared int) == int)); | 
|  |  | 
|  | static struct NoIndir { int i; } | 
|  | static assert(is(TailShared!NoIndir == NoIndir)); | 
|  | static assert(is(TailShared!(shared NoIndir) == NoIndir)); | 
|  |  | 
|  | // Tail can be independently shared or is already -> tail-shared. | 
|  |  | 
|  | static assert(is(TailShared!(int*) == shared(int)*)); | 
|  | static assert(is(TailShared!(shared int*) == shared(int)*)); | 
|  | static assert(is(TailShared!(shared(int)*) == shared(int)*)); | 
|  |  | 
|  | static assert(is(TailShared!(int[]) == shared(int)[])); | 
|  | static assert(is(TailShared!(shared int[]) == shared(int)[])); | 
|  | static assert(is(TailShared!(shared(int)[]) == shared(int)[])); | 
|  |  | 
|  | static struct S1 { shared int* p; } | 
|  | static assert(is(TailShared!S1 == S1)); | 
|  | static assert(is(TailShared!(shared S1) == S1)); | 
|  |  | 
|  | static struct S2 { shared(int)* p; } | 
|  | static assert(is(TailShared!S2 == S2)); | 
|  | static assert(is(TailShared!(shared S2) == S2)); | 
|  |  | 
|  | // Tail follows shared-ness of head -> fully shared. | 
|  |  | 
|  | static class C { int i; } | 
|  | static assert(is(TailShared!C == shared C)); | 
|  | static assert(is(TailShared!(shared C) == shared C)); | 
|  |  | 
|  | /* However, structs get a wrapper that has getters which cast to | 
|  | TailShared. */ | 
|  |  | 
|  | static struct S3 { int* p; int _impl; int _impl_; int _impl__; } | 
|  | static assert(!is(TailShared!S3 : S3)); | 
|  | static assert(is(TailShared!S3 : shared S3)); | 
|  | static assert(is(TailShared!(shared S3) == TailShared!S3)); | 
|  |  | 
|  | static struct S4 { shared(int)** p; } | 
|  | static assert(!is(TailShared!S4 : S4)); | 
|  | static assert(is(TailShared!S4 : shared S4)); | 
|  | static assert(is(TailShared!(shared S4) == TailShared!S4)); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  | // Unit Tests | 
|  | //////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  |  | 
|  | version (CoreUnittest) | 
|  | { | 
|  | version (D_LP64) | 
|  | { | 
|  | enum hasDWCAS = has128BitCAS; | 
|  | } | 
|  | else | 
|  | { | 
|  | enum hasDWCAS = has64BitCAS; | 
|  | } | 
|  |  | 
|  | void testXCHG(T)(T val) pure nothrow @nogc @trusted | 
|  | in | 
|  | { | 
|  | assert(val !is T.init); | 
|  | } | 
|  | do | 
|  | { | 
|  | T         base = cast(T)null; | 
|  | shared(T) atom = cast(shared(T))null; | 
|  |  | 
|  | assert(base !is val, T.stringof); | 
|  | assert(atom is base, T.stringof); | 
|  |  | 
|  | assert(atomicExchange(&atom, val) is base, T.stringof); | 
|  | assert(atom is val, T.stringof); | 
|  | } | 
|  |  | 
|  | void testCAS(T)(T val) pure nothrow @nogc @trusted | 
|  | in | 
|  | { | 
|  | assert(val !is T.init); | 
|  | } | 
|  | do | 
|  | { | 
|  | T         base = cast(T)null; | 
|  | shared(T) atom = cast(shared(T))null; | 
|  |  | 
|  | assert(base !is val, T.stringof); | 
|  | assert(atom is base, T.stringof); | 
|  |  | 
|  | assert(cas(&atom, base, val), T.stringof); | 
|  | assert(atom is val, T.stringof); | 
|  | assert(!cas(&atom, base, base), T.stringof); | 
|  | assert(atom is val, T.stringof); | 
|  |  | 
|  | atom = cast(shared(T))null; | 
|  |  | 
|  | shared(T) arg = base; | 
|  | assert(cas(&atom, &arg, val), T.stringof); | 
|  | assert(arg is base, T.stringof); | 
|  | assert(atom is val, T.stringof); | 
|  |  | 
|  | arg = base; | 
|  | assert(!cas(&atom, &arg, base), T.stringof); | 
|  | assert(arg is val, T.stringof); | 
|  | assert(atom is val, T.stringof); | 
|  | } | 
|  |  | 
|  | void testLoadStore(MemoryOrder ms = MemoryOrder.seq, T)(T val = T.init + 1) pure nothrow @nogc @trusted | 
|  | { | 
|  | T         base = cast(T) 0; | 
|  | shared(T) atom = cast(T) 0; | 
|  |  | 
|  | assert(base !is val); | 
|  | assert(atom is base); | 
|  | atomicStore!(ms)(atom, val); | 
|  | base = atomicLoad!(ms)(atom); | 
|  |  | 
|  | assert(base is val, T.stringof); | 
|  | assert(atom is val); | 
|  | } | 
|  |  | 
|  |  | 
|  | void testType(T)(T val = T.init + 1) pure nothrow @nogc @safe | 
|  | { | 
|  | static if (T.sizeof < 8 || has64BitXCHG) | 
|  | testXCHG!(T)(val); | 
|  | testCAS!(T)(val); | 
|  | testLoadStore!(MemoryOrder.seq, T)(val); | 
|  | testLoadStore!(MemoryOrder.raw, T)(val); | 
|  | } | 
|  |  | 
|  | @betterC @safe pure nothrow unittest | 
|  | { | 
|  | testType!(bool)(); | 
|  |  | 
|  | testType!(byte)(); | 
|  | testType!(ubyte)(); | 
|  |  | 
|  | testType!(short)(); | 
|  | testType!(ushort)(); | 
|  |  | 
|  | testType!(int)(); | 
|  | testType!(uint)(); | 
|  | } | 
|  |  | 
|  | @safe pure nothrow unittest | 
|  | { | 
|  |  | 
|  | testType!(shared int*)(); | 
|  |  | 
|  | static interface Inter {} | 
|  | static class KlassImpl : Inter {} | 
|  | testXCHG!(shared Inter)(new shared(KlassImpl)); | 
|  | testCAS!(shared Inter)(new shared(KlassImpl)); | 
|  |  | 
|  | static class Klass {} | 
|  | testXCHG!(shared Klass)(new shared(Klass)); | 
|  | testCAS!(shared Klass)(new shared(Klass)); | 
|  |  | 
|  | testXCHG!(shared int)(42); | 
|  |  | 
|  | testType!(float)(0.1f); | 
|  |  | 
|  | static if (has64BitCAS) | 
|  | { | 
|  | testType!(double)(0.1); | 
|  | testType!(long)(); | 
|  | testType!(ulong)(); | 
|  | } | 
|  | static if (has128BitCAS) | 
|  | { | 
|  | () @trusted | 
|  | { | 
|  | align(16) struct Big { long a, b; } | 
|  |  | 
|  | shared(Big) atom; | 
|  | shared(Big) base; | 
|  | shared(Big) arg; | 
|  | shared(Big) val = Big(1, 2); | 
|  |  | 
|  | assert(cas(&atom, arg, val), Big.stringof); | 
|  | assert(atom is val, Big.stringof); | 
|  | assert(!cas(&atom, arg, val), Big.stringof); | 
|  | assert(atom is val, Big.stringof); | 
|  |  | 
|  | atom = Big(); | 
|  | assert(cas(&atom, &arg, val), Big.stringof); | 
|  | assert(arg is base, Big.stringof); | 
|  | assert(atom is val, Big.stringof); | 
|  |  | 
|  | arg = Big(); | 
|  | assert(!cas(&atom, &arg, base), Big.stringof); | 
|  | assert(arg is val, Big.stringof); | 
|  | assert(atom is val, Big.stringof); | 
|  | }(); | 
|  | } | 
|  |  | 
|  | shared(size_t) i; | 
|  |  | 
|  | atomicOp!"+="(i, cast(size_t) 1); | 
|  | assert(i == 1); | 
|  |  | 
|  | atomicOp!"-="(i, cast(size_t) 1); | 
|  | assert(i == 0); | 
|  |  | 
|  | shared float f = 0.1f; | 
|  | atomicOp!"+="(f, 0.1f); | 
|  | assert(f > 0.1999f && f < 0.2001f); | 
|  |  | 
|  | static if (has64BitCAS) | 
|  | { | 
|  | shared double d = 0.1; | 
|  | atomicOp!"+="(d, 0.1); | 
|  | assert(d > 0.1999 && d < 0.2001); | 
|  | } | 
|  | } | 
|  |  | 
|  | @betterC pure nothrow unittest | 
|  | { | 
|  | static if (has128BitCAS) | 
|  | { | 
|  | struct DoubleValue | 
|  | { | 
|  | long value1; | 
|  | long value2; | 
|  | } | 
|  |  | 
|  | align(16) shared DoubleValue a; | 
|  | atomicStore(a, DoubleValue(1,2)); | 
|  | assert(a.value1 == 1 && a.value2 ==2); | 
|  |  | 
|  | while (!cas(&a, DoubleValue(1,2), DoubleValue(3,4))){} | 
|  | assert(a.value1 == 3 && a.value2 ==4); | 
|  |  | 
|  | align(16) DoubleValue b = atomicLoad(a); | 
|  | assert(b.value1 == 3 && b.value2 ==4); | 
|  | } | 
|  |  | 
|  | static if (hasDWCAS) | 
|  | { | 
|  | static struct List { size_t gen; List* next; } | 
|  | shared(List) head; | 
|  | assert(cas(&head, shared(List)(0, null), shared(List)(1, cast(List*)1))); | 
|  | assert(head.gen == 1); | 
|  | assert(cast(size_t)head.next == 1); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=20629 | 
|  | static struct Struct | 
|  | { | 
|  | uint a, b; | 
|  | } | 
|  | shared Struct s1 = Struct(1, 2); | 
|  | atomicStore(s1, Struct(3, 4)); | 
|  | assert(cast(uint) s1.a == 3); | 
|  | assert(cast(uint) s1.b == 4); | 
|  | } | 
|  |  | 
|  | // https://issues.dlang.org/show_bug.cgi?id=20844 | 
|  | static if (hasDWCAS) | 
|  | { | 
|  | debug: // tests CAS in-contract | 
|  |  | 
|  | pure nothrow unittest | 
|  | { | 
|  | import core.exception : AssertError; | 
|  |  | 
|  | align(16) shared ubyte[2 * size_t.sizeof + 1] data; | 
|  | auto misalignedPointer = cast(size_t[2]*) &data[1]; | 
|  | size_t[2] x; | 
|  |  | 
|  | try | 
|  | cas(misalignedPointer, x, x); | 
|  | catch (AssertError) | 
|  | return; | 
|  |  | 
|  | assert(0, "should have failed"); | 
|  | } | 
|  | } | 
|  |  | 
|  | @betterC pure nothrow @nogc @safe unittest | 
|  | { | 
|  | int a; | 
|  | if (casWeak!(MemoryOrder.acq_rel, MemoryOrder.raw)(&a, 0, 4)) | 
|  | assert(a == 4); | 
|  | } | 
|  |  | 
|  | @betterC pure nothrow unittest | 
|  | { | 
|  | static struct S { int val; } | 
|  | auto s = shared(S)(1); | 
|  |  | 
|  | shared(S*) ptr; | 
|  |  | 
|  | // head unshared | 
|  | shared(S)* ifThis = null; | 
|  | shared(S)* writeThis = &s; | 
|  | assert(ptr is null); | 
|  | assert(cas(&ptr, ifThis, writeThis)); | 
|  | assert(ptr is writeThis); | 
|  |  | 
|  | // head shared | 
|  | shared(S*) ifThis2 = writeThis; | 
|  | shared(S*) writeThis2 = null; | 
|  | assert(cas(&ptr, ifThis2, writeThis2)); | 
|  | assert(ptr is null); | 
|  | } | 
|  |  | 
|  | unittest | 
|  | { | 
|  | import core.thread; | 
|  |  | 
|  | // Use heap memory to ensure an optimizing | 
|  | // compiler doesn't put things in registers. | 
|  | uint* x = new uint(); | 
|  | bool* f = new bool(); | 
|  | uint* r = new uint(); | 
|  |  | 
|  | auto thr = new Thread(() | 
|  | { | 
|  | while (!*f) | 
|  | { | 
|  | } | 
|  |  | 
|  | atomicFence(); | 
|  |  | 
|  | *r = *x; | 
|  | }); | 
|  |  | 
|  | thr.start(); | 
|  |  | 
|  | *x = 42; | 
|  |  | 
|  | atomicFence(); | 
|  |  | 
|  | *f = true; | 
|  |  | 
|  | atomicFence(); | 
|  |  | 
|  | thr.join(); | 
|  |  | 
|  | assert(*r == 42); | 
|  | } | 
|  |  | 
|  | // === atomicFetchAdd and atomicFetchSub operations ==== | 
|  | @betterC pure nothrow @nogc @safe unittest | 
|  | { | 
|  | shared ubyte u8 = 1; | 
|  | shared ushort u16 = 2; | 
|  | shared uint u32 = 3; | 
|  | shared byte i8 = 5; | 
|  | shared short i16 = 6; | 
|  | shared int i32 = 7; | 
|  |  | 
|  | assert(atomicOp!"+="(u8, 8) == 9); | 
|  | assert(atomicOp!"+="(u16, 8) == 10); | 
|  | assert(atomicOp!"+="(u32, 8) == 11); | 
|  | assert(atomicOp!"+="(i8, 8) == 13); | 
|  | assert(atomicOp!"+="(i16, 8) == 14); | 
|  | assert(atomicOp!"+="(i32, 8) == 15); | 
|  | version (D_LP64) | 
|  | { | 
|  | shared ulong u64 = 4; | 
|  | shared long i64 = 8; | 
|  | assert(atomicOp!"+="(u64, 8) == 12); | 
|  | assert(atomicOp!"+="(i64, 8) == 16); | 
|  | } | 
|  | } | 
|  |  | 
|  | @betterC pure nothrow @nogc unittest | 
|  | { | 
|  | byte[10] byteArray = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]; | 
|  | ulong[10] ulongArray = [2, 4, 6, 8, 10, 12, 14, 16, 19, 20]; | 
|  |  | 
|  | { | 
|  | auto array = byteArray; | 
|  | byte* ptr = &array[0]; | 
|  | byte* prevPtr = atomicFetchAdd(ptr, 3); | 
|  | assert(prevPtr == &array[0]); | 
|  | assert(*prevPtr == 1); | 
|  | assert(*ptr == 7); | 
|  | } | 
|  | { | 
|  | auto array = ulongArray; | 
|  | ulong* ptr = &array[0]; | 
|  | ulong* prevPtr = atomicFetchAdd(ptr, 3); | 
|  | assert(prevPtr == &array[0]); | 
|  | assert(*prevPtr == 2); | 
|  | assert(*ptr == 8); | 
|  | } | 
|  | } | 
|  |  | 
|  | @betterC pure nothrow @nogc @safe unittest | 
|  | { | 
|  | shared ubyte u8 = 1; | 
|  | shared ushort u16 = 2; | 
|  | shared uint u32 = 3; | 
|  | shared byte i8 = 5; | 
|  | shared short i16 = 6; | 
|  | shared int i32 = 7; | 
|  |  | 
|  | assert(atomicOp!"-="(u8, 1) == 0); | 
|  | assert(atomicOp!"-="(u16, 1) == 1); | 
|  | assert(atomicOp!"-="(u32, 1) == 2); | 
|  | assert(atomicOp!"-="(i8, 1) == 4); | 
|  | assert(atomicOp!"-="(i16, 1) == 5); | 
|  | assert(atomicOp!"-="(i32, 1) == 6); | 
|  | version (D_LP64) | 
|  | { | 
|  | shared ulong u64 = 4; | 
|  | shared long i64 = 8; | 
|  | assert(atomicOp!"-="(u64, 1) == 3); | 
|  | assert(atomicOp!"-="(i64, 1) == 7); | 
|  | } | 
|  | } | 
|  |  | 
|  | @betterC pure nothrow @nogc unittest | 
|  | { | 
|  | byte[10] byteArray = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]; | 
|  | ulong[10] ulongArray = [2, 4, 6, 8, 10, 12, 14, 16, 19, 20]; | 
|  |  | 
|  | { | 
|  | auto array = byteArray; | 
|  | byte* ptr = &array[5]; | 
|  | byte* prevPtr = atomicFetchSub(ptr, 4); | 
|  | assert(prevPtr == &array[5]); | 
|  | assert(*prevPtr == 11); | 
|  | assert(*ptr == 3); // https://issues.dlang.org/show_bug.cgi?id=21578 | 
|  | } | 
|  | { | 
|  | auto array = ulongArray; | 
|  | ulong* ptr = &array[5]; | 
|  | ulong* prevPtr = atomicFetchSub(ptr, 4); | 
|  | assert(prevPtr == &array[5]); | 
|  | assert(*prevPtr == 12); | 
|  | assert(*ptr == 4); // https://issues.dlang.org/show_bug.cgi?id=21578 | 
|  | } | 
|  | } | 
|  |  | 
|  | @betterC pure nothrow @nogc @safe unittest // issue 16651 | 
|  | { | 
|  | shared ulong a = 2; | 
|  | uint b = 1; | 
|  | atomicOp!"-="(a, b); | 
|  | assert(a == 1); | 
|  |  | 
|  | shared uint c = 2; | 
|  | ubyte d = 1; | 
|  | atomicOp!"-="(c, d); | 
|  | assert(c == 1); | 
|  | } | 
|  |  | 
|  | pure nothrow @safe unittest // issue 16230 | 
|  | { | 
|  | shared int i; | 
|  | static assert(is(typeof(atomicLoad(i)) == int)); | 
|  |  | 
|  | shared int* p; | 
|  | static assert(is(typeof(atomicLoad(p)) == shared(int)*)); | 
|  |  | 
|  | shared int[] a; | 
|  | static if (__traits(compiles, atomicLoad(a))) | 
|  | { | 
|  | static assert(is(typeof(atomicLoad(a)) == shared(int)[])); | 
|  | } | 
|  |  | 
|  | static struct S { int* _impl; } | 
|  | shared S s; | 
|  | static assert(is(typeof(atomicLoad(s)) : shared S)); | 
|  | static assert(is(typeof(atomicLoad(s)._impl) == shared(int)*)); | 
|  | auto u = atomicLoad(s); | 
|  | assert(u._impl is null); | 
|  | u._impl = new shared int(42); | 
|  | assert(atomicLoad(*u._impl) == 42); | 
|  |  | 
|  | static struct S2 { S s; } | 
|  | shared S2 s2; | 
|  | static assert(is(typeof(atomicLoad(s2).s) == TailShared!S)); | 
|  |  | 
|  | static struct S3 { size_t head; int* tail; } | 
|  | shared S3 s3; | 
|  | static if (__traits(compiles, atomicLoad(s3))) | 
|  | { | 
|  | static assert(is(typeof(atomicLoad(s3).head) == size_t)); | 
|  | static assert(is(typeof(atomicLoad(s3).tail) == shared(int)*)); | 
|  | } | 
|  |  | 
|  | static class C { int i; } | 
|  | shared C c; | 
|  | static assert(is(typeof(atomicLoad(c)) == shared C)); | 
|  |  | 
|  | static struct NoIndirections { int i; } | 
|  | shared NoIndirections n; | 
|  | static assert(is(typeof(atomicLoad(n)) == NoIndirections)); | 
|  | } | 
|  |  | 
|  | unittest // Issue 21631 | 
|  | { | 
|  | shared uint si1 = 45; | 
|  | shared uint si2 = 38; | 
|  | shared uint* psi = &si1; | 
|  |  | 
|  | assert((&psi).cas(cast(const) psi, &si2)); | 
|  | } | 
|  | } |