Skip to content
This repository was archived by the owner on Oct 12, 2022. It is now read-only.

Commit 063171a

Browse files
radcapricornRazvanN7
authored andcommitted
assumeUnshared: convert shared lvalue to a non-shared one
1 parent 7f6e50d commit 063171a

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
`core.atomic.assumeUnshared` has been added
2+
3+
$(REF assumeUnshared, core,atomic) allows to convert a shared lvalue to a non-shared lvalue
4+
5+
---
6+
shared static int i;
7+
8+
// i is never used outside of synchronized {} blocks...
9+
10+
synchronized
11+
{
12+
++i; // ERROR: cannot directly modify shared lvalue
13+
14+
atomicOp!"+="(i, 1); // possible overhead
15+
16+
// Directly modify i
17+
assumeUnshared(i) += 1;
18+
// or:
19+
++assumeUnshared(i);
20+
// or:
21+
i.assumeUnshared += 1;
22+
}
23+
---

src/core/atomic.d

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)