From 664fadf12f7d4a004dcfdf9983629d5ac0cac6d4 Mon Sep 17 00:00:00 2001 From: Stanislav Blinov Date: Sat, 15 Feb 2014 10:09:57 +0400 Subject: [PATCH 1/5] assumeLocal: convert shared lvalue to a non-shared one --- src/core/atomic.d | 64 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/core/atomic.d b/src/core/atomic.d index a36d07f162..8ffb245947 100644 --- a/src/core/atomic.d +++ b/src/core/atomic.d @@ -151,6 +151,42 @@ version( CoreDdoc ) * loads and stores after the call. */ void atomicFence() nothrow; + + /** + * Converts a shared lvalue to a non-shared lvalue. + * + * As the name suggests, 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. under a lock). + * --- + * shared static int i; + * + * synchronized + * { + * ++i; // ERROR: cannot directly modify shared lvalue + * + * atomicOp!"+="(i, 1); // possible overhead + * + * // Directly modify i + * assumeLocal(i) += 1; + * // or: + * ++assumeLocal(i); + * } + * --- + * Note: this function does not perform any ordering. + * + * 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; + } } else version( AsmX86_32 ) { @@ -1090,6 +1126,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 + if( !is( T == class ) ) +{ + return *cast(T*)&val; +} + //////////////////////////////////////////////////////////////////////////////// // Unit Tests //////////////////////////////////////////////////////////////////////////////// @@ -1246,4 +1289,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))); + } } From ac3cccb5a54d338dbe187077ffefc2caa73c531e Mon Sep 17 00:00:00 2001 From: Stanislav Blinov Date: Sun, 16 Feb 2014 03:43:30 +0400 Subject: [PATCH 2/5] Fixed documentation --- src/core/atomic.d | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/atomic.d b/src/core/atomic.d index 8ffb245947..41f4262994 100644 --- a/src/core/atomic.d +++ b/src/core/atomic.d @@ -155,13 +155,14 @@ version( CoreDdoc ) /** * Converts a shared lvalue to a non-shared lvalue. * - * As the name suggests, this functions allows to treat a shared lvalue - * as if it was thread-local. + * 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. under a lock). + * 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 @@ -172,10 +173,19 @@ version( CoreDdoc ) * assumeLocal(i) += 1; * // or: * ++assumeLocal(i); + * // or: + * i.assumeLocal += 1; * } * --- + * Usage of this function is restricted to allow 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: $(D 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. * From eaab61770bed5e813cce1f3eea9ab5c00a4da3f0 Mon Sep 17 00:00:00 2001 From: Stanislav Blinov Date: Sun, 16 Feb 2014 09:11:44 +0400 Subject: [PATCH 3/5] More doc fixes; stray whitespace --- src/core/atomic.d | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/atomic.d b/src/core/atomic.d index 41f4262994..f30905b7ac 100644 --- a/src/core/atomic.d +++ b/src/core/atomic.d @@ -153,9 +153,9 @@ version( CoreDdoc ) void atomicFence() nothrow; /** - * Converts a shared lvalue to a non-shared lvalue. + * Converts a shared lvalue to a non-shared lvalue. * - * This functions allows to treat a shared lvalue as if it was thread-local. + * 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). * --- @@ -177,12 +177,12 @@ version( CoreDdoc ) * i.assumeLocal += 1; * } * --- - * Usage of this function is restricted to allow limited lvalue access to shared instances of + * 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: $(D assumeLocal) is a special-purpose primitive and should be used with care. When accessing + * 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. * @@ -1308,7 +1308,7 @@ version( unittest ) // 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); From edb108b6dafcbba2f892bb81f2f1b90adaa00ed3 Mon Sep 17 00:00:00 2001 From: Stanislav Blinov Date: Thu, 13 Apr 2017 02:10:57 +0300 Subject: [PATCH 4/5] Small doc fix --- src/core/atomic.d | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/atomic.d b/src/core/atomic.d index fe5a8f9939..f322ac8611 100644 --- a/src/core/atomic.d +++ b/src/core/atomic.d @@ -218,6 +218,12 @@ version( CoreDdoc ) * * This is a specialization for immutable lvalues. Since immutable is implicitly * unshared, this version accepts any type (even classes and interfaces). + * + * Params: + * val = the shared lvalue. + * + * Returns: + * The non-shared lvalue. */ ref immutable(T) assumeUnshared(T)(ref immutable(T) val) @safe @nogc pure nothrow { From b2fc618eae481ac85afbdc40aef2b58794bdd1b7 Mon Sep 17 00:00:00 2001 From: Stanislav Blinov Date: Thu, 13 Apr 2017 02:35:51 +0300 Subject: [PATCH 5/5] Space after close paren in cast --- src/core/atomic.d | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/atomic.d b/src/core/atomic.d index f322ac8611..fe4118d02c 100644 --- a/src/core/atomic.d +++ b/src/core/atomic.d @@ -206,7 +206,7 @@ version( CoreDdoc ) ref T assumeUnshared(T)(ref shared T val) @system @nogc pure nothrow if(!is(T == class) && !is(T == interface)) { - return *cast(T*)&val; + return *cast(T*) &val; } /** @@ -1361,7 +1361,7 @@ if(__traits(isFloating, T)) ref auto assumeUnshared(T)(ref shared T val) @system @nogc pure nothrow if(!is(T == class) && !is(T == interface)) { - return *cast(T*)&val; + return *cast(T*) &val; } // immutable is implicitly unshared