1 /** 2 Utility functions for memory management 3 4 Note that this module currently is a big sand box for testing allocation related stuff. 5 Nothing here, including the interfaces, is final but rather a lot of experimentation. 6 7 Copyright: © 2012-2013 RejectedSoftware e.K. 8 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 9 Authors: Sönke Ludwig 10 */ 11 module vibe.internal.freelistref; 12 13 import vibe.internal.allocator; 14 import vibe.internal.meta.traits : synchronizedIsNothrow; 15 16 import core.exception : OutOfMemoryError; 17 import core.stdc.stdlib; 18 import core.memory; 19 import std.conv; 20 import std.exception : enforceEx; 21 import std.traits; 22 import std.algorithm; 23 24 25 struct FreeListObjectAlloc(T, bool USE_GC = true, bool INIT = true, EXTRA = void) 26 { 27 enum ElemSize = AllocSize!T; 28 enum ElemSlotSize = max(AllocSize!T + AllocSize!EXTRA, Slot.sizeof); 29 30 static if( is(T == class) ){ 31 alias TR = T; 32 } else { 33 alias TR = T*; 34 } 35 36 struct Slot { Slot* next; } 37 38 private static Slot* s_firstFree; 39 40 static TR alloc(ARGS...)(ARGS args) 41 { 42 void[] mem; 43 if (s_firstFree !is null) { 44 auto ret = s_firstFree; 45 s_firstFree = s_firstFree.next; 46 ret.next = null; 47 mem = () @trusted { return (cast(void*)ret)[0 .. ElemSlotSize]; } (); 48 } else { 49 //logInfo("alloc %s/%d", T.stringof, ElemSize); 50 mem = Mallocator.instance.allocate(ElemSlotSize); 51 static if(hasIndirections!T) () @trusted { GC.addRange(mem.ptr, ElemSlotSize); } (); 52 } 53 54 // FIXME: this emplace has issues with qualified types, but Unqual!T may result in the wrong constructor getting called. 55 static if (INIT) internalEmplace!(Unqual!T)(mem[0 .. ElemSize], args); 56 57 return () @trusted { return cast(TR)mem.ptr; } (); 58 } 59 60 static void free(TR obj) 61 { 62 static if (INIT) { 63 scope (failure) assert(0, "You shouldn't throw in destructors"); 64 auto objc = obj; 65 static if (is(TR == T*)) .destroy(*objc);//typeid(T).destroy(cast(void*)obj); 66 else .destroy(objc); 67 } 68 69 auto sl = cast(Slot*)obj; 70 sl.next = s_firstFree; 71 s_firstFree = sl; 72 //static if( hasIndirections!T ) GC.removeRange(cast(void*)obj); 73 //Mallocator.instance.deallocate((cast(void*)obj)[0 .. ElemSlotSize]); 74 } 75 } 76 77 @safe unittest { 78 struct S {} 79 FreeListObjectAlloc!S.alloc(); 80 } 81 82 83 template AllocSize(T) 84 { 85 static if (is(T == class)) { 86 // workaround for a strange bug where AllocSize!SSLStream == 0: TODO: dustmite! 87 enum dummy = T.stringof ~ __traits(classInstanceSize, T).stringof; 88 enum AllocSize = __traits(classInstanceSize, T); 89 } else { 90 enum AllocSize = T.sizeof; 91 } 92 } 93 94 struct FreeListRef(T, bool INIT = true) 95 { 96 alias ObjAlloc = FreeListObjectAlloc!(T, true, INIT, int); 97 enum ElemSize = AllocSize!T; 98 99 static if( is(T == class) ){ 100 alias TR = T; 101 } else { 102 alias TR = T*; 103 } 104 105 private TR m_object; 106 private size_t m_magic = 0x1EE75817; // workaround for compiler bug 107 108 static FreeListRef opCall(ARGS...)(ARGS args) 109 { 110 FreeListRef ret; 111 ret.m_object = ObjAlloc.alloc!ARGS(args); 112 ret.refCount = 1; 113 //logInfo("refalloc %s/%d", T.stringof, ElemSize); 114 return ret; 115 } 116 117 ~this() 118 { 119 //if( m_object ) logInfo("~this!%s(): %d", T.stringof, this.refCount); 120 //if( m_object ) logInfo("ref %s destructor %d", T.stringof, refCount); 121 //else logInfo("ref %s destructor %d", T.stringof, 0); 122 clear(); 123 m_magic = 0; 124 m_object = null; 125 } 126 127 this(this) 128 { 129 checkInvariants(); 130 if( m_object ){ 131 //if( m_object ) logInfo("this!%s(this): %d", T.stringof, this.refCount); 132 this.refCount++; 133 } 134 } 135 136 void opAssign(FreeListRef other) 137 { 138 clear(); 139 m_object = other.m_object; 140 if( m_object ){ 141 //logInfo("opAssign!%s(): %d", T.stringof, this.refCount); 142 refCount++; 143 } 144 } 145 146 void clear() 147 { 148 checkInvariants(); 149 if (m_object) { 150 if (--this.refCount == 0) 151 () @trusted { ObjAlloc.free(m_object); } (); 152 } 153 154 m_object = null; 155 m_magic = 0x1EE75817; 156 } 157 158 static if (is(T == class)) { 159 @property inout(T) get() inout @safe nothrow { return m_object; } 160 } else { 161 @property ref inout(T) get() inout @safe nothrow { return *m_object; } 162 void opAssign(T t) { *m_object = t; } 163 } 164 alias get this; 165 166 private @property ref int refCount() 167 const @trusted { 168 assert(m_object !is null); 169 auto ptr = cast(ubyte*)cast(void*)m_object; 170 ptr += ElemSize; 171 return *cast(int*)ptr; 172 } 173 174 private void checkInvariants() 175 const { 176 assert(m_magic == 0x1EE75817); 177 assert(!m_object || refCount > 0); 178 } 179 } 180 181 182 /// See issue #14194 183 private T internalEmplace(T, Args...)(void[] chunk, auto ref Args args) 184 if (is(T == class)) 185 in { 186 import std.string, std.format; 187 assert(chunk.length >= T.sizeof, 188 format("emplace: Chunk size too small: %s < %s size = %s", 189 chunk.length, T.stringof, T.sizeof)); 190 assert((cast(size_t) chunk.ptr) % T.alignof == 0, 191 format("emplace: Misaligned memory block (0x%X): it must be %s-byte aligned for type %s", &chunk[0], T.alignof, T.stringof)); 192 193 } body { 194 enum classSize = __traits(classInstanceSize, T); 195 auto result = () @trusted { return cast(T) chunk.ptr; } (); 196 197 // Initialize the object in its pre-ctor state 198 () @trusted { 199 static if (__VERSION__ < 2071) 200 chunk[0 .. classSize] = typeid(T).init[]; 201 else 202 chunk[0 .. classSize] = typeid(T).initializer[]; // Avoid deprecation warning 203 } (); 204 205 // Call the ctor if any 206 static if (is(typeof(result.__ctor(args)))) 207 { 208 // T defines a genuine constructor accepting args 209 // Go the classic route: write .init first, then call ctor 210 result.__ctor(args); 211 } 212 else 213 { 214 static assert(args.length == 0 && !is(typeof(&T.__ctor)), 215 "Don't know how to initialize an object of type " 216 ~ T.stringof ~ " with arguments " ~ Args.stringof); 217 } 218 return result; 219 } 220 221 /// Dittor 222 private auto internalEmplace(T, Args...)(void[] chunk, auto ref Args args) 223 @safe if (!is(T == class)) 224 in { 225 import std.string, std.format; 226 assert(chunk.length >= T.sizeof, 227 format("emplace: Chunk size too small: %s < %s size = %s", 228 chunk.length, T.stringof, T.sizeof)); 229 assert((cast(size_t) chunk.ptr) % T.alignof == 0, 230 format("emplace: Misaligned memory block (0x%X): it must be %s-byte aligned for type %s", &chunk[0], T.alignof, T.stringof)); 231 232 } body { 233 return emplace(() @trusted { return cast(T*)chunk.ptr; } (), args); 234 } 235 236 private void logDebug_(ARGS...)(string msg, ARGS args) {}