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