Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.
/ druntime Public archive
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/atomic_exchange.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Added `core.atomic.atomicExchange`.

Added missing `core.atomic.atomicExchange` function to the atomic suite.
185 changes: 185 additions & 0 deletions src/core/atomic.d
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@ version (D_InlineAsm_X86)
{
version = AsmX86;
version = AsmX86_32;
enum has64BitXCHG = false;
enum has64BitCAS = true;
enum has128BitCAS = false;
}
else version (D_InlineAsm_X86_64)
{
version = AsmX86;
version = AsmX86_64;
enum has64BitXCHG = true;
enum has64BitCAS = true;
enum has128BitCAS = true;
}
else
{
enum has64BitXCHG = false;
enum has64BitCAS = false;
enum has128BitCAS = false;
}
Expand Down Expand Up @@ -189,6 +192,27 @@ version (CoreDdoc)
return TailShared!T.init;
}

/**
* 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`.
*/
shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)( shared(T)* here, V exchangeWith ) pure nothrow @nogc @safe
if ( !is(T == class) && !is(T U : U*) && __traits( compiles, { *here = exchangeWith; } ) );

/// Ditto
shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)( shared(T)* here, shared(V) exchangeWith ) pure nothrow @nogc @safe
if ( is(T == class) && __traits( compiles, { *here = exchangeWith; } ) );

/// Ditto
shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)( shared(T)* here, shared(V)* exchangeWith ) pure nothrow @nogc @safe
if ( is(T U : U*) && __traits( compiles, { *here = exchangeWith; } ) );

/**
* Stores 'writeThis' to the memory referenced by 'here' if the value
Expand Down Expand Up @@ -375,6 +399,68 @@ else version (AsmX86_32)
}
}

shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)( shared(T)* here, V exchangeWith ) pure nothrow @nogc @safe
if ( !is(T == class) && !is(T U : U*) && __traits( compiles, { *here = exchangeWith; } ) )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure you've done your homework, are there any invalid memory orders for this operation? (I think I know the answer without checking anyway).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no invalid orders.
Infact, the order does not affect codegen in any way; it should be routed through to the backend to inform code mobility.

{
return atomicExchangeImpl(here, exchangeWith);
}

shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)( shared(T)* here, shared(V) exchangeWith ) pure nothrow @nogc @safe
if ( is(T == class) && __traits( compiles, { *here = exchangeWith; } ) )
{
return atomicExchangeImpl(here, exchangeWith);
}

shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)( shared(T)* here, shared(V)* exchangeWith ) pure nothrow @nogc @safe
if ( is(T U : U*) && __traits( compiles, { *here = exchangeWith; } ) )
{
return atomicExchangeImpl(here, exchangeWith);
}

private shared(T) atomicExchangeImpl(T,V)( shared(T)* here, V exchangeWith ) pure nothrow @nogc @safe
in ( atomicPtrIsProperlyAligned( here ), "Argument `here` is not properly aligned" )
{
static if ( T.sizeof == byte.sizeof )
{
asm pure nothrow @nogc @trusted
{
mov AL, exchangeWith;
mov ECX, here;
xchg [ECX], AL;
}
}
else static if ( T.sizeof == short.sizeof )
{
asm pure nothrow @nogc @trusted
{
mov AX, exchangeWith;
mov ECX, here;
xchg [ECX], AX;
}
}
else static if ( T.sizeof == int.sizeof )
{
asm pure nothrow @nogc @trusted
{
mov EAX, exchangeWith;
mov ECX, here;
xchg [ECX], EAX;
}
static if ( __traits(isFloating, T) )
{
asm pure nothrow @nogc @trusted
{
mov exchangeWith, EAX;
}
return exchangeWith;
}
}
else
{
static assert( false, "Invalid template type specified." );
}
}

bool casByRef(T,V1,V2)( ref T value, V1 ifThis, V2 writeThis ) pure nothrow @nogc @trusted
{
return cas(&value, ifThis, writeThis);
Expand Down Expand Up @@ -866,6 +952,85 @@ else version (AsmX86_64)
}
}

shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)( shared(T)* here, V exchangeWith ) pure nothrow @nogc @safe
if ( !is(T == class) && !is(T U : U*) && __traits( compiles, { *here = exchangeWith; } ) )
{
return atomicExchangeImpl(here, exchangeWith);
}

shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)( shared(T)* here, shared(V) exchangeWith ) pure nothrow @nogc @safe
if ( is(T == class) && __traits( compiles, { *here = exchangeWith; } ) )
{
return atomicExchangeImpl(here, exchangeWith);
}

shared(T) atomicExchange(MemoryOrder ms = MemoryOrder.seq,T,V)( shared(T)* here, shared(V)* exchangeWith ) pure nothrow @nogc @safe
if ( is(T U : U*) && __traits( compiles, { *here = exchangeWith; } ) )
{
return atomicExchangeImpl(here, exchangeWith);
}

private shared(T) atomicExchangeImpl(T,V)( shared(T)* here, V exchangeWith ) pure nothrow @nogc @safe
in ( atomicPtrIsProperlyAligned( here ), "Argument `here` is not properly aligned" )
do
{
static if ( T.sizeof == byte.sizeof )
{
asm pure nothrow @nogc @trusted
{
mov AL, exchangeWith;
mov RCX, here;
xchg [RCX], AL;
}
}
else static if ( T.sizeof == short.sizeof )
{
asm pure nothrow @nogc @trusted
{
mov AX, exchangeWith;
mov RCX, here;
xchg [RCX], AX;
}
}
else static if ( T.sizeof == int.sizeof )
{
asm pure nothrow @nogc @trusted
{
mov EAX, exchangeWith;
mov RCX, here;
xchg [RCX], EAX;
}
static if ( __traits(isFloating, T) )
{
asm pure nothrow @nogc @trusted
{
mov exchangeWith, EAX;
movss XMM0, exchangeWith;
}
}
}
else static if ( T.sizeof == long.sizeof )
{
asm pure nothrow @nogc @trusted
{
mov RAX, exchangeWith;
mov RCX, here;
xchg [RCX], RAX;
}
static if ( __traits(isFloating, T) )
{
asm pure nothrow @nogc @trusted
{
mov exchangeWith, RAX;
movsd XMM0, exchangeWith;
}
}
}
else
{
static assert( false, "Invalid template type specified." );
}
}

bool casByRef(T,V1,V2)( ref T value, V1 ifThis, V2 writeThis ) pure nothrow @nogc @trusted
{
Expand Down Expand Up @@ -1432,6 +1597,23 @@ if (__traits(isFloating, T))

version (unittest)
{
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 );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe do

auto x = atomicExchange( &atom, val );
static if (is(T == float))
    printf("%a, %a\n",x, base);
    assert(x is base, T.stringof);

and see what it thinks?

assert( atom is val, T.stringof );
}

void testCAS(T)( T val ) pure nothrow @nogc @trusted
in
{
Expand Down Expand Up @@ -1468,6 +1650,8 @@ version (unittest)

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 );
Expand All @@ -1493,6 +1677,7 @@ version (unittest)
testType!(shared int*)();

static class Klass {}
testXCHG!(shared Klass)( new shared(Klass) );
testCAS!(shared Klass)( new shared(Klass) );

testType!(float)(1.0f);
Expand Down