Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
assumeUnshared: convert shared lvalue to a non-shared one
Browse files Browse the repository at this point in the history
  • Loading branch information
radcapricorn authored and wilzbach committed Mar 29, 2018
1 parent f6f537f commit 9d4f162
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 0 deletions.
23 changes: 23 additions & 0 deletions changelog/core-atomic-assumeUnshared.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
`core.atomic.assumeUnshared` has been added

$(REF assumeUnshared, core,atomic) allows to convert a shared lvalue to a non-shared lvalue

---
shared static int i;

// i is never used outside of synchronized {} blocks...

synchronized
{
++i; // ERROR: cannot directly modify shared lvalue

atomicOp!"+="(i, 1); // possible overhead

// Directly modify i
assumeUnshared(i) += 1;
// or:
++assumeUnshared(i);
// or:
i.assumeUnshared += 1;
}
---
142 changes: 142 additions & 0 deletions src/core/atomic.d
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,58 @@ version( CoreDdoc )
* loads and stores after the call.
*/
void atomicFence() nothrow @nogc;

/**
* Converts a shared lvalue to a non-shared lvalue.
*
* This functions allows to treat a shared lvalue as if it was thread-local.
* It is useful to avoid overhead of atomic operations when access to shared data
* is known to be within one thread (i.e. always under a lock).
* ---
* shared static int i;
*
* // i is never used outside of synchronized {} blocks...
*
* synchronized
* {
* ++i; // ERROR: cannot directly modify shared lvalue
*
* atomicOp!"+="(i, 1); // possible overhead
*
* // Directly modify i
* assumeUnshared(i) += 1;
* // or:
* ++assumeUnshared(i);
* // or:
* i.assumeUnshared += 1;
* }
* ---
* Usage of this function is restricted to allowing limited lvalue access to shared instances of
* primitive and POD types (e.g. direct use of operators), thus it is not defined for classes.
*
* Note: this function does not perform any ordering.
*
* Note: assumeUnshared is a special-purpose primitive and should be used with care. When accessing
* shared variables both inside and outside of synchronized blocks, atomic operations should be
* used instead.
*
* Params:
* val = the shared lvalue.
*
* Returns:
* The non-shared lvalue.
*/
ref T assumeUnshared(T)(ref shared T val) @system @nogc pure nothrow
if(!is(T == class) && !is(T == interface))
{
return *cast(T*) &val;
}

/// ditto
ref immutable(T) assumeUnshared(T)(ref immutable(T) val) @safe @nogc pure nothrow
{
return val;
}
}
else version( AsmX86_32 )
{
Expand Down Expand Up @@ -1409,6 +1461,19 @@ if(__traits(isFloating, T))
}
}

// assumeUnshared is architecture-independent: it is just a cast
ref auto assumeUnshared(T)(ref shared T val) @system @nogc pure nothrow
if(!is(T == class) && !is(T == interface))
{
return *cast(T*) &val;
}

// immutable is implicitly unshared
ref auto assumeUnshared(T)(ref immutable(T) val) @safe @nogc pure nothrow
{
return val;
}

////////////////////////////////////////////////////////////////////////////////
// Unit Tests
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1711,4 +1776,81 @@ version( unittest )
shared NoIndirections n;
static assert(is(typeof(atomicLoad(n)) == NoIndirections));
}

pure nothrow @nogc @system unittest
{
int base = 0;
shared int atom = 0;

// only accept shared lvalues
static assert(!is(typeof(assumeUnshared(base))));
static assert(!is(typeof(assumeUnshared(cast(shared)base))));

++assumeUnshared(atom);
assert(atomicLoad!(MemoryOrder.raw)(atom) == 1);
}

pure nothrow @nogc @system unittest
{
shared const int catom = 0;
shared immutable int iatom = 0;
// allow const
static assert(is(typeof(assumeUnshared(catom))));
static assert(is(typeof(assumeUnshared(iatom))));
// preserve const
static assert(!is(typeof(++assumeUnshared(catom))));
static assert(!is(typeof(++assumeUnshared(iatom))));
}

pure nothrow @nogc @system unittest
{
class Klass {}

Klass c1;
shared Klass c2;

// don't accept class instances
static assert(!is(typeof(assumeUnshared(c1))));
static assert(!is(typeof(assumeUnshared(c2))));
}

pure nothrow @nogc @system unittest
{
interface Interface {}
Interface i1;
shared Interface i2;

// don't accept interfaces
static assert(!is(typeof(assumeUnshared(i1))));
static assert(!is(typeof(assumeUnshared(i2))));
}

pure nothrow @nogc @system unittest
{
// test assumeShared with inout
shared struct S
{
int atom = 0;

@property ref get() inout
{
return atom.assumeUnshared;
}
}

shared S sm;
shared const S sc;
shared immutable S si;

static assert(is(typeof(sm.get) == int));
static assert(is(typeof(sc.get) == const(int)));
static assert(is(typeof(si.get) == immutable(int)));

static assert( is(typeof(++sm.get)));
static assert(!is(typeof(++sc.get)));
static assert(!is(typeof(++si.get)));

sm.get += 10;
assert(atomicLoad!(MemoryOrder.raw)(sm.atom) == 10);
}
}

0 comments on commit 9d4f162

Please sign in to comment.