1 /**
2 * This module contains a minimal garbage collector implementation according to
3 * published requirements. This library is mostly intended to serve as an
4 * example, but it is usable in applications which do not rely on a garbage
5 * collector to clean up memory (ie. when dynamic array resizing is not used,
6 * and all memory allocated with 'new' is freed deterministically with
7 * 'delete').
8 *
9 * Please note that block attribute data must be tracked, or at a minimum, the
10 * FINALIZE bit must be tracked for any allocated memory block because calling
11 * rt_finalize on a non-object block can result in an access violation. In the
12 * allocator below, this tracking is done via a leading uint bitmask. A real
13 * allocator may do better to store this data separately, similar to the basic
14 * GC.
15 *
16 * Copyright: Copyright Sean Kelly 2005 - 2009.
17 * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
18 * Authors: Sean Kelly
19 */
20
21 /* Copyright Sean Kelly 2005 - 2009.
22 * Distributed under the Boost Software License, Version 1.0.
23 * (See accompanying file LICENSE or copy at
24 * http://www.boost.org/LICENSE_1_0.txt)
25 */
26 module gc.gc;
27
28 private
29 {
30 import core.stdc.stdlib;
31 import core.stdc.stdio;
32
33 static import core.memory;
34 private alias BlkAttr = core.memory.GC.BlkAttr;
35 private alias BlkInfo = core.memory.GC.BlkInfo;
36
37 extern (C) void thread_init();
38 extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc; /* dmd @@@BUG11461@@@ */
39
40 struct Proxy
41 {
42 extern (C) void function() gc_enable;
43 extern (C) void function() gc_disable;
44 extern (C) void function() gc_collect;
45 extern (C) void function() gc_minimize;
46
47 extern (C) uint function(void*) gc_getAttr;
48 extern (C) uint function(void*, uint) gc_setAttr;
49 extern (C) uint function(void*, uint) gc_clrAttr;
50
51 extern (C) void* function(size_t, uint, const TypeInfo) gc_malloc;
52 extern (C) BlkInfo function(size_t, uint, const TypeInfo) gc_qalloc;
53 extern (C) void* function(size_t, uint, const TypeInfo) gc_calloc;
54 extern (C) void* function(void*, size_t, uint ba, const TypeInfo) gc_realloc;
55 extern (C) size_t function(void*, size_t, size_t, const TypeInfo) gc_extend;
56 extern (C) size_t function(size_t) gc_reserve;
57 extern (C) void function(void*) gc_free;
58
59 extern (C) void* function(void*) gc_addrOf;
60 extern (C) size_t function(void*) gc_sizeOf;
61
62 extern (C) BlkInfo function(void*) gc_query;
63
64 extern (C) void function(void*) gc_addRoot;
65 extern (C) void function(void*, size_t, const TypeInfo ti) gc_addRange;
66
67 extern (C) void function(void*) gc_removeRoot;
68 extern (C) void function(void*) gc_removeRange;
69 extern (C) void function(in void[]) gc_runFinalizers;
70
71 extern (C) bool function() gc_inFinalizer;
72 }
73
74 __gshared Proxy pthis;
75 __gshared Proxy* proxy;
76
initProxy()77 void initProxy()
78 {
79 pthis.gc_enable = &gc_enable;
80 pthis.gc_disable = &gc_disable;
81 pthis.gc_collect = &gc_collect;
82 pthis.gc_minimize = &gc_minimize;
83
84 pthis.gc_getAttr = &gc_getAttr;
85 pthis.gc_setAttr = &gc_setAttr;
86 pthis.gc_clrAttr = &gc_clrAttr;
87
88 pthis.gc_malloc = &gc_malloc;
89 pthis.gc_qalloc = &gc_qalloc;
90 pthis.gc_calloc = &gc_calloc;
91 pthis.gc_realloc = &gc_realloc;
92 pthis.gc_extend = &gc_extend;
93 pthis.gc_reserve = &gc_reserve;
94 pthis.gc_free = &gc_free;
95
96 pthis.gc_addrOf = &gc_addrOf;
97 pthis.gc_sizeOf = &gc_sizeOf;
98
99 pthis.gc_query = &gc_query;
100
101 pthis.gc_addRoot = &gc_addRoot;
102 pthis.gc_addRange = &gc_addRange;
103
104 pthis.gc_removeRoot = &gc_removeRoot;
105 pthis.gc_removeRange = &gc_removeRange;
106 pthis.gc_runFinalizers = &gc_runFinalizers;
107
108 pthis.gc_inFinalizer = &gc_inFinalizer;
109 }
110
111 __gshared void** roots = null;
112 __gshared size_t nroots = 0;
113
114 struct Range
115 {
116 void* pos;
117 size_t len;
118 TypeInfo ti; // should be tail const, but doesn't exist for references
119 }
120
121 __gshared Range* ranges = null;
122 __gshared size_t nranges = 0;
123 }
124
gc_init()125 extern (C) void gc_init()
126 {
127 // NOTE: The GC must initialize the thread library before its first
128 // collection, and always before returning from gc_init().
129 thread_init();
130 initProxy();
131 }
132
gc_term()133 extern (C) void gc_term()
134 {
135 free( roots );
136 free( ranges );
137 }
138
gc_enable()139 extern (C) void gc_enable()
140 {
141 if ( proxy is null )
142 return;
143 return proxy.gc_enable();
144 }
145
gc_disable()146 extern (C) void gc_disable()
147 {
148 if ( proxy is null )
149 return;
150 return proxy.gc_disable();
151 }
152
gc_collect()153 extern (C) void gc_collect()
154 {
155 if ( proxy is null )
156 return;
157 return proxy.gc_collect();
158 }
159
gc_minimize()160 extern (C) void gc_minimize()
161 {
162 if ( proxy is null )
163 return;
164 return proxy.gc_minimize();
165 }
166
gc_getAttr(void * p)167 extern (C) uint gc_getAttr( void* p )
168 {
169 if ( proxy is null )
170 return 0;
171 return proxy.gc_getAttr( p );
172 }
173
gc_setAttr(void * p,uint a)174 extern (C) uint gc_setAttr( void* p, uint a )
175 {
176 if ( proxy is null )
177 return 0;
178 return proxy.gc_setAttr( p, a );
179 }
180
gc_clrAttr(void * p,uint a)181 extern (C) uint gc_clrAttr( void* p, uint a )
182 {
183 if ( proxy is null )
184 return 0;
185 return proxy.gc_clrAttr( p, a );
186 }
187
188 extern (C) void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo ti = null )
189 {
190 if ( proxy is null )
191 {
192 void* p = malloc( sz );
193
194 if ( sz && p is null )
195 onOutOfMemoryError();
196 return p;
197 }
198 return proxy.gc_malloc( sz, ba, ti );
199 }
200
201 extern (C) BlkInfo gc_qalloc( size_t sz, uint ba = 0, const TypeInfo ti = null )
202 {
203 if ( proxy is null )
204 {
205 BlkInfo retval;
206 retval.base = gc_malloc(sz, ba);
207 retval.size = sz;
208 retval.attr = ba;
209 return retval;
210 }
211 return proxy.gc_qalloc( sz, ba, ti );
212 }
213
214 extern (C) void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo ti = null )
215 {
216 if ( proxy is null )
217 {
218 void* p = calloc( 1, sz );
219
220 if ( sz && p is null )
221 onOutOfMemoryError();
222 return p;
223 }
224 return proxy.gc_calloc( sz, ba, ti );
225 }
226
227 extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo ti = null )
228 {
229 if ( proxy is null )
230 {
231 p = realloc( p, sz );
232
233 if ( sz && p is null )
234 onOutOfMemoryError();
235 return p;
236 }
237 return proxy.gc_realloc( p, sz, ba, ti );
238 }
239
240 extern (C) size_t gc_extend( void* p, size_t mx, size_t sz, const TypeInfo ti = null )
241 {
242 if ( proxy is null )
243 return 0;
244 return proxy.gc_extend( p, mx, sz, ti );
245 }
246
gc_reserve(size_t sz)247 extern (C) size_t gc_reserve( size_t sz )
248 {
249 if ( proxy is null )
250 return 0;
251 return proxy.gc_reserve( sz );
252 }
253
gc_free(void * p)254 extern (C) void gc_free( void* p )
255 {
256 if ( proxy is null )
257 return free( p );
258 return proxy.gc_free( p );
259 }
260
gc_addrOf(void * p)261 extern (C) void* gc_addrOf( void* p )
262 {
263 if ( proxy is null )
264 return null;
265 return proxy.gc_addrOf( p );
266 }
267
gc_sizeOf(void * p)268 extern (C) size_t gc_sizeOf( void* p )
269 {
270 if ( proxy is null )
271 return 0;
272 return proxy.gc_sizeOf( p );
273 }
274
gc_query(void * p)275 extern (C) BlkInfo gc_query( void* p )
276 {
277 if ( proxy is null )
278 return BlkInfo.init;
279 return proxy.gc_query( p );
280 }
281
gc_addRoot(void * p)282 extern (C) void gc_addRoot( void* p )
283 {
284 if ( proxy is null )
285 {
286 void** r = cast(void**) realloc( roots,
287 (nroots+1) * roots[0].sizeof );
288 if ( r is null )
289 onOutOfMemoryError();
290 r[nroots++] = p;
291 roots = r;
292 return;
293 }
294 return proxy.gc_addRoot( p );
295 }
296
297 extern (C) void gc_addRange( void* p, size_t sz, const TypeInfo ti = null )
298 {
299 //printf("gcstub::gc_addRange() proxy = %p\n", proxy);
300 if ( proxy is null )
301 {
302 Range* r = cast(Range*) realloc( ranges,
303 (nranges+1) * ranges[0].sizeof );
304 if ( r is null )
305 onOutOfMemoryError();
306 r[nranges].pos = p;
307 r[nranges].len = sz;
308 r[nranges].ti = cast()ti;
309 ranges = r;
310 ++nranges;
311 return;
312 }
313 return proxy.gc_addRange( p, sz, ti );
314 }
315
gc_removeRoot(void * p)316 extern (C) void gc_removeRoot( void *p )
317 {
318 if ( proxy is null )
319 {
320 for ( size_t i = 0; i < nroots; ++i )
321 {
322 if ( roots[i] is p )
323 {
324 roots[i] = roots[--nroots];
325 return;
326 }
327 }
328 assert( false );
329 }
330 return proxy.gc_removeRoot( p );
331 }
332
gc_removeRange(void * p)333 extern (C) void gc_removeRange( void *p )
334 {
335 if ( proxy is null )
336 {
337 for ( size_t i = 0; i < nranges; ++i )
338 {
339 if ( ranges[i].pos is p )
340 {
341 ranges[i] = ranges[--nranges];
342 return;
343 }
344 }
345 assert( false );
346 }
347 return proxy.gc_removeRange( p );
348 }
349
gc_runFinalizers(in void[]segment)350 extern (C) void gc_runFinalizers( in void[] segment )
351 {
352 if ( proxy !is null )
353 proxy.gc_runFinalizers( segment );
354 }
355
gc_inFinalizer()356 extern (C) bool gc_inFinalizer()
357 {
358 if ( proxy !is null )
359 return proxy.gc_inFinalizer();
360 return false;
361 }
362
gc_getProxy()363 extern (C) Proxy* gc_getProxy()
364 {
365 return &pthis;
366 }
367
export(C)368 export extern (C) void gc_setProxy( Proxy* p )
369 {
370 if ( proxy !is null )
371 {
372 // TODO: Decide if this is an error condition.
373 }
374 proxy = p;
375 foreach ( r; roots[0 .. nroots] )
376 proxy.gc_addRoot( r );
377 foreach ( r; ranges[0 .. nranges] )
378 proxy.gc_addRange( r.pos, r.len, r.ti );
379 }
380
export(C)381 export extern (C) void gc_clrProxy()
382 {
383 foreach ( r; ranges[0 .. nranges] )
384 proxy.gc_removeRange( r.pos );
385 foreach ( r; roots[0 .. nroots] )
386 proxy.gc_removeRoot( r );
387 proxy = null;
388 }
389