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