Skip to content

Commit 8076b6f

Browse files
committed
Make AffixAllocator take into account RCISharedAllocator
1 parent 4549c4b commit 8076b6f

File tree

1 file changed

+145
-45
lines changed

1 file changed

+145
-45
lines changed

std/experimental/allocator/building_blocks/affix_allocator.d

Lines changed: 145 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ Suffixes are slower to get at because of alignment rounding, so prefixes should
1818
be preferred. However, small prefixes blunt the alignment so if a large
1919
alignment with a small affix is needed, suffixes should be chosen.
2020
21-
The following methods are defined if `Allocator` defines them, and forward to it: `deallocateAll`, `empty`, `owns`.
21+
The following methods are defined if `Allocator` defines them,
22+
and forwarded to it: `deallocateAll`, `empty`, `owns`.
2223
*/
2324
struct AffixAllocator(Allocator, Prefix, Suffix = void)
2425
{
2526
import std.algorithm.comparison : min;
2627
import std.conv : emplace;
27-
import std.experimental.allocator : RCIAllocator, theAllocator;
28+
import std.experimental.allocator : RCIAllocator, RCISharedAllocator,
29+
theAllocator, processAllocator;
2830
import std.experimental.allocator.common : stateSize, forwardToMember,
2931
roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf,
3032
hasStaticallyKnownAlignment;
@@ -43,7 +45,8 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
4345
"This restriction could be relaxed in the future.");
4446

4547
/**
46-
If `Prefix` is `void`, the alignment is that of the parent. Otherwise, the alignment is the same as the `Prefix`'s alignment.
48+
If `Prefix` is `void`, the alignment is that of the parent.
49+
Otherwise, the alignment is the same as the `Prefix`'s alignment.
4750
*/
4851
static if (hasStaticallyKnownAlignment!Allocator)
4952
{
@@ -60,61 +63,103 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
6063
enum uint alignment = Prefix.alignof;
6164
}
6265

63-
/**
64-
If the parent allocator `Allocator` is stateful, an instance of it is
65-
stored as a member. Otherwise, `AffixAllocator` uses
66-
`Allocator.instance`. In either case, the name `_parent` is uniformly
67-
used for accessing the parent allocator.
68-
*/
69-
static if (stateSize!Allocator)
66+
private template Impl()
7067
{
71-
Allocator _parent;
72-
static if (is(Allocator == RCIAllocator))
68+
static if (stateSize!Allocator)
7369
{
74-
@nogc nothrow pure @safe
75-
Allocator parent()
70+
Allocator _parent;
71+
72+
static if (is(Allocator == RCIAllocator) || is(Allocator == RCISharedAllocator))
7673
{
77-
static @nogc nothrow
78-
RCIAllocator wrapAllocatorObject()
74+
static if (is(Allocator == RCISharedAllocator))
7975
{
80-
import std.experimental.allocator.gc_allocator : GCAllocator;
81-
import std.experimental.allocator : allocatorObject;
82-
83-
return allocatorObject(GCAllocator.instance);
76+
// synchronization variable
77+
// can't use Mutex or SpinLock because they aren't pure
78+
private shared bool _lockVar = true;
79+
80+
@nogc nothrow pure @trusted
81+
private static void parentLock(ref shared bool _lockVar)
82+
{
83+
import core.atomic : cas;
84+
while (!cas(&_lockVar, true, false))
85+
{
86+
// Busy-wait for the parent to be set
87+
}
88+
}
89+
90+
@nogc nothrow pure @trusted
91+
private static void parentUnlock(ref shared bool _lockVar)
92+
{
93+
import core.atomic : atomicStore;
94+
atomicStore(_lockVar, true);
95+
}
8496
}
8597

86-
if (_parent.isNull)
98+
@nogc nothrow pure @safe
99+
Allocator parent()
87100
{
88-
// If the `_parent` allocator is `null` we will assign
89-
// an object that references the GC as the `parent`.
90-
auto fn = (() @trusted =>
91-
cast(RCIAllocator function() @nogc nothrow pure @safe)(&wrapAllocatorObject))();
92-
_parent = fn();
101+
static @nogc nothrow
102+
RCIAllocator wrapAllocatorObject()
103+
{
104+
import std.experimental.allocator.gc_allocator : GCAllocator;
105+
import std.experimental.allocator : allocatorObject;
106+
107+
return allocatorObject(GCAllocator.instance);
108+
}
109+
110+
static @nogc nothrow
111+
RCISharedAllocator wrapProcAllocatorObject()
112+
{
113+
import std.experimental.allocator.gc_allocator : GCAllocator;
114+
import std.experimental.allocator : sharedAllocatorObject;
115+
116+
return sharedAllocatorObject(GCAllocator.instance);
117+
}
118+
119+
static if (is(Allocator == RCIAllocator))
120+
{
121+
if (_parent.isNull)
122+
{
123+
// If the `_parent` allocator is `null` we will assign
124+
// an object that references the GC as the `parent`.
125+
auto fn = (() @trusted =>
126+
cast(RCIAllocator function() @nogc nothrow pure @safe)(&wrapAllocatorObject))();
127+
_parent = fn();
128+
}
129+
}
130+
else
131+
{
132+
parentLock(_lockVar);
133+
if (_parent.isNull)
134+
{
135+
auto fn = (() @trusted =>
136+
cast(RCISharedAllocator function() @nogc nothrow pure @safe)
137+
(&wrapProcAllocatorObject))();
138+
_parent = fn();
139+
}
140+
parentUnlock(_lockVar);
141+
}
142+
143+
// `RCIAllocator.alignment` currently doesn't have any attributes
144+
// so we must cast; throughout the allocators module, `alignment`
145+
// is defined as an `enum` for the existing allocators.
146+
// `alignment` should always be `@nogc nothrow pure @safe`; once
147+
// this is enforced by the interface we can remove the cast
148+
auto pureAlign = (() @trusted =>
149+
cast(uint delegate() @nogc nothrow pure @safe)(&_parent.alignment))();
150+
assert(alignment <= pureAlign());
151+
return _parent;
93152
}
94-
95-
// `RCIAllocator.alignment` currently doesn't have any attributes
96-
// so we must cast; throughout the allocators module, `alignment`
97-
// is defined as an `enum` for the existing allocators.
98-
// `alignment` should always be `@nogc nothrow pure @safe`; once
99-
// this is enforced by the interface we can remove the cast
100-
auto pureAlign = (() @trusted =>
101-
cast(uint delegate() @nogc nothrow pure @safe)(&_parent.alignment))();
102-
assert(alignment <= pureAlign());
103-
return _parent;
153+
}
154+
else
155+
{
156+
alias parent = _parent;
104157
}
105158
}
106159
else
107160
{
108-
alias parent = _parent;
161+
alias parent = Allocator.instance;
109162
}
110-
}
111-
else
112-
{
113-
alias parent = Allocator.instance;
114-
}
115-
116-
private template Impl()
117-
{
118163

119164
size_t goodAllocSize(size_t s)
120165
{
@@ -323,6 +368,19 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
323368
*/
324369
static AffixAllocator instance;
325370

371+
/**
372+
If the parent allocator `Allocator` is stateful, an instance of it is
373+
stored as a member. If the parent allocator is null instance of
374+
$(REF RCIAllocator, std,experimental,allocator) or
375+
$(REF RCISharedAllocator, std,experimental,allocator) then `AffixAllocator`
376+
will use $(REF GCAllocator, std,experimental,allocator,gc_allocator).
377+
If the parent allocator `Allocator` is stateless, `AffixAllocator` uses
378+
`Allocator.instance`.
379+
In either case, the name `_parent` is uniformly used for accessing the
380+
parent allocator.
381+
*/
382+
Allocator parent();
383+
326384
/**
327385
Affix access functions offering references to the affixes of a
328386
block `b` previously allocated with this allocator. `b` may not be null.
@@ -545,3 +603,45 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
545603
static assert(is(typeof(a.allocate) == shared));
546604
assert(buf.length == 10);
547605
}
606+
607+
@system unittest
608+
{
609+
import std.experimental.allocator.mallocator : Mallocator;
610+
import std.experimental.allocator.building_blocks.stats_collector;
611+
612+
alias SCAlloc = StatsCollector!(Mallocator, Options.bytesUsed);
613+
alias AffixAl = AffixAllocator!(SCAlloc, uint);
614+
615+
AffixAl a;
616+
auto b = a.allocate(42);
617+
assert(b.length == 42);
618+
assert(a.parent.bytesUsed == 42 + uint.sizeof);
619+
assert((() nothrow @nogc => a.reallocate(b, 100))());
620+
assert(b.length == 100);
621+
assert(a.parent.bytesUsed == 100 + uint.sizeof);
622+
assert((() nothrow @nogc => a.deallocate(b))());
623+
}
624+
625+
@system unittest
626+
{
627+
import std.experimental.allocator : RCISharedAllocator;
628+
import core.thread : ThreadGroup;
629+
630+
shared AffixAllocator!(RCISharedAllocator, size_t) a;
631+
632+
void fun()
633+
{
634+
enum allocSize = 42;
635+
void[] b = a.allocate(allocSize);
636+
assert(b.length == allocSize);
637+
a.deallocate(b);
638+
}
639+
640+
enum numThreads = 100;
641+
auto tg = new ThreadGroup;
642+
foreach (i; 0 .. numThreads)
643+
{
644+
tg.create(&fun);
645+
}
646+
tg.joinAll();
647+
}

0 commit comments

Comments
 (0)