@@ -18,13 +18,15 @@ Suffixes are slower to get at because of alignment rounding, so prefixes should
1818be preferred. However, small prefixes blunt the alignment so if a large
1919alignment 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 */
2324struct 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