@@ -290,6 +290,58 @@ version (CoreDdoc)
290
290
* loads and stores after the call.
291
291
*/
292
292
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
+ }
293
345
}
294
346
else version (AsmX86_32)
295
347
{
@@ -1432,6 +1484,19 @@ if (__traits(isFloating, T))
1432
1484
}
1433
1485
}
1434
1486
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
+
1435
1500
// //////////////////////////////////////////////////////////////////////////////
1436
1501
// Unit Tests
1437
1502
// //////////////////////////////////////////////////////////////////////////////
@@ -1734,4 +1799,81 @@ version (unittest)
1734
1799
shared NoIndirections n;
1735
1800
static assert (is (typeof (atomicLoad(n)) == NoIndirections));
1736
1801
}
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
+ }
1737
1879
}
0 commit comments