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.
24 // TODO collection statistics (timing, global counter, etc)
27 NOBUG_DEFINE_FLAG(acogc
);
28 NOBUG_DEFINE_FLAG_PARENT(acogc_mark
, acogc
);
29 NOBUG_DEFINE_FLAG_PARENT(acogc_collect
, acogc
);
30 NOBUG_DEFINE_FLAG_PARENT(acogc_alloc
, acogc
);
31 NOBUG_DEFINE_FLAG_PARENT(acogc_weak
, acogc
);
33 void acogc_nobug_init()
35 NOBUG_INIT_FLAG(acogc
);
36 NOBUG_INIT_FLAG(acogc_mark
);
37 NOBUG_INIT_FLAG(acogc_collect
);
38 NOBUG_INIT_FLAG(acogc_alloc
);
39 NOBUG_INIT_FLAG(acogc_weak
);
46 acogc_root_init (AcogcRoot self
)
49 NOTICE (acogc
, "self %p", self
);
51 llist_init (&self
->factories
);
52 self
->allocation_counter
= 0;
53 self
->collection_freq
= 32;
56 self
->nomemhandler
= NULL
;
57 self
->nomemsupp
= NULL
;
58 self
->state
= ACOGC_STATE_FIRST
;
63 acogc_root_erase (AcogcRoot self
)
66 NOTICE (acogc
, "self %p", self
);
68 LLIST_FOREACH (&self
->factories
, fnode
)
70 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
71 /* remove all roots (dynamic objects go to the alive list, uncollectable objects get dropped) */
72 LLIST_WHILE_HEAD (&f
->roots
, i
)
73 acogc_removeroot (acogc_memory_from_object (LLIST_TO_STRUCTP (i
, acogc_object
, node
)));
75 /* remove objects from the freelist */
76 LLIST_WHILE_HEAD (&f
->dead
, i
)
78 AcogcObject tmp
= LLIST_TO_STRUCTP (i
, acogc_object
, node
);
79 TRACE_DBG (acogc
, "erasing from dead %p", acogc_memory_from_object(tmp
));
80 if (tmp
->factory
->finalize
)
81 tmp
->factory
->finalize (acogc_memory_from_object (tmp
));
82 acogc_object_weakref_invalidate (acogc_memory_from_object (tmp
));
87 /* remove objects in the alive list */
88 LLIST_WHILE_HEAD (&f
->alive
, i
)
90 AcogcObject tmp
= LLIST_TO_STRUCTP (i
, acogc_object
, node
);
91 TRACE_DBG (acogc
, "erasing from alive %p", acogc_memory_from_object(tmp
));
92 if (tmp
->factory
->finalize
)
93 tmp
->factory
->finalize (acogc_memory_from_object (tmp
));
94 acogc_object_weakref_invalidate (acogc_memory_from_object (tmp
));
100 f
->objects_allocated
= 0;
106 self
->allocation_counter
= 0;
107 self
->state
= ACOGC_STATE_FIRST
;
112 acogc_root_collect (AcogcRoot self
, acogc_freeing_policy pol
)
115 NOTICE (acogc_collect
, "self %p, policy %d", self
, pol
);
117 /* we can short-circruit the collection we just did one and no user-code has been run */
118 if (self
->allocation_counter
!= 0 || pol
> ACOGC_COLLECT_USER1
|| pol
== ACOGC_COLLECT_DONTFREE
)
120 self
->allocation_counter
= 0;
122 /* move all previous alive objects to the tmp lists */
123 LLIST_FOREACH (&self
->factories
, fnode
)
125 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
126 llist_insert_list_before (&f
->alive
, &f
->tmp
);
130 // check for state overflow and reinit GC states (happens *very* rarely, once every 2^31 collections at worst)
131 if (self
->state
== ACOGC_STATE_LAST
)
133 NOTICE (acogc_collect
, "gc state overflow, recycle");
134 self
->state
= ACOGC_STATE_START
;
135 LLIST_FOREACH (&self
->factories
, fnode
)
137 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
138 LLIST_FOREACH (&f
->tmp
, i
)
139 LLIST_TO_STRUCTP (i
, acogc_object
, node
)->state
= ACOGC_STATE_FIRST
;
140 LLIST_FOREACH (&f
->dead
, i
)
141 LLIST_TO_STRUCTP (i
, acogc_object
, node
)->state
= ACOGC_STATE_FIRST
;
145 // and now go, marking is copying alive objects back to the alive list
146 LLIST_FOREACH (&self
->factories
, fnode
)
148 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
149 TRACE (acogc_collect
, "marking roots in %s", f
->name
);
152 LLIST_FOREACH (&f
->roots
, i
)
154 TRACE_DBG (acogc_collect
, "root marker %p",
155 acogc_memory_from_object (LLIST_TO_STRUCTP (i
, acogc_object
, node
)));
156 acogc_object_markreally (LLIST_TO_STRUCTP (i
, acogc_object
, node
));
161 // and for all root->stack references
162 TRACE (acogc_collect
, "marking stack references");
163 for (AcogcStack r
= self
->stack
; r
; r
= r
->prev
)
165 ASSERT (r
!= r
->prev
, "stack corruption, maybe ACOGC_STACK_PTR redefinition in a loop");
166 TRACE_DBG (acogc_collect
, "stack marker %p", r
->ptr
);
167 acogc_object_mark (r
->ptr
);
170 /* move remaining cruft in the tmplist to the freelist */
171 LLIST_FOREACH (&self
->factories
, fnode
)
173 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
174 NOTICE (acogc_collect
, "collected %d %s", llist_count (&f
->tmp
), f
->name
);
175 llist_insert_list_before (&f
->tmp
, &f
->dead
);
179 /* now the freeing pass */
183 case ACOGC_COLLECT_DONTFREE
:
184 case ACOGC_COLLECT_NORMAL
:
185 case ACOGC_COLLECT_FORCEMID
:
186 case ACOGC_COLLECT_FORCELOW
:
187 case ACOGC_COLLECT_FORCEALL
:
189 case ACOGC_COLLECT_USER1
:
190 case ACOGC_COLLECT_USER2
:
191 case ACOGC_COLLECT_USER3
:
192 if (self
->nomemhandler
)
193 self
->nomemhandler (self
->nomemsupp
, pol
);
195 case ACOGC_COLLECT_FAILED
:
199 unsigned long had_objects
= 0;
200 unsigned long freed_objects
= 0;
202 LLIST_FOREACH (&self
->factories
, fnode
)
204 AcogcFactory f
= LLIST_TO_STRUCTP (fnode
, acogc_factory
, factories
);
206 had_objects
+= f
->objects_allocated
;
208 unsigned keep_limit
= 0;
211 case ACOGC_COLLECT_DONTFREE
:
214 case ACOGC_COLLECT_NORMAL
:
215 keep_limit
= f
->high_water
;
217 case ACOGC_COLLECT_FORCEMID
:
218 keep_limit
= (f
->high_water
+ f
->low_water
)/2;
220 case ACOGC_COLLECT_FORCELOW
:
221 keep_limit
= f
->low_water
;
223 case ACOGC_COLLECT_FORCEALL
:
226 case ACOGC_COLLECT_USER1
:
227 case ACOGC_COLLECT_USER2
:
228 case ACOGC_COLLECT_USER3
:
229 case ACOGC_COLLECT_FAILED
:
233 while (!llist_is_empty (&f
->dead
) && (f
->objects_used
* 100 / f
->objects_allocated
> keep_limit
))
237 INFO (acogc_collect
, "free rate: %lu", f
->objects_used
* 100 / f
->objects_allocated
);
238 AcogcObject tmp
= LLIST_TO_STRUCTP (llist_get_head (&f
->dead
), acogc_object
, node
);
240 TRACE_DBG (acogc_collect
, "freeing %p", acogc_memory_from_object (tmp
));
242 if (tmp
->factory
->finalize
)
243 tmp
->factory
->finalize (acogc_memory_from_object (tmp
));
245 acogc_object_weakref_invalidate (acogc_memory_from_object (tmp
));
247 llist_unlink (&tmp
->node
);
249 --f
->objects_allocated
;
251 NOTICE (acogc_collect
, "after freeing: allocated %lu, used %lu, ", f
->objects_allocated
,f
->objects_used
);
254 if (freed_objects
> 16)
256 self
->collection_freq
= (self
->collection_freq
* (had_objects
-freed_objects
) / had_objects
)/2 +1;
257 TRACE (acogc_collect
, "decreased collection freq: %lu", self
->collection_freq
);
260 NOTICE_DBG (acogc_collect
, "collection complete");
268 acogc_factory_init (AcogcFactory self
,
273 acogc_mark_func mark
,
274 acogc_initize_func initize
,
275 acogc_finalize_func finalize
,
282 REQUIRE (low_water
< 100);
283 REQUIRE (high_water
< 100);
284 REQUIRE (low_water
<= high_water
);
286 NOTICE (acogc
, "self %p", self
);
288 llist_init (&self
->factories
);
289 llist_insert_tail (&root
->factories
, &self
->factories
);
291 llist_init (&self
->roots
);
292 llist_init (&self
->alive
);
293 llist_init (&self
->dead
);
294 llist_init (&self
->tmp
);
298 self
->objects_allocated
= 0;
299 self
->objects_used
= 0;
300 self
->objects_new
= 0;
301 self
->high_water
= high_water
;
302 self
->low_water
= low_water
;
305 self
->initize
= initize
;
306 self
->finalize
= finalize
;
312 acogc_factory_alloc (AcogcFactory self
)
319 ++self
->root
->allocation_counter
;
321 /* maybe call a collection if the freelist is empty */
322 if (llist_is_empty (&self
->dead
) &&
323 self
->root
->allocation_counter
> self
->root
->collection_freq
)
325 acogc_root_collect (self
->root
, ACOGC_COLLECT_NORMAL
);
326 if (self
->objects_allocated
&&
327 100 - (self
->objects_used
* 100 / self
->objects_allocated
) < self
->low_water
)
329 // collected less than low_water, increase collection_freq and allocate
330 self
->root
->collection_freq
= self
->root
->collection_freq
*3/2 +1;
331 TRACE (acogc_collect
, "increased collection freq: %lu", self
->root
->collection_freq
);
336 if (llist_is_empty (&self
->dead
))
338 /* allocate a new object */
341 acogc_alloc (&object
, sizeof (acogc_object
) + self
->size
, self
->root
);
343 llist_init (&object
->node
);
344 llist_insert_tail (&self
->alive
, &object
->node
);
346 ++self
->objects_allocated
;
347 TRACE_DBG (acogc_alloc
, "from malloc %p", acogc_memory_from_object (object
));
351 /* get one from free-queue */
352 llist_insert_tail (&self
->alive
, llist_get_head (&self
->dead
));
354 object
= LLIST_TO_STRUCTP(llist_get_tail (&self
->alive
), acogc_object
, node
);
357 self
->finalize (acogc_memory_from_object (object
));
359 acogc_object_weakref_invalidate (acogc_memory_from_object (object
));
361 TRACE_DBG (acogc_alloc
, "from freelist %p", acogc_memory_from_object (object
));
365 self
->initize (acogc_memory_from_object (object
));
367 object
->factory
= self
;
369 object
->weakrefs
= (AcogcWeakref
)&object
->weakrefs
;
370 object
->state
= self
->root
->state
;
373 ++self
->objects_used
;
375 return acogc_memory_from_object (object
);
382 acogc_object_markreally (AcogcObject self
)
387 TRACE (acogc_mark
, "mark object %p, state %d", acogc_memory_from_object (self
), self
->state
);
389 REQUIRE (self
->state
!= self
->factory
->root
->state
, "marking already marked object %p", acogc_memory_from_object (self
));
391 if (!self
->factory
->mark
|| self
->factory
->mark (acogc_memory_from_object (self
)) == ACOGC_KEEP
)
393 TRACE_DBG (acogc_mark
, "keep object %p", acogc_memory_from_object (self
));
394 if (self
->state
>= ACOGC_STATE_BUSY
)
396 /* is a dynamically allocated object */
397 ++self
->factory
->objects_used
;
399 /* is a collectable object, store it in the alive list */
400 llist_insert_tail (&self
->factory
->alive
, &self
->node
);
401 self
->state
= self
->factory
->root
->state
;
404 if (self
->factory
->root
->last
)
406 self
= self
->factory
->root
->last
;
407 self
->factory
->root
->last
= NULL
;
408 TRACE (acogc_mark
, "tailcall");
413 return ACOGC_COLLECT
;
416 /* register a GC root object */
418 acogc_addroot (void* object
)
421 TRACE (acogc
, "object: %p", object
);
425 AcogcObject o
= acogc_object_from_memory (object
);
427 if (o
->state
<= ACOGC_STATE_ROOT
)
431 INFO_DBG (acogc
, "object: %p already root, nested %d", object
, -o
->state
);
435 llist_insert_tail (&o
->factory
->roots
, &o
->node
);
436 if (o
->state
>= ACOGC_STATE_FIRST
)
437 o
->state
= ACOGC_STATE_ROOT
;
441 /* unregister a GC root object, collectable objects go back to the alive list, uncollectable objects are reset and removed from the GC */
443 acogc_removeroot (void* object
)
447 TRACE (acogc
, "object: %p", object
);
451 AcogcObject o
= acogc_object_from_memory (object
);
453 if (o
->state
< ACOGC_STATE_ROOT
)
455 /* dynamic object, nested */
457 INFO_DBG (acogc
, "object: %p still root, nested %d", object
, -o
->state
);
461 AcogcFactory factory
= o
->factory
;
462 if (o
->state
== ACOGC_STATE_ROOT
)
464 /* dynamic object, removeroot */
465 TRACE_DBG (acogc
, " ..dynamic, removed from root");
466 llist_insert_tail (&factory
->alive
, &o
->node
);
467 o
->state
= factory
->root
->state
;
471 /* uncollectable object */
472 TRACE_DBG (acogc
, " ..uncollectable, removed from root");
473 if (o
->state
== ACOGC_STATE_UNCOLLECTABLE
&& factory
->finalize
)
474 factory
->finalize (object
);
476 acogc_object_weakref_invalidate (object
);
478 if (factory
->initize
)
479 factory
->initize (object
);
481 llist_unlink (&o
->node
);
491 acogc_weakref_link (AcogcWeakref self
, void* o
)
496 TRACE (acogc_weak
, "%p to %p", self
, o
);
500 NOTICE (acogc_weak
, "already linked together");
503 NOTICE (acogc_weak
, "weakref linked to another object");
504 acogc_weakref_unlink (self
);
509 AcogcObject object
= acogc_object_from_memory (o
);
510 ASSERT (object
->weakrefs
, "weakref not initialized");
511 self
->next
= object
->weakrefs
;
512 object
->weakrefs
= self
;
517 acogc_weakref_unlink (AcogcWeakref self
)
521 TRACE (acogc_weak
, "%p", self
);
523 AcogcWeakref_ref prev
= &self
->next
;
524 AcogcWeakref i
= self
->next
;
529 ASSERT (i
->next
, "zero in weak cycle");
533 TRACE_DBG (acogc_weak
, "unlink %p from after %p, before %p", self
, prev
, self
->next
);
540 ASSERT (!self
->ref
, "weakref not linked");
545 acogc_object_weakref_invalidate (void* memory
)
548 TRACE (acogc_weak
, "%p, weak %p", memory
, acogc_object_from_memory(memory
)->weakrefs
);
550 AcogcWeakref_ref object
= &acogc_object_from_memory(memory
)->weakrefs
;
553 while ((*object
)->next
!= *object
)
555 AcogcWeakref tmp
= *object
;
556 TRACE_DBG (acogc_weak
, "invalidate %p, ref %p", tmp
, tmp
->ref
);
557 (*object
) = (*object
)->next
;
566 acogc_weakref_reinstget (AcogcWeakref self
)
569 TRACE (acogc_weak
, "%p", self
);
573 TRACE_DBG (acogc_weak
, "reinst miss %p", self
);
577 AcogcObject o
= acogc_object_from_memory (self
->ref
);
579 if (o
->state
>= ACOGC_STATE_FIRST
&& o
->state
< o
->factory
->root
->state
)
581 TRACE_DBG (acogc_weak
, "reinst hit %p, object %p", self
, self
->ref
);
582 o
->state
= o
->factory
->root
->state
;
583 llist_insert_tail (&o
->factory
->alive
, &o
->node
);
584 ++o
->factory
->objects_used
;
590 acogc_weakref_get (AcogcWeakref self
)
593 TRACE (acogc_weak
, "%p", self
);
598 AcogcObject o
= acogc_object_from_memory (self
->ref
);
600 if (o
->state
>= ACOGC_STATE_FIRST
&& o
->state
< o
->factory
->root
->state
)
602 INFO_DBG (acogc_weak
, "already invalidated");
606 INFO_DBG (acogc_weak
, "resolved %p", self
->ref
);
612 acogc_weakref_assert_cleared (AcogcWeakref p
)
616 ASSERT (!p
->next
&& ! p
->ref
, "weak-weakref %p not cleared at end of scope", p
);
619 /* malloc / free wrapers */
621 acogc_alloc_ (void ** addr
, size_t size
, AcogcRoot root
)
628 acogc_freeing_policy pol
= ACOGC_COLLECT_NORMAL
;
632 ret
= realloc (*addr
, size
);
638 acogc_root_collect (root
, pol
);
644 ERROR (NOBUG_ON
, "out of memory");
654 acogc_reserve_ (void ** addr
, size_t actual
, size_t needed
, AcogcRoot root
)
657 REQUIRE (actual
> 0);
658 REQUIRE (needed
> 0);
662 acogc_freeing_policy pol
= ACOGC_COLLECT_NORMAL
;
665 for (n
= actual
>16 ? actual
: 16; n
<= needed
; n
+= (n
>>1)); /*n = n * 1.5*/
668 PLANNED("realloc only 'needed' when memory is low");
669 ret
= realloc (*addr
, n
);
675 acogc_root_collect (root
, pol
);
681 ERROR (NOBUG_ON
, "out of memory");
693 acogc_free_ (void ** addr
)
702 assertion for cleanup
705 acogc_assert_stackframeleft (AcogcStack_ref p
)
709 ASSERT (*p
== (AcogcStack
) p
);
716 // c-file-style: "gnu"
718 // arch-tag: 2a29f53b-906e-4025-bf90-8e12b4182474