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