@@ -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,19 @@ if(__traits(isFloating, T))
14091461 }
14101462}
14111463
1464+ // assumeUnshared is architecture-independent: it is just a cast
1465+ ref auto assumeUnshared (T)(ref shared T val) @system @nogc pure nothrow
1466+ if (! is (T == class ) && ! is (T == interface ))
1467+ {
1468+ return * cast (T* ) &val;
1469+ }
1470+
1471+ // immutable is implicitly unshared
1472+ ref auto assumeUnshared(T)(ref immutable (T) val) @safe @nogc pure nothrow
1473+ {
1474+ return val;
1475+ }
1476+
14121477// //////////////////////////////////////////////////////////////////////////////
14131478// Unit Tests
14141479// //////////////////////////////////////////////////////////////////////////////
@@ -1711,4 +1776,81 @@ version( unittest )
17111776 shared NoIndirections n;
17121777 static assert (is (typeof (atomicLoad(n)) == NoIndirections));
17131778 }
1779+
1780+ pure nothrow @nogc @system unittest
1781+ {
1782+ int base = 0 ;
1783+ shared int atom = 0 ;
1784+
1785+ // only accept shared lvalues
1786+ static assert (! is (typeof (assumeUnshared(base))));
1787+ static assert (! is (typeof (assumeUnshared(cast (shared )base))));
1788+
1789+ ++ assumeUnshared(atom);
1790+ assert (atomicLoad! (MemoryOrder.raw)(atom) == 1 );
1791+ }
1792+
1793+ pure nothrow @nogc @system unittest
1794+ {
1795+ shared const int catom = 0 ;
1796+ shared immutable int iatom = 0 ;
1797+ // allow const
1798+ static assert (is (typeof (assumeUnshared(catom))));
1799+ static assert (is (typeof (assumeUnshared(iatom))));
1800+ // preserve const
1801+ static assert (! is (typeof (++ assumeUnshared(catom))));
1802+ static assert (! is (typeof (++ assumeUnshared(iatom))));
1803+ }
1804+
1805+ pure nothrow @nogc @system unittest
1806+ {
1807+ class Klass {}
1808+
1809+ Klass c1;
1810+ shared Klass c2;
1811+
1812+ // don't accept class instances
1813+ static assert (! is (typeof (assumeUnshared(c1))));
1814+ static assert (! is (typeof (assumeUnshared(c2))));
1815+ }
1816+
1817+ pure nothrow @nogc @system unittest
1818+ {
1819+ interface Interface {}
1820+ Interface i1;
1821+ shared Interface i2;
1822+
1823+ // don't accept interfaces
1824+ static assert (! is (typeof (assumeUnshared(i1))));
1825+ static assert (! is (typeof (assumeUnshared(i2))));
1826+ }
1827+
1828+ pure nothrow @nogc @system unittest
1829+ {
1830+ // test assumeShared with inout
1831+ shared struct S
1832+ {
1833+ int atom = 0 ;
1834+
1835+ @property ref get () inout
1836+ {
1837+ return atom.assumeUnshared;
1838+ }
1839+ }
1840+
1841+ shared S sm;
1842+ shared const S sc;
1843+ shared immutable S si;
1844+
1845+ static assert (is (typeof (sm.get ) == int ));
1846+ static assert (is (typeof (sc.get ) == const (int )));
1847+ static assert (is (typeof (si.get ) == immutable (int )));
1848+
1849+ static assert ( is (typeof (++ sm.get )));
1850+ static assert (! is (typeof (++ sc.get )));
1851+ static assert (! is (typeof (++ si.get )));
1852+
1853+ sm.get += 10 ;
1854+ assert (atomicLoad! (MemoryOrder.raw)(sm.atom) == 10 );
1855+ }
17141856}
0 commit comments