@@ -263,6 +263,52 @@ in (atomicPtrIsProperlyAligned(here), "Argument `here` is not properly aligned")
263263 return cast (shared )core.internal.atomic.atomicExchange ! ms(cast (T* )here, cast (V)exchangeWith);
264264}
265265
266+ /**
267+ * Converts a shared lvalue to a non-shared lvalue.
268+ *
269+ * This functions allows to treat a shared lvalue as if it was thread-local.
270+ * It is useful to avoid overhead of atomic operations when access to shared data
271+ * is known to be within one thread (i.e. always under a lock).
272+ * ---
273+ * shared static int i;
274+ *
275+ * // i is never used outside of synchronized {} blocks...
276+ *
277+ * synchronized
278+ * {
279+ * ++i; // ERROR: cannot directly modify shared lvalue
280+ *
281+ * atomicOp!"+="(i, 1); // possible overhead
282+ *
283+ * // Directly modify i
284+ * assumeUnshared(i) += 1;
285+ * // or:
286+ * ++assumeUnshared(i);
287+ * // or:
288+ * i.assumeUnshared += 1;
289+ * }
290+ * ---
291+ * Usage of this function is restricted to allowing limited lvalue access to shared instances of
292+ * primitive and POD types (e.g. direct use of operators), thus it is not defined for classes.
293+ *
294+ * Note: this function does not perform any ordering.
295+ *
296+ * Note: assumeUnshared is a special-purpose primitive and should be used with care. When accessing
297+ * shared variables both inside and outside of synchronized blocks, atomic operations should be
298+ * used instead.
299+ *
300+ * Params:
301+ * val = the shared lvalue.
302+ *
303+ * Returns:
304+ * The non-shared lvalue.
305+ */
306+ ref T assumeUnshared (T)(ref shared T val) @system @nogc pure nothrow
307+ if (! is (T == class ) && ! is (T == interface ))
308+ {
309+ return * cast (T* ) &val;
310+ }
311+
266312/**
267313 * Performs either compare-and-set or compare-and-swap (or exchange).
268314 *
@@ -1276,4 +1322,81 @@ version (CoreUnittest)
12761322 shared NoIndirections n;
12771323 static assert (is (typeof (atomicLoad(n)) == NoIndirections));
12781324 }
1325+
1326+ pure nothrow @nogc @system unittest
1327+ {
1328+ int base = 0 ;
1329+ shared int atom = 0 ;
1330+
1331+ // only accept shared lvalues
1332+ static assert (! is (typeof (assumeUnshared(base))));
1333+ static assert (! is (typeof (assumeUnshared(cast (shared )base))));
1334+
1335+ ++ assumeUnshared(atom);
1336+ assert (atomicLoad! (MemoryOrder.raw)(atom) == 1 );
1337+ }
1338+
1339+ pure nothrow @nogc @system unittest
1340+ {
1341+ shared const int catom = 0 ;
1342+ shared immutable int iatom = 0 ;
1343+ // allow const
1344+ static assert (is (typeof (assumeUnshared(catom))));
1345+ static assert (is (typeof (assumeUnshared(iatom))));
1346+ // preserve const
1347+ static assert (! is (typeof (++ assumeUnshared(catom))));
1348+ static assert (! is (typeof (++ assumeUnshared(iatom))));
1349+ }
1350+
1351+ pure nothrow @nogc @system unittest
1352+ {
1353+ class Klass {}
1354+
1355+ Klass c1;
1356+ shared Klass c2;
1357+
1358+ // don't accept class instances
1359+ static assert (! is (typeof (assumeUnshared(c1))));
1360+ static assert (! is (typeof (assumeUnshared(c2))));
1361+ }
1362+
1363+ pure nothrow @nogc @system unittest
1364+ {
1365+ interface Interface {}
1366+ Interface i1;
1367+ shared Interface i2;
1368+
1369+ // don't accept interfaces
1370+ static assert (! is (typeof (assumeUnshared(i1))));
1371+ static assert (! is (typeof (assumeUnshared(i2))));
1372+ }
1373+
1374+ pure nothrow @nogc @system unittest
1375+ {
1376+ // test assumeShared with inout
1377+ shared struct S
1378+ {
1379+ int atom = 0 ;
1380+
1381+ @property ref get () inout
1382+ {
1383+ return atom.assumeUnshared;
1384+ }
1385+ }
1386+
1387+ shared S sm;
1388+ shared const S sc;
1389+ shared immutable S si;
1390+
1391+ static assert (is (typeof (sm.get ) == int ));
1392+ static assert (is (typeof (sc.get ) == const (int )));
1393+ static assert (is (typeof (si.get ) == immutable (int )));
1394+
1395+ static assert ( is (typeof (++ sm.get )));
1396+ static assert (! is (typeof (++ sc.get )));
1397+ static assert (! is (typeof (++ si.get )));
1398+
1399+ sm.get += 10 ;
1400+ assert (atomicLoad! (MemoryOrder.raw)(sm.atom) == 10 );
1401+ }
12791402}
0 commit comments