1 /// 2 module stdx.allocator.building_blocks.scoped_allocator; 3 4 import stdx.allocator.common; 5 6 /** 7 8 $(D ScopedAllocator) delegates all allocation requests to $(D ParentAllocator). 9 When destroyed, the $(D ScopedAllocator) object automatically calls $(D 10 deallocate) for all memory allocated through its lifetime. (The $(D 11 deallocateAll) function is also implemented with the same semantics.) 12 13 $(D deallocate) is also supported, which is where most implementation effort 14 and overhead of $(D ScopedAllocator) go. If $(D deallocate) is not needed, a 15 simpler design combining $(D AllocatorList) with $(D Region) is recommended. 16 17 */ 18 struct ScopedAllocator(ParentAllocator) 19 { 20 @system unittest 21 { 22 testAllocator!(() => ScopedAllocator()); 23 } 24 25 import stdx.allocator.building_blocks.affix_allocator 26 : AffixAllocator; 27 import stdx.allocator.internal : Ternary; 28 29 private struct Node 30 { 31 Node* prev; 32 Node* next; 33 size_t length; 34 } 35 36 alias Allocator = AffixAllocator!(ParentAllocator, Node); 37 38 // state 39 /** 40 If $(D ParentAllocator) is stateful, $(D parent) is a property giving access 41 to an $(D AffixAllocator!ParentAllocator). Otherwise, $(D parent) is an alias for `AffixAllocator!ParentAllocator.instance`. 42 */ 43 static if (stateSize!ParentAllocator) 44 { 45 Allocator parent; 46 } 47 else 48 { 49 alias parent = Allocator.instance; 50 } 51 private Node* root; 52 53 /** 54 $(D ScopedAllocator) is not copyable. 55 */ 56 @disable this(this); 57 58 /** 59 $(D ScopedAllocator)'s destructor releases all memory allocated during its 60 lifetime. 61 */ 62 ~this() 63 { 64 deallocateAll; 65 } 66 67 /// Alignment offered 68 enum alignment = Allocator.alignment; 69 70 /** 71 Forwards to $(D parent.goodAllocSize) (which accounts for the management 72 overhead). 73 */ 74 size_t goodAllocSize(size_t n) 75 { 76 return parent.goodAllocSize(n); 77 } 78 79 /** 80 Allocates memory. For management it actually allocates extra memory from 81 the parent. 82 */ 83 void[] allocate(size_t n) 84 { 85 auto b = parent.allocate(n); 86 if (!b.ptr) return b; 87 Node* toInsert = & parent.prefix(b); 88 toInsert.prev = null; 89 toInsert.next = root; 90 toInsert.length = n; 91 assert(!root || !root.prev); 92 if (root) root.prev = toInsert; 93 root = toInsert; 94 return b; 95 } 96 97 /** 98 Forwards to $(D parent.expand(b, delta)). 99 */ 100 static if (__traits(hasMember, Allocator, "expand")) 101 bool expand(ref void[] b, size_t delta) 102 { 103 auto result = parent.expand(b, delta); 104 if (result && b.ptr) 105 { 106 parent.prefix(b).length = b.length; 107 } 108 return result; 109 } 110 111 /** 112 Reallocates $(D b) to new size $(D s). 113 */ 114 bool reallocate(ref void[] b, size_t s) 115 { 116 // Remove from list 117 if (b.ptr) 118 { 119 Node* n = & parent.prefix(b); 120 if (n.prev) n.prev.next = n.next; 121 else root = n.next; 122 if (n.next) n.next.prev = n.prev; 123 } 124 auto result = parent.reallocate(b, s); 125 // Add back to list 126 if (b.ptr) 127 { 128 Node* n = & parent.prefix(b); 129 n.prev = null; 130 n.next = root; 131 n.length = s; 132 if (root) root.prev = n; 133 root = n; 134 } 135 return result; 136 } 137 138 /** 139 Forwards to $(D parent.owns(b)). 140 */ 141 static if (__traits(hasMember, Allocator, "owns")) 142 Ternary owns(void[] b) 143 { 144 return parent.owns(b); 145 } 146 147 /** 148 Deallocates $(D b). 149 */ 150 static if (__traits(hasMember, Allocator, "deallocate")) 151 bool deallocate(void[] b) 152 { 153 // Remove from list 154 if (b.ptr) 155 { 156 Node* n = & parent.prefix(b); 157 if (n.prev) n.prev.next = n.next; 158 else root = n.next; 159 if (n.next) n.next.prev = n.prev; 160 } 161 return parent.deallocate(b); 162 } 163 164 /** 165 Deallocates all memory allocated. 166 */ 167 bool deallocateAll() 168 { 169 bool result = true; 170 for (auto n = root; n; ) 171 { 172 void* p = n + 1; 173 auto length = n.length; 174 n = n.next; 175 if (!parent.deallocate(p[0 .. length])) 176 result = false; 177 } 178 root = null; 179 return result; 180 } 181 182 /** 183 Returns `Ternary.yes` if this allocator is not responsible for any memory, 184 `Ternary.no` otherwise. (Never returns `Ternary.unknown`.) 185 */ 186 Ternary empty() const 187 { 188 return Ternary(root is null); 189 } 190 } 191 192 /// 193 @system unittest 194 { 195 import stdx.allocator.mallocator : Mallocator; 196 import stdx.allocator.internal : Ternary; 197 ScopedAllocator!Mallocator alloc; 198 assert(alloc.empty == Ternary.yes); 199 const b = alloc.allocate(10); 200 assert(b.length == 10); 201 assert(alloc.empty == Ternary.no); 202 } 203 204 @system unittest 205 { 206 import stdx.allocator.gc_allocator : GCAllocator; 207 testAllocator!(() => ScopedAllocator!GCAllocator()); 208 } 209 210 @system unittest // https://issues.dlang.org/show_bug.cgi?id=16046 211 { 212 import stdx.allocator; 213 import stdx.allocator.mallocator; 214 ScopedAllocator!Mallocator alloc; 215 auto foo = alloc.make!int(1); 216 auto bar = alloc.make!int(2); 217 assert(foo); 218 assert(bar); 219 alloc.dispose(foo); 220 alloc.dispose(bar); // segfault here 221 }