@@ -290,6 +290,58 @@ version (CoreDdoc)
290290 * loads and stores after the call.
291291 */
292292 void atomicFence () nothrow @nogc ;
293+
294+ /**
295+ * Converts a shared lvalue to a non-shared lvalue.
296+ *
297+ * This functions allows to treat a shared lvalue as if it was thread-local.
298+ * It is useful to avoid overhead of atomic operations when access to shared data
299+ * is known to be within one thread (i.e. always under a lock).
300+ * ---
301+ * shared static int i;
302+ *
303+ * // i is never used outside of synchronized {} blocks...
304+ *
305+ * synchronized
306+ * {
307+ * ++i; // ERROR: cannot directly modify shared lvalue
308+ *
309+ * atomicOp!"+="(i, 1); // possible overhead
310+ *
311+ * // Directly modify i
312+ * assumeUnshared(i) += 1;
313+ * // or:
314+ * ++assumeUnshared(i);
315+ * // or:
316+ * i.assumeUnshared += 1;
317+ * }
318+ * ---
319+ * Usage of this function is restricted to allowing limited lvalue access to shared instances of
320+ * primitive and POD types (e.g. direct use of operators), thus it is not defined for classes.
321+ *
322+ * Note: this function does not perform any ordering.
323+ *
324+ * Note: assumeUnshared is a special-purpose primitive and should be used with care. When accessing
325+ * shared variables both inside and outside of synchronized blocks, atomic operations should be
326+ * used instead.
327+ *
328+ * Params:
329+ * val = the shared lvalue.
330+ *
331+ * Returns:
332+ * The non-shared lvalue.
333+ */
334+ ref T assumeUnshared (T)(ref shared T val) @system @nogc pure nothrow
335+ if (! is (T == class ) && ! is (T == interface ))
336+ {
337+ return * cast (T* ) &val;
338+ }
339+
340+ // / ditto
341+ ref immutable (T) assumeUnshared (T)(ref immutable (T) val) @safe @nogc pure nothrow
342+ {
343+ return val;
344+ }
293345}
294346else version (AsmX86_32)
295347{
@@ -1432,6 +1484,19 @@ if (__traits(isFloating, T))
14321484 }
14331485}
14341486
1487+ // assumeUnshared is architecture-independent: it is just a cast
1488+ ref auto assumeUnshared (T)(ref shared T val) @system @nogc pure nothrow
1489+ if (! is (T == class ) && ! is (T == interface ))
1490+ {
1491+ return * cast (T* ) &val;
1492+ }
1493+
1494+ // immutable is implicitly unshared
1495+ ref auto assumeUnshared(T)(ref immutable (T) val) @safe @nogc pure nothrow
1496+ {
1497+ return val;
1498+ }
1499+
14351500// //////////////////////////////////////////////////////////////////////////////
14361501// Unit Tests
14371502// //////////////////////////////////////////////////////////////////////////////
@@ -1734,4 +1799,81 @@ version (unittest)
17341799 shared NoIndirections n;
17351800 static assert (is (typeof (atomicLoad(n)) == NoIndirections));
17361801 }
1802+
1803+ pure nothrow @nogc @system unittest
1804+ {
1805+ int base = 0 ;
1806+ shared int atom = 0 ;
1807+
1808+ // only accept shared lvalues
1809+ static assert (! is (typeof (assumeUnshared(base))));
1810+ static assert (! is (typeof (assumeUnshared(cast (shared )base))));
1811+
1812+ ++ assumeUnshared(atom);
1813+ assert (atomicLoad! (MemoryOrder.raw)(atom) == 1 );
1814+ }
1815+
1816+ pure nothrow @nogc @system unittest
1817+ {
1818+ shared const int catom = 0 ;
1819+ shared immutable int iatom = 0 ;
1820+ // allow const
1821+ static assert (is (typeof (assumeUnshared(catom))));
1822+ static assert (is (typeof (assumeUnshared(iatom))));
1823+ // preserve const
1824+ static assert (! is (typeof (++ assumeUnshared(catom))));
1825+ static assert (! is (typeof (++ assumeUnshared(iatom))));
1826+ }
1827+
1828+ pure nothrow @nogc @system unittest
1829+ {
1830+ class Klass {}
1831+
1832+ Klass c1;
1833+ shared Klass c2;
1834+
1835+ // don't accept class instances
1836+ static assert (! is (typeof (assumeUnshared(c1))));
1837+ static assert (! is (typeof (assumeUnshared(c2))));
1838+ }
1839+
1840+ pure nothrow @nogc @system unittest
1841+ {
1842+ interface Interface {}
1843+ Interface i1;
1844+ shared Interface i2;
1845+
1846+ // don't accept interfaces
1847+ static assert (! is (typeof (assumeUnshared(i1))));
1848+ static assert (! is (typeof (assumeUnshared(i2))));
1849+ }
1850+
1851+ pure nothrow @nogc @system unittest
1852+ {
1853+ // test assumeShared with inout
1854+ shared struct S
1855+ {
1856+ int atom = 0 ;
1857+
1858+ @property ref get () inout
1859+ {
1860+ return atom.assumeUnshared;
1861+ }
1862+ }
1863+
1864+ shared S sm;
1865+ shared const S sc;
1866+ shared immutable S si;
1867+
1868+ static assert (is (typeof (sm.get ) == int ));
1869+ static assert (is (typeof (sc.get ) == const (int )));
1870+ static assert (is (typeof (si.get ) == immutable (int )));
1871+
1872+ static assert ( is (typeof (++ sm.get )));
1873+ static assert (! is (typeof (++ sc.get )));
1874+ static assert (! is (typeof (++ si.get )));
1875+
1876+ sm.get += 10 ;
1877+ assert (atomicLoad! (MemoryOrder.raw)(sm.atom) == 10 );
1878+ }
17371879}
0 commit comments