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

assumeLocal: convert shared lvalue to a non-shared one #724

Closed
wants to merge 6 commits into from
Closed
Changes from 3 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
74 changes: 74 additions & 0 deletions src/core/atomic.d
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,52 @@ version( CoreDdoc )
* loads and stores after the call.
*/
void atomicFence() nothrow;

/**
* 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
* assumeLocal(i) += 1;
Copy link

Choose a reason for hiding this comment

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

You could also write i.assumeLocal += 1. Whatever floats one's boat.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, to be updated then :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, regarding documentation, should I add myself in Authors block? Or put this block in function documentation? Or?..

Copy link
Member

Choose a reason for hiding this comment

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

should I add myself in Authors block?

No, git history has made that practice obsolete.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. thanks. Docs updated.

Copy link
Member

Choose a reason for hiding this comment

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

In general, it's a good idea to add yourself to the authors list if you've contributed a fairly large amount of code to a module and you're willing to take on a sort of maintainer role for at least the code that you've contributed. Git history isn't helpful for that.

* // or:
* ++assumeLocal(i);
* // or:
* i.assumeLocal += 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: assumeLocal 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 assumeLocal(T)( ref shared T val ) @trusted pure nothrow
if( !is( T == class ) )
{
return *cast(T*)&val;
Copy link
Member

Choose a reason for hiding this comment

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

space after close paren in cast

}
}
else version( AsmX86_32 )
{
Expand Down Expand Up @@ -1090,6 +1136,13 @@ if(__traits(isFloating, T))
}
}

// assumeLocal is architecture-independent: it is just a cast
ref auto assumeLocal(T)( ref shared T val ) @trusted pure nothrow
Copy link
Member

Choose a reason for hiding this comment

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

I don't see how this can be trusted

if( !is( T == class ) )
Copy link
Member

Choose a reason for hiding this comment

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

 if (!is(T == class))

{
return *cast(T*)&val;
}

////////////////////////////////////////////////////////////////////////////////
// Unit Tests
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1246,4 +1299,25 @@ version( unittest )

assert(*r == 42);
}

unittest
{
int base = 0;
shared int atom = 0;

// only accept shared lvalues
static assert(!__traits(compiles, assumeLocal(base)));
static assert(!__traits(compiles, assumeLocal(cast(shared)base)));

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

static class Klass {}
auto c1 = new Klass;
auto c2 = new shared Klass;

// don't accept class instances
static assert(!__traits(compiles, assumeLocal(c1)));
static assert(!__traits(compiles, assumeLocal(c2)));
}
}