1 /** 2 FreeList allocator proxy templates used to prevent memory segmentation 3 on base allocator. 4 5 Copyright: © 2012-2013 RejectedSoftware e.K. 6 © 2014-2015 Etienne Cimon 7 License: Subject to the terms of the MIT license. 8 Authors: Sönke Ludwig, Etienne Cimon 9 */ 10 module memutils.freelist; 11 12 import memutils.allocators; 13 import std.algorithm : min; 14 15 final class AutoFreeListAllocator(Base : Allocator) : Allocator { 16 import std.typetuple; 17 18 private { 19 enum minExponent = 3; 20 enum freeListCount = 12; 21 FreeListAlloc!Base[freeListCount] m_freeLists; 22 Base m_baseAlloc; 23 } 24 25 this() 26 { 27 m_baseAlloc = getAllocator!Base(); 28 foreach (i; iotaTuple!freeListCount) 29 m_freeLists[i] = new FreeListAlloc!Base(nthFreeListSize!(i)); 30 } 31 32 ~this() { 33 foreach(fl; m_freeLists) 34 destroy(fl); 35 } 36 37 void[] alloc(size_t sz) 38 { 39 logTrace("AFL alloc ", sz); 40 if (sz > nthFreeListSize!(freeListCount-1)) return m_baseAlloc.alloc(sz); 41 foreach (i; iotaTuple!freeListCount) 42 if (sz <= nthFreeListSize!(i)) 43 return m_freeLists[i].alloc().ptr[0 .. sz]; 44 assert(false); 45 } 46 47 void[] realloc(void[] data, size_t sz) 48 { 49 foreach (fl; m_freeLists) { 50 if (data.length <= fl.elementSize) { 51 // just grow the slice if it still fits into the free list slot 52 if (sz <= fl.elementSize) 53 return data.ptr[0 .. sz]; 54 55 // otherwise re-allocate 56 auto newd = alloc(sz); 57 assert(newd.ptr+sz <= data.ptr || newd.ptr >= data.ptr+data.length, "New block overlaps old one!?"); 58 auto len = min(data.length, sz); 59 newd[0 .. len] = data[0 .. len]; 60 free(data); 61 return newd; 62 } 63 } 64 // forward large blocks to the base allocator 65 return m_baseAlloc.realloc(data, sz); 66 } 67 68 void free(void[] data) 69 { 70 logTrace("AFL free ", data.length); 71 if (data.length > nthFreeListSize!(freeListCount-1)) { 72 m_baseAlloc.free(data); 73 return; 74 } 75 foreach(i; iotaTuple!freeListCount) { 76 if (data.length <= nthFreeListSize!i) { 77 m_freeLists[i].free(data.ptr[0 .. nthFreeListSize!i]); 78 return; 79 } 80 } 81 assert(false); 82 } 83 84 private static pure size_t nthFreeListSize(size_t i)() { return 1 << (i + minExponent); } 85 private template iotaTuple(size_t i) { 86 static if (i > 1) alias iotaTuple = TypeTuple!(iotaTuple!(i-1), i-1); 87 else alias iotaTuple = TypeTuple!(0); 88 } 89 } 90 91 final class FreeListAlloc(Base : Allocator) : Allocator 92 { 93 import memutils.vector : Vector; 94 import memutils.utils : Malloc; 95 private static struct FreeListSlot { FreeListSlot* next; } 96 private { 97 immutable size_t m_elemSize; 98 Base m_baseAlloc; 99 FreeListSlot* m_firstFree = null; 100 size_t[] m_owned; 101 size_t m_nalloc = 0; 102 size_t m_nfree = 0; 103 } 104 105 ~this() { 106 import core.thread : thread_isMainThread; 107 if (!thread_isMainThread) 108 foreach(size_t slot; m_owned) { 109 m_baseAlloc.free( (cast(void*)slot)[0 .. m_elemSize]); 110 } 111 } 112 113 this(size_t elem_size) 114 { 115 assert(elem_size >= size_t.sizeof); 116 m_elemSize = elem_size; 117 m_baseAlloc = getAllocator!Base(); 118 //logTrace("Create FreeListAlloc %d", m_elemSize); 119 } 120 121 @property size_t elementSize() const { return m_elemSize; } 122 123 void[] alloc(size_t sz) 124 { 125 assert(sz == m_elemSize, "Invalid allocation size."); 126 return alloc(); 127 } 128 129 void[] alloc() 130 { 131 void[] mem; 132 if( m_firstFree ){ 133 auto slot = m_firstFree; 134 m_firstFree = slot.next; 135 slot.next = null; 136 mem = (cast(void*)slot)[0 .. m_elemSize]; 137 m_nfree--; 138 } else { 139 mem = m_baseAlloc.alloc(m_elemSize); 140 m_owned ~= cast(size_t)mem.ptr; 141 //logInfo("Alloc %d bytes: alloc: %d, free: %d", SZ, s_nalloc, s_nfree); 142 } 143 m_nalloc++; 144 //logInfo("Alloc %d bytes: alloc: %d, free: %d", SZ, s_nalloc, s_nfree); 145 return mem; 146 } 147 148 void[] realloc(void[] mem, size_t sz) 149 { 150 assert(mem.length == m_elemSize); 151 assert(sz == m_elemSize); 152 return mem; 153 } 154 155 void free(void[] mem) 156 { 157 assert(mem.length == m_elemSize, "Memory block passed to free has wrong size."); 158 auto s = cast(FreeListSlot*)mem.ptr; 159 s.next = m_firstFree; 160 m_firstFree = s; 161 m_nalloc--; 162 m_nfree++; 163 } 164 }