1 /*********************************************************
2 Copyright: (C) 2008-2010 by Steven Schveighoffer.
3 All rights reserved
4 5 License: Boost Software License version 1.0
6 7 Permission is hereby granted, free of charge, to any person or organization
8 obtaining a copy of the software and accompanying documentation covered by
9 this license (the "Software") to use, reproduce, display, distribute,
10 execute, and transmit the Software, and to prepare derivative works of the
11 Software, and to permit third-parties to whom the Software is furnished to
12 do so, all subject to the following:
13 14 The copyright notices in the Software and this entire statement, including
15 the above license grant, this restriction and the following disclaimer, must
16 be included in all copies of the Software, in whole or in part, and all
17 derivative works of the Software, unless such copies or derivative works are
18 solely in the form of machine-executable object code generated by a source
19 language processor.
20 21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 DEALINGS IN THE SOFTWARE.
28 29 **********************************************************/30 moduleEngine.Allocator;
31 32 version(DDoc)
33 {
34 }
35 else36 {
37 privateimportcore.memory;
38 privateimportstd.c..string;
39 }
40 41 /**
42 * Allocate a chunk of elements at once, then use the chunk to return
43 * elements. This makes allocating individual elements more efficient
44 * because the GC isn't used for allocating every element, only every
45 * chunk of elements.
46 *
47 * The only requirement is that the size of V is >= size of a pointer.
48 * This is because the data V contains is used as a pointer when freeing
49 * the element.
50 *
51 * If an entire chunk of elements is freed, that chunk is then returned to
52 * the GC.
53 */54 structChunkAllocator(V, uintelemsPerChunk)
55 {
56 /**
57 * Free is needed to recycle nodes for another allocation.
58 */59 enumboolfreeNeeded = true;
60 staticif(V.sizeof < (void*).sizeof)
61 {
62 staticassert(false, "Error, allocator for " ~ V.stringof ~ " failed to instantiate");
63 }
64 65 /**
66 * This is the form used to link recyclable elements together.
67 */68 structelement69 {
70 element *next;
71 }
72 73 /**
74 * A chunk of elements
75 */76 structchunk77 {
78 /**
79 * The next chunk in the chain
80 */81 chunk *next;
82 83 /**
84 * The previous chunk in the chain. Required for O(1) removal
85 * from the chain.
86 */87 chunk *prev;
88 89 /**
90 * The linked list of free elements in the chunk. This list is
91 * amended each time an element in this chunk is freed.
92 */93 element *freeList;
94 95 /**
96 * The number of free elements in the freeList. Used to determine
97 * whether this chunk can be given back to the GC
98 */99 uintnumFree;
100 101 /**
102 * The elements in the chunk.
103 */104 V[elemsPerChunk] elems;
105 106 /**
107 * Allocate a V* from the free list.
108 */109 V *allocateFromFree()
110 {
111 element *x = freeList;
112 freeList = x.next;
113 //114 // initialize the element. We do it this way to avoid triggering115 // any copy hooks such as postblits116 //117 autoinit = cast(ubyte[])typeid(V).init();
118 if(init.ptrisnull) // null ptr means initialize to 0119 x.next = null; // we already know the other bytes are 0120 else121 {
122 autobuf = (cast(ubyte *)x)[0..(V).sizeof];
123 buf[] = init[];
124 }
125 numFree--;
126 returncast(V*)x;
127 }
128 129 /**
130 * deallocate a V*, send it to the free list
131 *
132 * returns true if this chunk no longer has any used elements.
133 */134 booldeallocate(V *v)
135 {
136 //137 // clear the element so the GC does not interpret the element138 // as pointing to anything else.139 //140 memset(v, 0, (V).sizeof);
141 element *x = cast(element *)v;
142 x.next = freeList;
143 freeList = x;
144 return (++numFree == elemsPerChunk);
145 }
146 }
147 148 /**
149 * The chain of used chunks. Used chunks have had all their elements
150 * allocated at least once.
151 */152 chunk *used;
153 154 /**
155 * The fresh chunk. This is only used if no elements are available in
156 * the used chain.
157 */158 chunk *fresh;
159 160 /**
161 * The next element in the fresh chunk. Because we don't worry about
162 * the free list in the fresh chunk, we need to keep track of the next
163 * fresh element to use.
164 */165 uintnextFresh;
166 167 /**
168 * Allocate a V*
169 */170 V* allocate()
171 {
172 if(used !isnull && used.numFree > 0)
173 {
174 //175 // allocate one element of the used list176 //177 V* result = used.allocateFromFree();
178 if(used.numFree == 0)
179 //180 // move used to the end of the list181 //182 used = used.next;
183 returnresult;
184 }
185 186 //187 // no used elements are available, allocate out of the fresh188 // elements189 //190 if(freshisnull)
191 {
192 fresh = newchunk;
193 nextFresh = 0;
194 }
195 196 V* result = &fresh.elems[nextFresh];
197 if(++nextFresh == elemsPerChunk)
198 {
199 if(usedisnull)
200 {
201 used = fresh;
202 fresh.next = fresh;
203 fresh.prev = fresh;
204 }
205 else206 {
207 //208 // insert fresh into the used chain209 //210 fresh.prev = used.prev;
211 fresh.next = used;
212 fresh.prev.next = fresh;
213 fresh.next.prev = fresh;
214 if(fresh.numFree != 0)
215 {
216 //217 // can recycle elements from fresh218 //219 used = fresh;
220 }
221 }
222 fresh = null;
223 }
224 returnresult;
225 }
226 227 /**
228 * free a V*
229 */230 voidfree(V* v)
231 {
232 //233 // need to figure out which chunk v is in234 //235 chunk *cur = cast(chunk *)GC.addrOf(v);
236 237 if(cur !isfresh && cur.numFree == 0)
238 {
239 //240 // move cur to the front of the used list, it has free nodes241 // to be used.242 //243 if(cur !isused)
244 {
245 if(used !isnull)
246 {
247 //248 // first, unlink cur from its current location249 //250 cur.prev.next = cur.next;
251 cur.next.prev = cur.prev;
252 253 //254 // now, insert cur before used.255 //256 cur.prev = used.prev;
257 cur.next = used;
258 used.prev = cur;
259 cur.prev.next = cur;
260 }
261 else262 {
263 cur.next = cur.prev = cur;
264 }
265 used = cur;
266 }
267 }
268 269 if(cur.deallocate(v))
270 {
271 //272 // cur no longer has any elements in use, it can be deleted.273 //274 if(curisfresh || cur.nextiscur)
275 {
276 //277 // cur is either fresh or is the only element in the278 // used chain279 //280 }
281 else282 {
283 //284 // remove cur from list285 //286 if(usediscur)
287 {
288 //289 // update used pointer290 //291 used = used.next;
292 }
293 cur.next.prev = cur.prev;
294 cur.prev.next = cur.next;
295 deletecur;
296 }
297 }
298 }
299 300 /**
301 * Deallocate all chunks used by this allocator.
302 */303 voidfreeAll()
304 {
305 if(freshisnull)
306 {
307 fresh = used;
308 }
309 used = null;
310 311 //312 // keep fresh around313 //314 if(fresh !isnull)
315 {
316 nextFresh = 0;
317 // need to re-initialize the data elements318 autochunkinit = cast(ubyte[])typeid(chunk).init();
319 if(chunkinit.ptrisnull)
320 // all the data is zero, just write 0s to the chunk321 memset(fresh, 0, chunk.sizeof);
322 else323 {
324 // copy the entire init array325 autobuf = (cast(ubyte *)fresh)[0..chunk.sizeof];
326 buf[] = chunkinit[];
327 }
328 }
329 }
330 }