@@ -267,6 +267,58 @@ version( CoreDdoc )
267
267
* loads and stores after the call.
268
268
*/
269
269
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
+ }
270
322
}
271
323
else version ( AsmX86_32 )
272
324
{
@@ -1409,6 +1461,19 @@ if(__traits(isFloating, T))
1409
1461
}
1410
1462
}
1411
1463
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
+
1412
1477
// //////////////////////////////////////////////////////////////////////////////
1413
1478
// Unit Tests
1414
1479
// //////////////////////////////////////////////////////////////////////////////
@@ -1711,4 +1776,81 @@ version( unittest )
1711
1776
shared NoIndirections n;
1712
1777
static assert (is (typeof (atomicLoad(n)) == NoIndirections));
1713
1778
}
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
+ }
1714
1856
}
0 commit comments