@@ -267,6 +267,58 @@ version( CoreDdoc )
267267 * loads and stores after the call.
268268 */
269269 void atomicFence () nothrow @nogc ;
270+
271+ /**
272+ * Converts a shared lvalue to a non-shared lvalue.
273+ *
274+ * This functions allows to treat a shared lvalue as if it was thread-local.
275+ * It is useful to avoid overhead of atomic operations when access to shared data
276+ * is known to be within one thread (i.e. always under a lock).
277+ * ---
278+ * shared static int i;
279+ *
280+ * // i is never used outside of synchronized {} blocks...
281+ *
282+ * synchronized
283+ * {
284+ * ++i; // ERROR: cannot directly modify shared lvalue
285+ *
286+ * atomicOp!"+="(i, 1); // possible overhead
287+ *
288+ * // Directly modify i
289+ * assumeUnshared(i) += 1;
290+ * // or:
291+ * ++assumeUnshared(i);
292+ * // or:
293+ * i.assumeUnshared += 1;
294+ * }
295+ * ---
296+ * Usage of this function is restricted to allowing limited lvalue access to shared instances of
297+ * primitive and POD types (e.g. direct use of operators), thus it is not defined for classes.
298+ *
299+ * Note: this function does not perform any ordering.
300+ *
301+ * Note: assumeUnshared is a special-purpose primitive and should be used with care. When accessing
302+ * shared variables both inside and outside of synchronized blocks, atomic operations should be
303+ * used instead.
304+ *
305+ * Params:
306+ * val = the shared lvalue.
307+ *
308+ * Returns:
309+ * The non-shared lvalue.
310+ */
311+ ref T assumeUnshared (T)(ref shared T val) @system @nogc pure nothrow
312+ if (! is (T == class ) && ! is (T == interface ))
313+ {
314+ return * cast (T* ) &val;
315+ }
316+
317+ // / ditto
318+ ref immutable (T) assumeUnshared (T)(ref immutable (T) val) @safe @nogc pure nothrow
319+ {
320+ return val;
321+ }
270322}
271323else version ( AsmX86_32 )
272324{
@@ -1409,6 +1461,26 @@ if(__traits(isFloating, T))
14091461 }
14101462}
14111463
1464+ // assumeLocal is architecture-independent: it is just a cast
1465+ ref auto assumeLocal (T)( ref shared T val ) @trusted pure nothrow
1466+ if ( ! is ( T == class ) )
1467+ {
1468+ return * cast (T* ) &val;
1469+ }
1470+
1471+ // assumeUnshared is architecture-independent: it is just a cast
1472+ ref auto assumeUnshared (T)(ref shared T val) @system @nogc pure nothrow
1473+ if (! is (T == class ) && ! is (T == interface ))
1474+ {
1475+ return * cast (T* ) &val;
1476+ }
1477+
1478+ // immutable is implicitly unshared
1479+ ref auto assumeUnshared(T)(ref immutable (T) val) @safe @nogc pure nothrow
1480+ {
1481+ return val;
1482+ }
1483+
14121484// //////////////////////////////////////////////////////////////////////////////
14131485// Unit Tests
14141486// //////////////////////////////////////////////////////////////////////////////
@@ -1711,4 +1783,81 @@ version( unittest )
17111783 shared NoIndirections n;
17121784 static assert (is (typeof (atomicLoad(n)) == NoIndirections));
17131785 }
1786+
1787+ pure nothrow @nogc @system unittest
1788+ {
1789+ int base = 0 ;
1790+ shared int atom = 0 ;
1791+
1792+ // only accept shared lvalues
1793+ static assert (! is (typeof (assumeUnshared(base))));
1794+ static assert (! is (typeof (assumeUnshared(cast (shared )base))));
1795+
1796+ ++ assumeUnshared(atom);
1797+ assert (atomicLoad! (MemoryOrder.raw)(atom) == 1 );
1798+ }
1799+
1800+ pure nothrow @nogc @system unittest
1801+ {
1802+ shared const int catom = 0 ;
1803+ shared immutable int iatom = 0 ;
1804+ // allow const
1805+ static assert (is (typeof (assumeUnshared(catom))));
1806+ static assert (is (typeof (assumeUnshared(iatom))));
1807+ // preserve const
1808+ static assert (! is (typeof (++ assumeUnshared(catom))));
1809+ static assert (! is (typeof (++ assumeUnshared(iatom))));
1810+ }
1811+
1812+ pure nothrow @nogc @system unittest
1813+ {
1814+ class Klass {}
1815+
1816+ Klass c1;
1817+ shared Klass c2;
1818+
1819+ // don't accept class instances
1820+ static assert (! is (typeof (assumeUnshared(c1))));
1821+ static assert (! is (typeof (assumeUnshared(c2))));
1822+ }
1823+
1824+ pure nothrow @nogc @system unittest
1825+ {
1826+ interface Interface {}
1827+ Interface i1;
1828+ shared Interface i2;
1829+
1830+ // don't accept interfaces
1831+ static assert (! is (typeof (assumeUnshared(i1))));
1832+ static assert (! is (typeof (assumeUnshared(i2))));
1833+ }
1834+
1835+ pure nothrow @nogc @system unittest
1836+ {
1837+ // test assumeShared with inout
1838+ shared struct S
1839+ {
1840+ int atom = 0 ;
1841+
1842+ @property ref get () inout
1843+ {
1844+ return atom.assumeUnshared;
1845+ }
1846+ }
1847+
1848+ shared S sm;
1849+ shared const S sc;
1850+ shared immutable S si;
1851+
1852+ static assert (is (typeof (sm.get ) == int ));
1853+ static assert (is (typeof (sc.get ) == const (int )));
1854+ static assert (is (typeof (si.get ) == immutable (int )));
1855+
1856+ static assert ( is (typeof (++ sm.get )));
1857+ static assert (! is (typeof (++ sc.get )));
1858+ static assert (! is (typeof (++ si.get )));
1859+
1860+ sm.get += 10 ;
1861+ assert (atomicLoad! (MemoryOrder.raw)(sm.atom) == 10 );
1862+ }
17141863}
0 commit comments