From 9d4f1623baa35a7098612754f4a91a8a8ae156f1 Mon Sep 17 00:00:00 2001 From: Stanislav Blinov Date: Sat, 15 Feb 2014 10:09:57 +0400 Subject: [PATCH] assumeUnshared: convert shared lvalue to a non-shared one --- changelog/core-atomic-assumeUnshared.dd | 23 ++++ src/core/atomic.d | 142 ++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 changelog/core-atomic-assumeUnshared.dd diff --git a/changelog/core-atomic-assumeUnshared.dd b/changelog/core-atomic-assumeUnshared.dd new file mode 100644 index 00000000000..0c4cc7d0971 --- /dev/null +++ b/changelog/core-atomic-assumeUnshared.dd @@ -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; +} +--- diff --git a/src/core/atomic.d b/src/core/atomic.d index 06d70de0090..a501812b59f 100644 --- a/src/core/atomic.d +++ b/src/core/atomic.d @@ -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 ) { @@ -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 //////////////////////////////////////////////////////////////////////////////// @@ -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); + } }