Skip to content
This repository has been 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.
Jump to
Jump to file
Failed to load files.
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)));
}
}