@@ -18,13 +18,15 @@ Suffixes are slower to get at because of alignment rounding, so prefixes should
18
18
be preferred. However, small prefixes blunt the alignment so if a large
19
19
alignment with a small affix is needed, suffixes should be chosen.
20
20
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`.
22
23
*/
23
24
struct AffixAllocator (Allocator, Prefix, Suffix = void )
24
25
{
25
26
import std.algorithm.comparison : min;
26
27
import std.conv : emplace;
27
- import std.experimental.allocator : RCIAllocator, theAllocator;
28
+ import std.experimental.allocator : RCIAllocator, RCISharedAllocator,
29
+ theAllocator, processAllocator;
28
30
import std.experimental.allocator.common : stateSize, forwardToMember,
29
31
roundUpToMultipleOf, alignedAt, alignDownTo, roundUpToMultipleOf,
30
32
hasStaticallyKnownAlignment;
@@ -43,7 +45,8 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
43
45
" This restriction could be relaxed in the future." );
44
46
45
47
/**
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.
47
50
*/
48
51
static if (hasStaticallyKnownAlignment! Allocator)
49
52
{
@@ -60,61 +63,96 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
60
63
enum uint alignment = Prefix.alignof;
61
64
}
62
65
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 ()
70
67
{
71
- Allocator _parent;
72
- static if (is (Allocator == RCIAllocator))
68
+ static if (stateSize! Allocator)
73
69
{
74
- @nogc nothrow pure @safe
75
- Allocator parent ()
70
+ Allocator _parent;
71
+
72
+ static if (is (Allocator == RCIAllocator) || is (Allocator == RCISharedAllocator))
76
73
{
77
- static @nogc nothrow
78
- RCIAllocator wrapAllocatorObject ()
74
+ static if (is (Allocator == RCISharedAllocator))
79
75
{
80
- import std.experimental.allocator.gc_allocator : GCAllocator;
81
- import std.experimental.allocator : allocatorObject;
82
-
83
- return allocatorObject (GCAllocator.instance) ;
76
+ // synchronization variables
77
+ // can't use Mutex or SpinLock because they aren't pure
78
+ shared bool nullParent = true ;
79
+ shared bool nullParentWait = true ;
84
80
}
85
81
86
- if (_parent.isNull)
82
+ @nogc nothrow pure @safe
83
+ Allocator parent ()
87
84
{
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();
85
+ static @nogc nothrow
86
+ RCIAllocator wrapAllocatorObject ()
87
+ {
88
+ import std.experimental.allocator.gc_allocator : GCAllocator;
89
+ import std.experimental.allocator : allocatorObject;
90
+
91
+ return allocatorObject (GCAllocator.instance);
92
+ }
93
+
94
+ static @nogc nothrow
95
+ RCISharedAllocator wrapProcAllocatorObject ()
96
+ {
97
+ import std.experimental.allocator.gc_allocator : GCAllocator;
98
+ import std.experimental.allocator : sharedAllocatorObject;
99
+
100
+ return sharedAllocatorObject (GCAllocator.instance);
101
+ }
102
+
103
+ if (_parent.isNull)
104
+ {
105
+ static if (is (Allocator == RCIAllocator))
106
+ {
107
+ // If the `_parent` allocator is `null` we will assign
108
+ // an object that references the GC as the `parent`.
109
+ auto fn = (() @trusted =>
110
+ cast (RCIAllocator function () @nogc nothrow pure @safe )(&wrapAllocatorObject))();
111
+ _parent = fn();
112
+ }
113
+ else
114
+ {
115
+ import core.atomic : cas, atomicLoad, atomicStore;
116
+
117
+ if ((() @trusted => cas(&nullParent, true , false ))())
118
+ {
119
+ auto fn = (() @trusted =>
120
+ cast (RCISharedAllocator function () @nogc nothrow pure @safe )
121
+ (&wrapProcAllocatorObject))();
122
+ _parent = fn();
123
+ // Notify other threads that the parent has been set
124
+ atomicStore(nullParentWait, false );
125
+ }
126
+ else
127
+ {
128
+ while (atomicLoad(nullParentWait))
129
+ {
130
+ // Busy-wait for the parent to be set
131
+ }
132
+ }
133
+ }
134
+ }
135
+
136
+ // `RCIAllocator.alignment` currently doesn't have any attributes
137
+ // so we must cast; throughout the allocators module, `alignment`
138
+ // is defined as an `enum` for the existing allocators.
139
+ // `alignment` should always be `@nogc nothrow pure @safe`; once
140
+ // this is enforced by the interface we can remove the cast
141
+ auto pureAlign = (() @trusted =>
142
+ cast (uint delegate () @nogc nothrow pure @safe )(&_parent.alignment))();
143
+ assert (alignment <= pureAlign());
144
+ return _parent;
93
145
}
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;
146
+ }
147
+ else
148
+ {
149
+ alias parent = _parent;
104
150
}
105
151
}
106
152
else
107
153
{
108
- alias parent = _parent ;
154
+ alias parent = Allocator.instance ;
109
155
}
110
- }
111
- else
112
- {
113
- alias parent = Allocator.instance;
114
- }
115
-
116
- private template Impl ()
117
- {
118
156
119
157
size_t goodAllocSize (size_t s)
120
158
{
@@ -323,6 +361,19 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
323
361
*/
324
362
static AffixAllocator instance;
325
363
364
+ /**
365
+ If the parent allocator `Allocator` is stateful, an instance of it is
366
+ stored as a member. If the parent allocator is null instance of
367
+ $(REF RCIAllocator, std,experimental,allocator) or
368
+ $(REF RCISharedAllocator, std,experimental,allocator) then `AffixAllocator`
369
+ will use $(REF GCAllocator, std,experimental,allocator,gc_allocator).
370
+ If the parent allocator `Allocator` is stateless, `AffixAllocator` uses
371
+ `Allocator.instance`.
372
+ In either case, the name `_parent` is uniformly used for accessing the
373
+ parent allocator.
374
+ */
375
+ Allocator parent ();
376
+
326
377
/**
327
378
Affix access functions offering references to the affixes of a
328
379
block `b` previously allocated with this allocator. `b` may not be null.
@@ -545,3 +596,45 @@ struct AffixAllocator(Allocator, Prefix, Suffix = void)
545
596
static assert (is (typeof (a.allocate) == shared ));
546
597
assert (buf.length == 10 );
547
598
}
599
+
600
+ @system unittest
601
+ {
602
+ import std.experimental.allocator.mallocator : Mallocator;
603
+ import std.experimental.allocator.building_blocks.stats_collector ;
604
+
605
+ alias SCAlloc = StatsCollector! (Mallocator, Options.bytesUsed);
606
+ alias AffixAl = AffixAllocator! (SCAlloc, uint );
607
+
608
+ AffixAl a;
609
+ auto b = a.allocate(42 );
610
+ assert (b.length == 42 );
611
+ assert (a.parent.bytesUsed == 42 + uint .sizeof);
612
+ assert ((() nothrow @nogc => a.reallocate(b, 100 ))());
613
+ assert (b.length == 100 );
614
+ assert (a.parent.bytesUsed == 100 + uint .sizeof);
615
+ assert ((() nothrow @nogc => a.deallocate(b))());
616
+ }
617
+
618
+ @system unittest
619
+ {
620
+ import std.experimental.allocator : RCISharedAllocator;
621
+ import core.thread : ThreadGroup ;
622
+
623
+ shared AffixAllocator! (RCISharedAllocator, size_t ) a;
624
+
625
+ void fun ()
626
+ {
627
+ enum allocSize = 42 ;
628
+ void [] b = a.allocate(allocSize);
629
+ assert (b.length == allocSize);
630
+ a.deallocate(b);
631
+ }
632
+
633
+ enum numThreads = 10 ;
634
+ auto tg = new ThreadGroup ;
635
+ foreach (i; 0 .. numThreads)
636
+ {
637
+ tg.create(&fun);
638
+ }
639
+ tg.joinAll();
640
+ }
0 commit comments