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

Commit 106d4ff

Browse files
radcapricornwilzbach
authored andcommitted
assumeUnshared: convert shared lvalue to a non-shared one
1 parent 523ecda commit 106d4ff

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-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: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}
294346
else 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

Comments
 (0)