* src/vm/descriptor.cpp,
[cacao.git] / src / mm / cacao-gc / gc.c
index 88088b3d36bf51fd318d104a6d076225c2ae0a5e..deb86675a4c02c0a704ea0031c35524434fb3457 100644 (file)
@@ -1,9 +1,7 @@
 /* src/mm/cacao-gc/gc.c - main garbage collector methods
 
-   Copyright (C) 2006 R. Grafl, A. Krall, C. Kruegel,
-   C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
-   E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
-   J. Wenninger, Institut f. Computersprachen - TU Wien
+   Copyright (C) 2006, 2007, 2008
+   CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
 
    This file is part of CACAO.
 
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA.
 
-   $Id$
-
 */
 
 
 #include "config.h"
+
 #include <signal.h>
+#include <stdint.h>
+
 #include "vm/types.h"
 
-#if defined(ENABLE_THREADS)
-# include "threads/native/threads.h"
-#else
-/*# include "threads/none/threads.h"*/
-#endif
+#include "threads/lock.hpp"
+#include "threads/thread.hpp"
 
 #include "compact.h"
 #include "copy.h"
 #include "rootset.h"
 #include "mm/memory.h"
 #include "toolbox/logging.h"
-#include "vm/finalizer.h"
-#include "vm/vm.h"
-#include "vmcore/rt-timing.h"
+
+#include "vm/finalizer.hpp"
+#include "vm/rt-timing.h"
+#include "vm/vm.hpp"
 
 
 /* Global Variables ***********************************************************/
@@ -58,6 +55,9 @@ bool gc_pending;
 bool gc_running;
 bool gc_notify_finalizer;
 
+list_t *gc_reflist_strong;
+list_t *gc_reflist_weak;
+
 #if !defined(ENABLE_THREADS)
 executionstate_t *_no_threads_executionstate;
 sourcestate_t    *_no_threads_sourcestate;
@@ -78,6 +78,16 @@ void gc_init(u4 heapmaxsize, u4 heapstartsize)
                dolog("GC: Initialising with heap-size %d (max. %d)",
                        heapstartsize, heapmaxsize);
 
+#if defined(ENABLE_HANDLES)
+       /* check our indirection cells */
+       if (OFFSET(java_handle_t, heap_object) != 0)
+               vm_abort("gc_init: indirection cell offset is displaced: %d", OFFSET(java_handle_t, heap_object));
+       if (OFFSET(hashtable_classloader_entry, object) != 0)
+               vm_abort("gc_init: classloader entry cannot be used as indirection cell: %d", OFFSET(hashtable_classloader_entry, object));
+       if (OFFSET(hashtable_global_ref_entry, o) != 0)
+               vm_abort("gc_init: global reference entry cannot be used as indirection cell: %d", OFFSET(hashtable_global_ref_entry, o));
+#endif
+
        /* finalizer stuff */
        final_init();
 
@@ -85,6 +95,10 @@ void gc_init(u4 heapmaxsize, u4 heapstartsize)
        gc_pending = false;
        gc_running = false;
 
+       /* create list for external references */
+       gc_reflist_strong = list_create(OFFSET(list_gcref_entry_t, linkage));
+       gc_reflist_weak   = list_create(OFFSET(list_gcref_entry_t, linkage));
+
        /* region for uncollectable objects */
        heap_region_sys = NEW(regioninfo_t);
        if (!region_create(heap_region_sys, GC_SYS_SIZE))
@@ -100,6 +114,109 @@ void gc_init(u4 heapmaxsize, u4 heapstartsize)
 }
 
 
+/* gc_reference_register *******************************************************
+
+   Register an external reference which points onto the Heap. The
+   reference needs to be cleared (set to NULL) when registering and
+   has to be set after it has been registered (to avoid a race condition).
+   
+   STRONG REFERENCE: gets updated and keeps objects alive
+   WEAK REFERENCE:   only gets updated (or maybe cleared)
+
+*******************************************************************************/
+
+static void gc_reference_register_intern(list_t *list, java_object_t **ref, int32_t reftype)
+{
+       list_gcref_entry_t *re;
+
+       /* the global GC lock also guards the reference lists */
+       GC_MUTEX_LOCK;
+
+       GC_LOG2( printf("Registering Reference at %p\n", (void *) ref); );
+
+       /* the reference needs to be registered before it is set, so make sure the
+          reference is not yet set */
+       GC_ASSERT(*ref == NULL);
+
+#if !defined(NDEBUG)
+       /* check if this reference is already registered */
+       for (re = list_first(list); re != NULL; re = list_next(list, re)) {
+               if (re->ref == ref)
+                       vm_abort("gc_reference_register_intern: reference already registered");
+       }
+#endif
+
+       /* create a new reference entry */
+       re = NEW(list_gcref_entry_t);
+
+       re->ref     = ref;
+#if !defined(NDEBUG)
+       re->reftype = reftype;
+#endif
+
+       /* add the entry to the given list */
+       list_add_last(list, re);
+
+       /* the global GC lock also guards the reference lists */
+       GC_MUTEX_UNLOCK;
+}
+
+void gc_reference_register(java_object_t **ref, int32_t reftype)
+{
+       gc_reference_register_intern(gc_reflist_strong, ref, reftype);
+}
+
+void gc_weakreference_register(java_object_t **ref, int32_t reftype)
+{
+       gc_reference_register_intern(gc_reflist_weak, ref, reftype);
+}
+
+
+/* gc_reference_unregister *****************************************************
+
+   Unregister a previously registered external reference.
+
+*******************************************************************************/
+
+static void gc_reference_unregister_intern(list_t *list, java_object_t **ref)
+{
+       list_gcref_entry_t *re;
+
+       /* the global GC lock also guards the reference lists */
+       GC_MUTEX_LOCK;
+
+       GC_LOG2( printf("Un-Registering Reference at %p\n", (void *) ref); );
+
+       /* search for the appropriate reference entry */
+       for (re = list_first(list); re != NULL; re = list_next(list, re)) {
+               if (re->ref == ref) {
+                       /* remove the entry from the given list */
+                       list_remove(list, re);
+
+                       /* free the reference entry */
+                       FREE(re, list_gcref_entry_t);
+
+                       break;
+               }
+       }
+
+       vm_abort("gc_reference_unregister_intern: reference not found");
+
+       /* the global GC lock also guards the reference lists */
+       GC_MUTEX_UNLOCK;
+}
+
+void gc_reference_unregister(java_object_t **ref)
+{
+       gc_reference_unregister_intern(gc_reflist_strong, ref);
+}
+
+void gc_weakreference_unregister(java_object_t **ref)
+{
+       gc_reference_unregister_intern(gc_reflist_weak, ref);
+}
+
+
 /* gc_collect ******************************************************************
 
    This is the main machinery which manages a collection. It should be run by
@@ -116,21 +233,21 @@ void gc_init(u4 heapmaxsize, u4 heapstartsize)
 void gc_collect(s4 level)
 {
        rootset_t    *rs;
-       s4            dumpsize;
+       int32_t       dumpmarker;
+#if !defined(NDEBUG)
+       stacktrace_t *st;
+#endif
 #if defined(ENABLE_RT_TIMING)
        struct timespec time_start, time_suspend, time_rootset, time_mark, time_compact, time_end;
 #endif
 
-       /* this is only quick'n'dirty check, but is NOT thread safe */
-       if (gc_pending || gc_running) {
-               GC_LOG( dolog("GC: Preventing reentrance!"); );
-               return;
-       }
-
-       /* TODO: some global GC lock!!! */
+       /* enter the global gc lock */
+       GC_MUTEX_LOCK;
 
        /* remember start of dump memory area */
-       dumpsize = dump_size();
+       DMARKER;
+
+       GCSTAT_COUNT(gcstat_collections);
 
        RT_TIMING_GET_TIME(time_start);
 
@@ -146,11 +263,24 @@ void gc_collect(s4 level)
        GC_LOG( dolog("GC: Suspending threads ..."); );
        GC_LOG( threads_dump(); );
        threads_stopworld();
-       GC_LOG( threads_dump(); );
+       /*GC_LOG( threads_dump(); );*/
        GC_LOG( dolog("GC: Suspension finished."); );
 #endif
 
-       /* sourcestate of the current thread */
+#if !defined(NDEBUG)
+       /* get the stacktrace of the current thread and make sure it is non-empty */
+       GC_LOG( printf("Stacktrace of current thread:\n"); );
+       st = stacktrace_get_current();
+       if (st == NULL)
+               vm_abort("gc_collect: no stacktrace available for current thread!");
+       GC_LOG( stacktrace_print(st); );
+#endif
+
+       /* sourcestate of the current thread, assuming we are in the native world */
+       GC_LOG( dolog("GC: Stackwalking current thread ..."); );
+#if defined(ENABLE_THREADS)
+       GC_ASSERT(THREADOBJECT->flags & THREAD_FLAG_IN_NATIVE);
+#endif
        replace_gc_from_native(THREADOBJECT, NULL, NULL);
 
        /* everyone is halted now, we consider ourselves running */
@@ -165,7 +295,12 @@ void gc_collect(s4 level)
 
        /* find the global and local rootsets */
        rs = rootset_readout();
-       GC_LOG( rootset_print(rs); );
+
+#if !defined(NDEBUG)
+       /* print the rootsets if debugging is enabled */
+       if (opt_GCDebugRootSet)
+               rootset_print(rs);
+#endif
 
        RT_TIMING_GET_TIME(time_rootset);
 
@@ -173,14 +308,13 @@ void gc_collect(s4 level)
 
        /* mark the objects considering the given rootset */
        mark_me(rs);
-       /*GC_LOG( heap_dump_region(heap_region_main, true); );*/
+       /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
 
        RT_TIMING_GET_TIME(time_mark);
 
        /* compact the heap */
        compact_me(rs, heap_region_main);
-       /*GC_LOG( heap_dump_region(heap_region_main, false); );
-       GC_LOG( rootset_print(rs); );*/
+       /*GC_LOG( heap_dump_region(heap_region_main, false); );*/
 
 #if defined(ENABLE_MEMCHECK)
        /* invalidate the rest of the main region */
@@ -189,6 +323,10 @@ void gc_collect(s4 level)
 
        RT_TIMING_GET_TIME(time_compact);
 
+       /* check if we should increase the heap size */
+       if (gc_get_free_bytes() < gc_get_heap_size() / 3) /* TODO: improve this heuristic */
+               heap_increase_size(rs);
+
 #else
 
        /* copy the heap to new region */
@@ -218,14 +356,7 @@ void gc_collect(s4 level)
                gcstat_println();
 #endif
 
-#if defined(GCCONF_FINALIZER)
-       /* does the finalizer need to be notified */
-       if (gc_notify_finalizer)
-               finalizer_notify();
-#endif
-
        /* we are no longer running */
-       /* REMEBER: keep this below the finalizer notification */
        gc_running = false;
 
 #if defined(ENABLE_THREADS)
@@ -235,6 +366,12 @@ void gc_collect(s4 level)
        /*GC_LOG( threads_dump(); );*/
 #endif
 
+#if defined(GCCONF_FINALIZER)
+       /* does the finalizer need to be notified */
+       if (gc_notify_finalizer)
+               finalizer_notify();
+#endif
+
        RT_TIMING_GET_TIME(time_end);
 
        RT_TIMING_TIME_DIFF(time_start  , time_suspend, RT_TIMING_GC_SUSPEND);
@@ -245,8 +382,13 @@ void gc_collect(s4 level)
        RT_TIMING_TIME_DIFF(time_start  , time_end    , RT_TIMING_GC_TOTAL);
 
     /* free dump memory area */
-    dump_release(dumpsize);
+    DRELEASE;
+
+       /* leave the global gc lock */
+       GC_MUTEX_UNLOCK;
 
+       /* XXX move this to an appropriate place */
+       lock_hashtable_cleanup();
 }
 
 
@@ -331,6 +473,8 @@ void gc_call(void)
        if (opt_verbosegc)
                dolog("GC: Forced Collection ...");
 
+       GCSTAT_COUNT(gcstat_collections_forced);
+
        gc_collect(0);
 
        if (opt_verbosegc)
@@ -362,11 +506,23 @@ void gc_invoke_finalizers(void)
    Forces the finalization of all objects on the Java Heap.
    This is the function which is called by java.lang.Runtime.exit()
 
+   We do this by setting all objects with finalizers to reclaimable,
+   which is inherently dangerouse because objects may still be alive.
+
 *******************************************************************************/
 
 void gc_finalize_all(void)
 {
-       vm_abort("gc_finalize_all: IMPLEMENT ME!");
+#if !defined(NDEBUG)
+       /* doing this is deprecated, inform the user */
+       dolog("gc_finalize_all: Deprecated!");
+#endif
+
+       /* set all objects with finalizers to reclaimable */
+       final_set_all_reclaimable();
+
+       /* notify the finalizer thread */
+       finalizer_notify();
 }
 
 
@@ -381,12 +537,18 @@ s8 gc_get_max_heap_size(void) { return heap_maximal_size; }
 /* Statistics *****************************************************************/
 
 #if defined(ENABLE_STATISTICS)
+int gcstat_collections;
+int gcstat_collections_forced;
 int gcstat_mark_depth;
 int gcstat_mark_depth_max;
 int gcstat_mark_count;
 
 void gcstat_println()
 {
+       printf("\nGCSTAT - General Statistics:\n");
+       printf("\t# of collections: %d\n", gcstat_collections);
+       printf("\t# of forced collections: %d\n", gcstat_collections_forced);
+
     printf("\nGCSTAT - Marking Statistics:\n");
     printf("\t# of objects marked: %d\n", gcstat_mark_count);
     printf("\tMaximal marking depth: %d\n", gcstat_mark_depth_max);