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!(DebugAllocator!(AutoFreeListAllocator!(MallocAllocator)))); 36 alias ProxyGCAllocator = DebugAllocator!GCAllocator; 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) 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) assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to realloc()."); 59 } 60 out { static if (!HasSecurePool) 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) assert((cast(size_t)mem.ptr & alignmentMask) == 0, "misaligned pointer passed to free()."); 66 } 67 } 68 69 package: 70 71 public auto getAllocator(int ALLOC)() { 72 static if (ALLOC == LocklessFreeList) alias R = LocklessAllocator; 73 else static if (ALLOC == NativeGC) alias R = ProxyGCAllocator; 74 else static if (HasCryptoSafe && ALLOC == CryptoSafe) alias R = CryptoSafeAllocator; 75 else static if (ALLOC == Mallocator) alias R = MallocAllocator; 76 else static assert(false, "Invalid allocator specified"); 77 78 static if (ALLOC == NativeGC) { 79 static __gshared R alloc; 80 81 if (!alloc) { 82 alloc = new R; 83 } 84 return alloc; 85 } 86 else return getAllocator!R(); 87 } 88 89 R getAllocator(R)() { 90 static R alloc; 91 if (!alloc) { 92 alloc = new R; 93 } 94 return alloc; 95 } 96 97 static if (HasBotan) 98 shared static ~this() { 99 .exit(0); // TODO: Fix invariant error. Maybe this is due to an erroneous treap in GC 100 } 101 102 version(LDC) shared static ~this() { .exit(0); } // LDC unit tests failing otherwise 103 104 size_t alignedSize(size_t sz) 105 { 106 return ((sz + Allocator.alignment - 1) / Allocator.alignment) * Allocator.alignment; 107 } 108 109 void ensureValidMemory(void[] mem) 110 { 111 auto bytes = cast(ubyte[])mem; 112 swap(bytes[0], bytes[$-1]); 113 swap(bytes[0], bytes[$-1]); 114 } 115 116 void* extractUnalignedPointer(void* base) 117 { 118 ubyte misalign = *(cast(const(ubyte)*)base-1); 119 assert(misalign <= Allocator.alignment); 120 return base - misalign; 121 } 122 123 void* adjustPointerAlignment(void* base) 124 { 125 ubyte misalign = Allocator.alignment - (cast(size_t)base & Allocator.alignmentMask); 126 base += misalign; 127 *(cast(ubyte*)base-1) = misalign; 128 return base; 129 } 130 131 template AllocSize(T) 132 { 133 static if (is(T == class)) { 134 // workaround for a strange bug where AllocSize!SSLStream == 0: TODO: dustmite! 135 enum dummy = T.stringof ~ __traits(classInstanceSize, T).stringof; 136 enum AllocSize = __traits(classInstanceSize, T); 137 } else { 138 enum AllocSize = T.sizeof; 139 } 140 } 141 142 template RefTypeOf(T) { 143 static if( is(T == class) || __traits(isAbstractClass, T) || is(T == interface) ){ 144 alias RefTypeOf = T; 145 } else { 146 alias RefTypeOf = T*; 147 } 148 } 149 150 151 unittest { 152 void testAlign(void* p, size_t adjustment) { 153 void* pa = adjustPointerAlignment(p); 154 assert((cast(size_t)pa & Allocator.alignmentMask) == 0, "Non-aligned pointer."); 155 assert(*(cast(const(ubyte)*)pa-1) == adjustment, "Invalid adjustment "~to!string(p)~": "~to!string(*(cast(const(ubyte)*)pa-1))); 156 void* pr = extractUnalignedPointer(pa); 157 assert(pr == p, "Recovered base != original"); 158 } 159 void* ptr = .malloc(0x40); 160 ptr += Allocator.alignment - (cast(size_t)ptr & Allocator.alignmentMask); 161 testAlign(ptr++, 0x10); 162 testAlign(ptr++, 0x0F); 163 testAlign(ptr++, 0x0E); 164 testAlign(ptr++, 0x0D); 165 testAlign(ptr++, 0x0C); 166 testAlign(ptr++, 0x0B); 167 testAlign(ptr++, 0x0A); 168 testAlign(ptr++, 0x09); 169 testAlign(ptr++, 0x08); 170 testAlign(ptr++, 0x07); 171 testAlign(ptr++, 0x06); 172 testAlign(ptr++, 0x05); 173 testAlign(ptr++, 0x04); 174 testAlign(ptr++, 0x03); 175 testAlign(ptr++, 0x02); 176 testAlign(ptr++, 0x01); 177 testAlign(ptr++, 0x10); 178 } 179 180 unittest { 181 foreach( i; 0 .. 20 ){ 182 auto ia = alignedSize(i); 183 assert(ia >= i); 184 assert((ia & Allocator.alignmentMask) == 0); 185 assert(ia < i+Allocator.alignment); 186 } 187 }