From 965fa3322cccf35d279a51fa09fe4ae70ddc0f51 Mon Sep 17 00:00:00 2001 From: Geod24 Date: Mon, 4 May 2020 09:23:46 +0900 Subject: [PATCH] Refactor `core.atomic : cas` into a two-level template This should reduce the amount of boilerplate needed in the implementation, and hopefully make the code easier to read for everyone. --- src/core/atomic.d | 192 ++++++++++++++++++++++++---------------------- 1 file changed, 102 insertions(+), 90 deletions(-) diff --git a/src/core/atomic.d b/src/core/atomic.d index f30e09caa6..d452a40606 100644 --- a/src/core/atomic.d +++ b/src/core/atomic.d @@ -264,9 +264,16 @@ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") } /** - * Stores 'writeThis' to the memory referenced by 'here' if the value - * referenced by 'here' is equal to 'ifThis'. This operation is both - * lock-free and atomic. + * Performs either compare-and-set or compare-and-swap (or exchange). + * + * There are two categories of overloads in this template: + * the first one does a simple compare-and-set, and returns a boolean if the + * operation happened. The value this is written (`writeThis`) can be an rvalue. + * the second category does a compare-and-swap, or compare-and-exchange, + * and expects `writeThis` 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. @@ -276,112 +283,117 @@ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") * Returns: * true if the store occurred, false if not. */ -bool cas(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") +template cas(MemoryOrder succ = MemoryOrder.seq, MemoryOrder fail = MemoryOrder.seq) { - // resolve implicit conversions - T arg1 = ifThis; - T arg2 = writeThis; - - static if (__traits(isFloating, T)) + /// 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") { - alias IntTy = IntForFloat!T; - return atomicCompareExchangeStrongNoResult!(succ, fail)(cast(IntTy*)here, *cast(IntTy*)&arg1, *cast(IntTy*)&arg2); + // resolve implicit conversions + 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); } - else - return atomicCompareExchangeStrongNoResult!(succ, fail)(here, arg1, arg2); -} -/// Ditto -bool cas(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(shared(T)* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @trusted + /// 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 + in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") { - 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 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); } - return cas!(succ, fail)(cast(T*)here, *cast(Thunk1*)&ifThis, *cast(Thunk2*)&writeThis); -} -/// Ditto -bool cas(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(shared(T)* here, shared(V1) ifThis, shared(V2) writeThis) pure nothrow @nogc @trusted + /// 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); -} + in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") + { + return atomicCompareExchangeStrongNoResult!(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. 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 cas(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V)(T* here, T* ifThis, V writeThis) pure nothrow @nogc @trusted + /// 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)) + in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") { - alias IntTy = IntForFloat!T; - return atomicCompareExchangeStrong!(succ, fail)(cast(IntTy*)here, cast(IntTy*)ifThis, *cast(IntTy*)&writeThis); + // 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); } - else - return atomicCompareExchangeStrong!(succ, fail)(here, ifThis, writeThis); -} -/// Ditto -bool cas(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V1,V2)(shared(T)* here, V1* ifThis, V2 writeThis) pure nothrow @nogc @trusted + /// 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 + in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") { - 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 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); } - static assert (is(T : Thunk1), "Mismatching types for `here` and `ifThis`: `" ~ shared(T).stringof ~ "` and `" ~ V1.stringof ~ "`."); - return cas!(succ, fail)(cast(T*)here, cast(Thunk1*)ifThis, *cast(Thunk2*)&writeThis); -} -/// Ditto -bool cas(MemoryOrder succ = MemoryOrder.seq,MemoryOrder fail = MemoryOrder.seq,T,V)(shared(T)* here, shared(T)* ifThis, shared(V) writeThis) pure nothrow @nogc @trusted + /// 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); + in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned") + { + return atomicCompareExchangeStrong!(succ, fail)( + cast(T*)here, cast(T*)ifThis, cast(V)writeThis); + } } /**