2 acogc.c - simple accurate/cooperative garbage collector
4 Copyright (C) 2006, Christian Thaeter <chth@gmx.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License version 2 as
8 published by the Free Software Foundation.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, contact me.
25 // TODO collection statistics (timing, global counter, etc)
31 acogc_root_init (AcogcRoot self
)
33 ACOGC_TRACE (2, "self %p", self
);
34 llist_init (&self
->factories
);
35 self
->collection_freq_avg
= 0;
36 self
->allocation_counter
= 0;
38 self
->nomemhandler
= NULL
;
39 self
->nomemsupp
= NULL
;
40 self
->state
= ACOGC_STATE_FIRST
;
45 acogc_root_erase (AcogcRoot self
)
47 LLIST_FOREACH (&self
->factories
, fnode
)
49 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
50 /* remove all roots (dynamic objects go to the alive list, uncollectable objects get dropped) */
51 LLIST_WHILE_HEAD (&f
->roots
, i
)
52 acogc_removeroot (acogc_memory_from_object (LLIST_TO_STRUCTP (i
, acogc_object
, node
)));
54 /* remove objects from the freelist */
55 LLIST_WHILE_HEAD (&f
->dead
, i
)
57 AcogcObject tmp
= LLIST_TO_STRUCTP (i
, acogc_object
, node
);
58 ACOGC_TRACE (5, "erasing from dead %p", acogc_memory_from_object(tmp
));
59 if (tmp
->factory
->finalize
)
60 tmp
->factory
->finalize (acogc_memory_from_object (tmp
));
61 acogc_object_weakref_invalidate (acogc_memory_from_object (tmp
));
66 /* remove objects in the alive list */
67 LLIST_WHILE_HEAD (&f
->alive
, i
)
69 AcogcObject tmp
= LLIST_TO_STRUCTP (i
, acogc_object
, node
);
70 ACOGC_TRACE (2, "erasing from alive %p", acogc_memory_from_object(tmp
));
71 if (tmp
->factory
->finalize
)
72 tmp
->factory
->finalize (acogc_memory_from_object (tmp
));
73 acogc_object_weakref_invalidate (acogc_memory_from_object (tmp
));
79 f
->objects_allocated
= 0;
85 self
->collection_freq_avg
= 0;
86 self
->allocation_counter
= 0;
87 self
->state
= ACOGC_STATE_FIRST
;
92 acogc_root_collect (AcogcRoot self
, enum acogc_freeing_policy pol
)
94 /* we can short-circruit the collection we just did one and no user-code has been run */
95 if (self
->allocation_counter
!= 0 || pol
>= ACOGC_COLLECT_FORCEALL
|| pol
== ACOGC_COLLECT_DONTFREE
)
97 /* calculate new average and reset stat */
98 self
->collection_freq_avg
= (self
->collection_freq_avg
+ self
->allocation_counter
) / 2 + 1;
99 self
->allocation_counter
= 0;
100 ACOGC_TRACE (2, "start collection %p, collection_freq %lu", self
, self
->collection_freq_avg
);
102 /* move all previous alive objects to the tmp lists */
103 LLIST_FOREACH (&self
->factories
, fnode
)
105 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
106 llist_insert_list_before (&f
->alive
, &f
->tmp
);
110 // check for state overflow and reinit GC states (happens *very* rarely)
111 if (self
->state
== ACOGC_STATE_LAST
)
113 self
->state
= ACOGC_STATE_START
;
114 LLIST_FOREACH (&self
->factories
, fnode
)
116 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
117 LLIST_FOREACH (&f
->tmp
, i
)
118 LLIST_TO_STRUCTP (i
, acogc_object
, node
)->state
= ACOGC_STATE_FIRST
;
119 LLIST_FOREACH (&f
->dead
, i
)
120 LLIST_TO_STRUCTP (i
, acogc_object
, node
)->state
= ACOGC_STATE_FIRST
;
124 // and now go, marking is copying alive objects back to the alive list
125 LLIST_FOREACH (&self
->factories
, fnode
)
127 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
129 LLIST_FOREACH (&f
->roots
, i
)
131 ACOGC_TRACE (4, "root marker %p", acogc_memory_from_object (LLIST_TO_STRUCTP (i
, acogc_object
, node
)));
132 acogc_object_markreally (LLIST_TO_STRUCTP (i
, acogc_object
, node
));
135 // and for all root->stack references
136 for (AcogcStack r
= self
->stack
; r
; r
= r
->prev
)
138 ACOGC_TRACE (4, "stack marker %p", r
->ptr
);
139 acogc_object_mark (r
->ptr
);
142 /* move remaining cruft in the tmplist to the freelist */
143 LLIST_FOREACH (&self
->factories
, fnode
)
145 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
146 llist_insert_list_before (&f
->tmp
, &f
->dead
);
150 /* now the freeing pass */
154 case ACOGC_COLLECT_DONTFREE
:
155 case ACOGC_COLLECT_NORMAL
:
156 case ACOGC_COLLECT_FORCEMID
:
157 case ACOGC_COLLECT_FORCELOW
:
158 case ACOGC_COLLECT_FORCEALL
:
160 case ACOGC_COLLECT_USER1
:
161 case ACOGC_COLLECT_USER2
:
162 case ACOGC_COLLECT_USER3
:
163 if (self
->nomemhandler
)
164 self
->nomemhandler (self
->nomemsupp
, pol
);
166 case ACOGC_COLLECT_FAILED
:
170 LLIST_FOREACH (&self
->factories
, fnode
)
172 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
174 unsigned free_limit
= 0;
177 case ACOGC_COLLECT_DONTFREE
:
180 case ACOGC_COLLECT_NORMAL
:
181 free_limit
= f
->high_water
;
183 case ACOGC_COLLECT_FORCEMID
:
184 free_limit
= (f
->high_water
+ f
->low_water
)/2;
186 case ACOGC_COLLECT_FORCELOW
:
187 free_limit
= f
->low_water
;
189 case ACOGC_COLLECT_FORCEALL
:
192 case ACOGC_COLLECT_USER1
:
193 case ACOGC_COLLECT_USER2
:
194 case ACOGC_COLLECT_USER3
:
195 case ACOGC_COLLECT_FAILED
:
199 while (!llist_is_empty (&f
->dead
) && f
->objects_allocated
&&
200 (f
->was_free
= 100 - (f
->objects_used
* 100 / f
->objects_allocated
)) > free_limit
)
202 AcogcObject tmp
= LLIST_TO_STRUCTP (llist_get_head (&f
->dead
), acogc_object
, node
);
204 ACOGC_TRACE (4, "freeing %p", acogc_memory_from_object (tmp
));
206 if (tmp
->factory
->finalize
)
207 tmp
->factory
->finalize (acogc_memory_from_object (tmp
));
209 acogc_object_weakref_invalidate (acogc_memory_from_object (tmp
));
211 llist_unlink (&tmp
->node
);
213 --f
->objects_allocated
;
217 ACOGC_TRACE (2, "collection complete");
225 acogc_factory_init (AcogcFactory self
,
230 acogc_mark_func mark
,
231 acogc_initize_func initize
,
232 acogc_finalize_func finalize
)
234 ACOGC_TRACE (1, "%p", self
);
236 llist_init (&self
->factories
);
237 llist_insert_tail (&root
->factories
, &self
->factories
);
239 llist_init (&self
->roots
);
240 llist_init (&self
->alive
);
241 llist_init (&self
->dead
);
242 llist_init (&self
->tmp
);
246 self
->objects_allocated
= self
->objects_used
= 0;
248 self
->high_water
= high_water
;
249 self
->low_water
= low_water
;
252 self
->initize
= initize
;
253 self
->finalize
= finalize
;
258 acogc_factory_alloc (AcogcFactory self
)
262 ACOGC_TRACE (4, "self %p", self
);
264 ++self
->root
->allocation_counter
;
266 /* call a collection if the freelist is empty */
267 if (llist_is_empty (&self
->dead
) &&
268 (/* and there where enough free objects last time */
269 (self
->was_free
> self
->low_water
) ||
270 /* or we did more allocations so far than we did on average */
271 self
->root
->allocation_counter
> self
->root
->collection_freq_avg
))
273 acogc_root_collect (self
->root
, ACOGC_COLLECT_NORMAL
);
276 if (llist_is_empty (&self
->dead
))
278 /* allocate a new object */
280 acogc_alloc (&object
, sizeof (acogc_object
) + self
->size
, self
->root
);
282 llist_init (&object
->node
);
283 llist_insert_tail (&self
->alive
, &object
->node
);
285 ++self
->objects_allocated
;
286 ACOGC_TRACE (4, "from malloc %p", acogc_memory_from_object (object
));
290 /* get one from free-queue */
291 llist_insert_tail (&self
->alive
, llist_get_head (&self
->dead
));
293 object
= LLIST_TO_STRUCTP(llist_get_tail (&self
->alive
), acogc_object
, node
);
296 self
->finalize (acogc_memory_from_object (object
));
298 acogc_object_weakref_invalidate (acogc_memory_from_object (object
));
300 ACOGC_TRACE (4, "from freelist %p", acogc_memory_from_object (object
));
304 self
->initize (acogc_memory_from_object (object
));
306 object
->factory
= self
;
308 object
->weakrefs
= (AcogcWeakref
)&object
->weakrefs
;
309 object
->state
= self
->root
->state
;
311 ++self
->objects_used
;
313 return acogc_memory_from_object (object
);
320 acogc_object_markreally (AcogcObject self
)
322 ACOGC_TRACE (4, "mark object %p, state %d", acogc_memory_from_object (self
), self
->state
);
324 ACOGC_TRACE (0, "factory %p", self
->factory
);
326 ACOGC_ASSERT (self
->state
!= self
->factory
->root
->state
, "marking already marked object %p", acogc_memory_from_object (self
));
328 if (self
->state
>= ACOGC_STATE_FIRST
)
329 self
->state
= ACOGC_STATE_BUSY
;
331 if (!self
->factory
->mark
|| self
->factory
->mark (acogc_memory_from_object (self
)) == ACOGC_KEEP
)
333 if (self
->state
<= ACOGC_STATE_ROOT
|| self
->state
== ACOGC_STATE_BUSY
)
334 /* is a dynamically allocated object */
335 ++self
->factory
->objects_used
;
337 if (self
->state
== ACOGC_STATE_BUSY
)
339 /* is a collectable object, store it in the alive list */
340 llist_insert_tail (&self
->factory
->alive
, &self
->node
);
341 self
->state
= self
->factory
->root
->state
;
346 /* register a GC root object */
348 acogc_addroot (void* object
)
350 ACOGC_TRACE (3, "object %p", object
);
354 AcogcObject o
= acogc_object_from_memory (object
);
356 if (o
->state
<= ACOGC_STATE_ROOT
)
363 llist_insert_tail (&o
->factory
->roots
, &o
->node
);
364 if (o
->state
>= ACOGC_STATE_FIRST
)
365 o
->state
= ACOGC_STATE_ROOT
;
369 /* unregister a GC root object, collectable objects go back to the alive list, uncollectable objects are reset and removed from the GC */
371 acogc_removeroot (void* object
)
373 ACOGC_TRACE (3, "object %p", object
);
377 AcogcObject o
= acogc_object_from_memory (object
);
379 if (o
->state
< ACOGC_STATE_ROOT
)
380 /* dynamic object, nested */
384 AcogcFactory factory
= o
->factory
;
385 if (o
->state
== ACOGC_STATE_ROOT
)
387 /* dynamic object, removeroot */
388 ACOGC_TRACE (3, "dynamic");
389 llist_insert_tail (&factory
->alive
, &o
->node
);
390 o
->state
= factory
->root
->state
;
394 /* uncollectable object */
395 ACOGC_TRACE (3, "uncollectable");
396 if (o
->state
== ACOGC_STATE_UNCOLLECTABLE
&& factory
->finalize
)
397 factory
->finalize (object
);
399 acogc_object_weakref_invalidate (object
);
401 if (factory
->initize
)
402 factory
->initize (object
);
404 llist_unlink (&o
->node
);
414 acogc_weakref_link (AcogcWeakref self
, void* o
)
418 ACOGC_TRACE (4, "weaklink %p to %p", self
, o
);
420 acogc_weakref_unlink (self
);
424 AcogcObject object
= acogc_object_from_memory (o
);
425 ACOGC_ASSERT (object
->weakrefs
, "weakref not initialized");
426 self
->next
= object
->weakrefs
;
427 object
->weakrefs
= self
;
432 acogc_weakref_unlink (AcogcWeakref self
)
434 AcogcWeakref_ref prev
= &self
->next
;
435 AcogcWeakref i
= self
->next
;
440 ACOGC_ASSERT (i
->next
, "zero in weak cycle");
444 ACOGC_TRACE (4, "unlink %p from after %p, before %p", self
, prev
, self
->next
);
450 ACOGC_ASSERT (!self
->ref
, "weakref not linked");
454 acogc_object_weakref_invalidate (void* memory
)
456 ACOGC_TRACE (3, "invalidate %p, weak %p", memory
, acogc_object_from_memory(memory
)->weakrefs
);
457 AcogcWeakref_ref object
= &acogc_object_from_memory(memory
)->weakrefs
;
460 while ((*object
)->next
!= *object
)
462 AcogcWeakref tmp
= *object
;
463 ACOGC_TRACE (3, "invalidate %p, ref %p", tmp
, tmp
->ref
);
464 (*object
) = (*object
)->next
;
473 acogc_weakref_reinstget (AcogcWeakref self
)
478 AcogcObject o
= acogc_object_from_memory (self
->ref
);
480 if (o
->state
>= ACOGC_STATE_START
&& o
->state
< o
->factory
->root
->state
)
482 o
->state
= o
->factory
->root
->state
;
483 llist_insert_tail (&o
->factory
->alive
, &o
->node
);
484 ++o
->factory
->objects_used
;
490 acogc_weakref_get (AcogcWeakref self
)
495 AcogcObject o
= acogc_object_from_memory (self
->ref
);
497 if (o
->state
>= ACOGC_STATE_START
&& o
->state
< o
->factory
->root
->state
)
504 acogc_weakref_assert_cleared (AcogcWeakref p
)
507 ACOGC_ASSERT (!p
->next
&& ! p
->ref
,
508 "weak-weakref %p not cleared at end of scope", p
);
511 /* malloc / free wrapers */
513 acogc_alloc_ (void ** addr
, size_t size
, AcogcRoot root
)
516 enum acogc_freeing_policy pol
= ACOGC_COLLECT_NORMAL
;
520 ret
= realloc (*addr
, size
);
525 acogc_root_collect (root
, pol
);
535 acogc_reserve_ (void ** addr
, size_t actual
, size_t needed
, AcogcRoot root
)
538 enum acogc_freeing_policy pol
= ACOGC_COLLECT_NORMAL
;
541 for (n
= actual
>16 ? actual
: 16; n
<= needed
; n
+= (n
>>1)); /*n = n * 1.5*/
544 ret
= realloc (*addr
, n
);
548 acogc_root_collect (root
, pol
);
560 acogc_free_ (void ** addr
)
567 assertion for cleanup
570 acogc_assert_stackframeleft (AcogcStack_ref p
)
572 assert (*p
== (AcogcStack
) p
);
579 // c-file-style: "gnu"
581 // arch-tag: 2a29f53b-906e-4025-bf90-8e12b4182474