1 module evael.lib.memory; 2 3 import std.experimental.allocator.mallocator; 4 import std.conv : emplace; 5 import std.traits : isImplicitlyConvertible; 6 7 public 8 { 9 import evael.lib.memory.no_gc_class; 10 } 11 12 debug 13 { 14 import std.experimental.allocator.building_blocks.stats_collector; 15 16 alias CustomStatsCollector = StatsCollector!(Mallocator, 17 Options.all, // Global stats 18 Options.all // Per call stats 19 ); 20 21 __gshared CustomStatsCollector defaultAllocator; 22 23 static ~this() 24 { 25 import std.stdio : writefln; 26 if (defaultAllocator.bytesUsed != 0) 27 { 28 writefln("StatsCollector: There are still %d bytes used, you probably have a memory leak.", 29 defaultAllocator.bytesUsed); 30 } 31 } 32 } 33 else 34 { 35 __gshared Mallocator defaultAllocator; 36 } 37 38 /// From druntime. Used by MemoryHelper.dispose(...). 39 extern (C) 40 private void _d_monitordelete(Object h, bool det) @nogc nothrow pure; 41 42 /** 43 * Static helper that provides @nogc new and delete. 44 */ 45 static class MemoryHelper 46 { 47 /** 48 * @nogc new for classes. 49 * Only classes that inherit from NoGCClass can be allocated with this function. 50 */ 51 @nogc 52 public static T create(T, Args...)(Args args, string file = __FILE__, int line =__LINE__) if (is(T == class)) 53 { 54 static if (!isImplicitlyConvertible!(T, NoGCClass)) 55 { 56 static assert(false, "Your class must inerith from NoGCClass if you want to use create."); 57 } 58 59 enum size = __traits(classInstanceSize, T); 60 61 auto memory = defaultAllocator.allocate(size); 62 (cast(void*) memory)[0..size] = typeid(T).initializer[]; 63 64 T ret = emplace!(T, Args)(memory, args); 65 ret.instantiatedWithGC = false; 66 67 return ret; 68 } 69 70 /** 71 * @nogc delete for classes and interfaces. 72 * 73 * - If this function is called on classes that inherit from NoGCClass / NoGCInterface (and are instantiated with @nogc new), 74 * this functon will free memory and call object's destructor. 75 * 76 * - If it is called on classes allocated with GC, only the destructor will be called. 77 */ 78 @nogc 79 public static void dispose(T)(ref T instance, string file = __FILE__, int line =__LINE__) if (is(T == class) || is(T == interface)) 80 { 81 if (instance is null) 82 return; 83 84 enum isNoGCClass = isImplicitlyConvertible!(T, NoGCClass) || isImplicitlyConvertible!(T, NoGCInterface); 85 86 static if (isNoGCClass) 87 { 88 bool instantiatedWithGC = (cast(NoGCClass) instance).instantiatedWithGC; 89 } 90 91 auto support = MemoryHelper.finalize(instance); 92 93 static if(isNoGCClass) 94 { 95 if (instantiatedWithGC == false && support !is null) 96 { 97 defaultAllocator.deallocate(support); 98 } 99 } 100 101 instance = null; 102 } 103 104 /** 105 * Calls the destructor of an object. 106 * This code has been taken from the druntime repository. 107 */ 108 @nogc 109 private static void[] finalize(T)(ref T instance) 110 { 111 auto obj = cast(Object) instance; 112 auto p = cast(void*) obj; 113 auto ppv = cast(void**) p; 114 115 if (!p || !*ppv) 116 return null; 117 118 auto pc = cast(ClassInfo*) *ppv; 119 auto c = *pc; 120 121 do 122 { 123 if (c.destructor) 124 { 125 alias DtorType = void function(Object) @nogc; 126 (cast(DtorType) c.destructor)(obj); 127 } 128 } 129 while ((c = c.base) !is null); 130 131 if (ppv[1]) // if monitor is not null 132 _d_monitordelete(obj, true); 133 134 auto support = p[0..typeid(obj).initializer.length]; 135 136 *ppv = null; 137 138 return support; 139 } 140 141 /** 142 * @nogc new for structs. 143 */ 144 @nogc 145 public static T* create(T, Args...)(Args args, string file = __FILE__, int line =__LINE__) if (is(T == struct)) 146 { 147 enum size = T.sizeof; 148 149 auto memory = defaultAllocator.allocate(size); 150 151 return emplace!(T, Args)(memory, args); 152 } 153 154 /** 155 * @nogc delete for structs. 156 */ 157 @nogc 158 public static void dispose(T)(ref T* instance, string file = __FILE__, int line =__LINE__) if (is(T == struct)) 159 { 160 destroy(instance); 161 162 defaultAllocator.deallocate((cast(void*) instance)[0..T.sizeof]); 163 instance = null; 164 } 165 166 /** 167 * @nogc delete for structs that have not been allocated with @nogc new. 168 */ 169 @nogc 170 public static void dispose(T)(ref T instance, string file = __FILE__, int line =__LINE__) if (is(T == struct)) 171 { 172 destroy(instance); 173 } 174 }