2 * This module contains a minimal garbage collector implementation according to
3 * Tango requirements. This library is mostly intended to serve as an example,
4 * but it is usable in applications which do not rely on a garbage collector
5 * to clean up memory (ie. when dynamic array resizing is not used, and all
6 * memory allocated with 'new' is freed deterministically with 'delete').
8 * Please note that block attribute data must be tracked, or at a minimum, the
9 * FINALIZE bit must be tracked for any allocated memory block because calling
10 * rt_finalize on a non-object block can result in an access violation. In the
11 * allocator below, this tracking is done via a leading uint bitmask. A real
12 * allocator may do better to store this data separately, similar to the basic
13 * GC normally used by Tango.
15 * Copyright: Public Domain
17 * Authors: Leandro Lucarella
24 import cell: Cell, BlkAttr;
26 import dynarray: DynArray;
27 import arch: stack_smaller, push_registers, pop_registers;
28 import alloc: mem_alloc, mem_free;
30 import stdc = tango.stdc.stdlib;
31 import tango.stdc.string: memset, memcpy;
32 debug import tango.stdc.stdio: printf;
34 alias void delegate(void*, void*) mark_function;
35 extern (C) void onOutOfMemoryError();
36 extern (C) void rt_finalize(void* p, bool det=true);
37 extern (C) void rt_scanStaticData(mark_function mark);
38 extern (C) void thread_init();
39 extern (C) bool thread_needLock();
40 extern (C) void thread_suspendAll();
41 extern (C) void thread_resumeAll();
42 extern (C) void thread_scanAll(mark_function mark, void* stack_top=null);
51 int opApply(int delegate(ref void*) dg)
54 auto from = cast(void**) this.from;
55 auto to = cast(void**) this.to;
56 // TODO: alignment. The range should be aligned and the size
57 // should be a multiple of the word size. If the later does
58 // not hold, the last bytes that are not enough to build
59 // a complete word should be ignored. Right now we are
60 // doing invalid reads in that cases.
61 for (void** current = from; current < to; current++) {
62 result = dg(*current);
80 // TODO: ver bien donde desmarkcar/marcar celdas (probablemente en malloc)
91 DynArray!(void*) root_pointers;
93 DynArray!(RootRange) root_ranges;
99 debug (gc_naive_gc_collect)
100 printf("gc.unmark()\n");
101 foreach (cell; this.live_list)
107 debug (gc_naive_gc_collect)
108 printf("gc.mark_all()\n");
110 mixin (push_registers("stack_top"));
112 debug (gc_naive_gc_collect)
113 printf("gc.mark_all() - scanning static data\n");
114 rt_scanStaticData(&mark_range);
115 debug (gc_naive_gc_collect)
116 printf("gc.mark_all() - scanning threads stack\n");
117 thread_scanAll(&mark_range, stack_top);
118 debug (gc_naive_gc_collect)
119 printf("gc.mark_all() - scanning root pointers:");
120 foreach (ptr; this.root_pointers) {
121 debug (gc_naive_gc_collect)
125 debug (gc_naive_gc_collect)
126 printf("\ngc.mark_all() - scanning root ranges:");
127 foreach (range; this.root_ranges) {
128 debug (gc_naive_gc_collect)
129 printf(" %p-%p", range.from, range.to);
130 this.mark_range(range.from, range.to);
132 debug (gc_naive_gc_collect)
135 mixin (pop_registers("stack_top"));
138 void mark_range(void* from, void* to)
140 debug (gc_naive_gc_collect)
141 printf("gc.mark_range(%p, %p)\n", from, to);
142 foreach (ptr; RootRange(from, to))
148 debug (gc_naive_gc_collect_extra)
149 printf("gc.mark(%p)\n", ptr);
150 Cell* cell = Cell.from_ptr(this.addrOf(ptr));
153 debug (gc_naive_gc_collect)
154 printf("gc.mark() - %p cell=%p\n", cell.ptr, cell);
157 debug (gc_naive_gc_collect)
158 printf("gc.mark() - cell=%p marked\n", cell);
159 if (cell.has_pointers) {
168 debug (gc_naive_gc_collect) {
169 printf("gc.sweep()\n\tfree:");
170 foreach (cell; this.free_list)
173 foreach (cell; this.live_list)
174 printf(" %s%p", cell.marked ? "*\0".ptr : "\0".ptr, cell);
175 printf("\n\tfreed:");
177 foreach (cell; this.live_list) {
179 debug (gc_naive_gc_collect)
181 this.live_list.unlink(cell);
183 rt_finalize(cell.ptr, false);
184 this.free_list.link(cell);
187 debug (gc_naive_gc_collect) {
189 foreach (cell; this.free_list)
192 foreach (cell; this.live_list)
202 // NOTE: The GC must initialize the thread library before its first
203 // collection, and always before returning from gc_init().
210 // Finalization of unreferenced cells is not mandatory by the specs.
211 // This implementation guarantees that all live data gets finalized,
212 // referenced or unreferenced, at the end of the program.
213 //foreach (cell; this.live_list)
214 // if (cell.finalize)
215 // rt_finalize(cell.ptr, false);
221 assert (this.enabled > 0);
226 assert (this.enabled > 0);
239 foreach (cell; this.free_list) {
240 this.free_list.unlink(cell);
241 mem_free(cell, cell.capacity + Cell.sizeof);
245 uint getAttr(void* ptr)
247 auto cell = this.live_list.find(ptr);
253 // return the old value
254 uint setAttr(void* ptr, uint attr)
256 auto cell = this.live_list.find(ptr);
258 auto old = cell.attr;
265 uint clrAttr(void* ptr, uint attr)
267 auto cell = this.live_list.find(ptr);
269 auto old = cell.attr;
276 void* malloc(size_t size, uint attr=0)
278 debug (gc_naive_gc_malloc)
279 printf("gc.malloc(%u, %u)\n", size, attr);
283 // Find a free cell in the free list with enough space
284 auto cell = this.free_list.pop(size);
287 debug (gc_naive_gc_malloc)
288 printf("gc.malloc() - no cell available in the free list\n");
290 // No room in the free list found, trigger a collection
293 // Try again in the free list
294 cell = this.free_list.pop(size);
297 debug (gc_naive_gc_malloc)
298 printf("gc.malloc() - after collection, still no free cell\n");
300 // No luck still, allocate new memory
301 cell = cast(Cell*) mem_alloc(size + Cell.sizeof);
304 debug (gc_naive_gc_malloc)
305 printf("gc.malloc() - can't malloc() from the OS\n");
308 onOutOfMemoryError();
314 if (cell.capacity == 0) // fresh cell
315 cell.capacity = size;
316 cell.attr = cast(BlkAttr) attr;
318 this.live_list.link(cell);
320 debug (gc_naive_gc_collect) {
321 printf("gc.malloc() -> %p (cell=%p)\n\tfree:", cell.ptr, cell);
322 foreach (cell; this.free_list)
325 foreach (cell; this.live_list)
333 void* calloc(size_t size, uint attr=0)
335 void* ptr = this.malloc(size, attr);
338 onOutOfMemoryError();
340 memset(ptr, 0, size);
345 void* realloc(void* ptr, size_t size, uint attr=0)
348 return this.malloc(size, attr);
355 auto cell = this.live_list.find(ptr);
358 if (cell.capacity >= size) {
363 Cell* new_cell = cast(Cell*) mem_alloc(size + Cell.sizeof);
364 if (new_cell is null)
365 onOutOfMemoryError();
367 memcpy(new_cell, cell, size + Cell.sizeof);
368 new_cell.size = size;
369 new_cell.capacity = size;
371 this.live_list.link(new_cell);
378 size_t extend(void* ptr, size_t min_size, size_t max_size)
380 assert (min_size <= max_size);
381 // There is no possible extension of the capacity for this
386 size_t reserve(size_t size)
389 auto cell = cast(Cell*) mem_alloc(size + Cell.sizeof);
393 cell.capacity = size;
394 this.free_list.link(cell);
403 auto cell = this.live_list.pop(ptr);
406 this.free_list.link(cell);
409 void* addrOf(void* ptr)
414 bool in_range(Cell* cell)
416 return ptr >= cell.ptr && ptr < (cell.ptr + cell.size);
419 auto cell = this.live_list.find(&in_range);
426 // TODO: acepta un address que no sea el base?
427 // es valido aceptar un ptr que no pertenezca al heap?
428 // (contestado en basic/gcx.d, pero es estandar?)
429 size_t sizeOf(void* ptr)
431 auto cell = this.live_list.find(ptr);
433 return cell.capacity;
437 // TODO: acepta un address que no sea el base?
438 // es valido aceptar un ptr que no pertenezca al heap?
439 BlkInfo query(void* ptr)
443 auto cell = this.live_list.find(ptr);
445 blk_info.base = cell.ptr;
446 blk_info.size = cell.capacity;
447 blk_info.attr = cell.attr;
453 void addRoot(void* ptr)
455 this.root_pointers.append(ptr);
458 void addRange(void* ptr, size_t size)
460 this.root_ranges.append(RootRange(ptr, ptr + size));
463 void removeRoot(void* ptr)
465 this.root_pointers.remove(ptr);
468 void removeRange(void* ptr)
470 foreach (range; this.root_ranges) {
471 if (range.from is ptr) {
472 this.root_ranges.remove(range);
480 // vim: set et sw=4 sts=4 :