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