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 }