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

Commit 9d4f162

Browse files
radcapricornwilzbach
authored andcommitted
assumeUnshared: convert shared lvalue to a non-shared one
1 parent f6f537f commit 9d4f162

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
@@ -267,6 +267,58 @@ version( CoreDdoc )
267267
* loads and stores after the call.
268268
*/
269269
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+
}
270322
}
271323
else version( AsmX86_32 )
272324
{
@@ -1409,6 +1461,19 @@ if(__traits(isFloating, T))
14091461
}
14101462
}
14111463

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+
14121477
////////////////////////////////////////////////////////////////////////////////
14131478
// Unit Tests
14141479
////////////////////////////////////////////////////////////////////////////////
@@ -1711,4 +1776,81 @@ version( unittest )
17111776
shared NoIndirections n;
17121777
static assert(is(typeof(atomicLoad(n)) == NoIndirections));
17131778
}
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+
}
17141856
}

0 commit comments

Comments
 (0)