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) {}