1 /** 2 Utility functions for memory management 3 4 Copyright: © 2012-2013 RejectedSoftware e.K. 5 © 2014-2015 Etienne Cimon 6 License: Subject to the terms of the MIT license. 7 Authors: Sönke Ludwig, Etienne Cimon 8 */ 9 module memutils.allocators; 10 11 public import memutils.constants; 12 import core.thread : Fiber; 13 import core.exception : OutOfMemoryError; 14 import core.stdc.stdlib; 15 import core.memory; 16 import core.sync.mutex; 17 import std.conv; 18 import std.traits; 19 import std.algorithm; 20 import std.traits : ReturnType; 21 import memutils.hashmap : HashMap; 22 import memutils.pool; 23 import memutils.memory; 24 import memutils.debugger; 25 import memutils.cryptosafe; 26 import memutils.freelist; 27 import memutils.utils : Malloc; 28 import core.thread : thread_isMainThread; 29 30 static if (HasDebugAllocations) { 31 pragma(msg, "Memory debugger enabled"); 32 alias LocklessAllocator = DebugAllocator!(AutoFreeListAllocator!(MallocAllocator)); 33 static if (HasCryptoSafe) 34 alias CryptoSafeAllocator = DebugAllocator!(SecureAllocator!(AutoFreeListAllocator!(MallocAllocator))); 35 alias ProxyGCAllocator = DebugAllocator!GCAllocator; 36 37 } 38 else { 39 alias LocklessAllocator = AutoFreeListAllocator!(MallocAllocator); 40 static if (HasCryptoSafe) 41 alias CryptoSafeAllocator = SecureAllocator!LocklessAllocator; 42 alias ProxyGCAllocator = GCAllocator; 43 } 44 45 interface Allocator { 46 enum size_t alignment = 0x10; 47 48 enum size_t alignmentMask = alignment-1; 49 50 void[] alloc(size_t sz) 51 out { 52 static if (!HasSecurePool && !HasBotan) assert((cast(size_t)__result.ptr & alignmentMask) == 0, "alloc() returned misaligned data."); 53 } 54 55 void[] realloc(void[] mem, size_t new_sz) 56 in { 57 assert(mem.ptr !is null, "realloc() called with null array."); 58 static if (!HasSecurePool && !HasBotan) assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to realloc()."); 59 } 60 out { static if (!HasSecurePool && !HasBotan) assert((cast(size_t)__result.ptr & alignmentMask) == 0, "realloc() returned misaligned data."); } 61 62 void free(void[] mem) 63 in { 64 assert(mem.ptr !is null, "free() called with null array."); 65 static if (!HasSecurePool && !HasBotan) assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to free()."); 66 } 67 } 68 69 package: 70 71 version(TLSGC) { } else { 72 public import core.sync.mutex : Mutex; 73 __gshared Mutex mtx; 74 } 75 76 pragma(inline, true) 77 public auto getAllocator(int ALLOC)(bool is_freeing = false) { 78 static if (ALLOC == LocklessFreeList) alias R = LocklessAllocator; 79 else static if (ALLOC == NativeGC) alias R = ProxyGCAllocator; 80 else static if (HasCryptoSafe && ALLOC == CryptoSafe) alias R = CryptoSafeAllocator; 81 else static if (ALLOC == Mallocator) alias R = MallocAllocator; 82 else static assert(false, "Invalid allocator specified"); 83 return getAllocator!R(is_freeing); 84 } 85 86 R getAllocator(R)(bool is_freeing = false, bool kill_it = false) { 87 version(TLSGC) 88 static R alloc; 89 else static __gshared R alloc; 90 91 static bool deinit; 92 if (kill_it) {alloc.destroy(); deinit = true; alloc = null; return null; } 93 if (!alloc && !is_freeing) { 94 alloc = new R; 95 } 96 97 return alloc; 98 } 99 100 version(TLSGC) 101 static ~this() { 102 getAllocator!CryptoSafeAllocator(false, true); 103 getAllocator!LocklessAllocator(false, true); 104 } 105 106 size_t alignedSize(size_t sz) 107 { 108 return ((sz + Allocator.alignment - 1) / Allocator.alignment) * Allocator.alignment; 109 } 110 111 void ensureValidMemory(void[] mem) 112 { 113 auto bytes = cast(ubyte[])mem; 114 swap(bytes[0], bytes[$-1]); 115 swap(bytes[0], bytes[$-1]); 116 } 117 118 void* extractUnalignedPointer(void* base) 119 { 120 ubyte misalign = *(cast(const(ubyte)*)base-1); 121 assert(misalign <= Allocator.alignment); 122 return base - misalign; 123 } 124 125 void* adjustPointerAlignment(void* base, ubyte* misalign_ = null) 126 { 127 ubyte misalign = Allocator.alignment - (cast(size_t)base & Allocator.alignmentMask); 128 base += misalign; 129 if (misalign_) *misalign_ = misalign; 130 else *(cast(ubyte*)base-1) = misalign; 131 return base; 132 } 133 134 template AllocSize(T) 135 { 136 static if (is(T == class)) { 137 // workaround for a strange bug where AllocSize!SSLStream == 0: TODO: dustmite! 138 enum dummy = T.stringof ~ __traits(classInstanceSize, T).stringof; 139 enum AllocSize = __traits(classInstanceSize, T); 140 } else { 141 enum AllocSize = T.sizeof; 142 } 143 } 144 145 template RefTypeOf(T) { 146 static if( is(T == class) || __traits(isAbstractClass, T) || is(T == interface) ){ 147 alias RefTypeOf = T; 148 } else { 149 alias RefTypeOf = T*; 150 } 151 } 152 153 154 unittest { 155 void testAlign(void* p, size_t adjustment) { 156 void* pa = adjustPointerAlignment(p); 157 assert((cast(size_t)pa & Allocator.alignmentMask) == 0, "Non-aligned pointer."); 158 assert(*(cast(const(ubyte)*)pa-1) == adjustment, "Invalid adjustment "~to!string(p)~": "~to!string(*(cast(const(ubyte)*)pa-1))); 159 void* pr = extractUnalignedPointer(pa); 160 assert(pr == p, "Recovered base != original"); 161 } 162 void* ptr = .malloc(0x40); 163 ptr += Allocator.alignment - (cast(size_t)ptr & Allocator.alignmentMask); 164 testAlign(ptr++, 0x10); 165 testAlign(ptr++, 0x0F); 166 testAlign(ptr++, 0x0E); 167 testAlign(ptr++, 0x0D); 168 testAlign(ptr++, 0x0C); 169 testAlign(ptr++, 0x0B); 170 testAlign(ptr++, 0x0A); 171 testAlign(ptr++, 0x09); 172 testAlign(ptr++, 0x08); 173 testAlign(ptr++, 0x07); 174 testAlign(ptr++, 0x06); 175 testAlign(ptr++, 0x05); 176 testAlign(ptr++, 0x04); 177 testAlign(ptr++, 0x03); 178 testAlign(ptr++, 0x02); 179 testAlign(ptr++, 0x01); 180 testAlign(ptr++, 0x10); 181 } 182 183 unittest { 184 foreach( i; 0 .. 20 ){ 185 auto ia = alignedSize(i); 186 assert(ia >= i); 187 assert((ia & Allocator.alignmentMask) == 0); 188 assert(ia < i+Allocator.alignment); 189 } 190 }