* src/vm/finalizer.c (finalizer_run): Fixed for handles and added boehm-hack.
authorMichael Starzinger <michi@complang.tuwien.ac.at>
Mon, 8 Oct 2007 23:22:03 +0000 (01:22 +0200)
committerMichael Starzinger <michi@complang.tuwien.ac.at>
Mon, 8 Oct 2007 23:22:03 +0000 (01:22 +0200)
(finalizer_thread, finalizer_notify): Added debug output.

* src/vmcore/options.c (opt_DebugFinalizer): Added debug switch.
* src/vmcore/options.h (opt_DebugFinalizer): Likewise.

* src/threads/native/lock.c (lock_hashtable_remove): Also frees the lock record.
(lock_record_finalizer): Same as for finalizer_run above.
(lock_hashtable_get) [ENABLE_GC_BOEHM]: Fixed registration of finalizer.

* tests/gc/LockRecords.java: Added new testcase for lock records cleanup.

src/threads/native/lock.c
src/vm/finalizer.c
src/vmcore/options.c
src/vmcore/options.h
tests/gc/LockRecords.java [new file with mode: 0644]

index e1afd493c99e9a1ef1db619980602d2f604ec169..36294c93c1b4828ad87e71970ee84bdf705bb76c 100644 (file)
@@ -467,7 +467,7 @@ static lock_record_t *lock_hashtable_get(threadobject *t, java_handle_t *o)
 #if defined(ENABLE_GC_BOEHM)
                /* register new finalizer to clean up the lock record */
 
-               GC_REGISTER_FINALIZER(o, lock_record_finalizer, 0, 0, 0);
+               GC_REGISTER_FINALIZER(LLNI_DIRECT(o), lock_record_finalizer, 0, 0, 0);
 #endif
 
                /* enter it in the hashtable */
@@ -495,14 +495,16 @@ static lock_record_t *lock_hashtable_get(threadobject *t, java_handle_t *o)
 
 /* lock_hashtable_remove *******************************************************
 
-   Remove the lock record for the given object from the hashtable.
+   Remove the lock record for the given object from the hashtable
+   and free it afterwards.
 
    IN:
+       t....the current thread
        o....the object to look up
 
 *******************************************************************************/
 
-static void lock_hashtable_remove(java_object_t *o)
+static void lock_hashtable_remove(threadobject *t, java_handle_t *o)
 {
        uintptr_t      lockword;
        lock_record_t *lr;
@@ -515,7 +517,7 @@ static void lock_hashtable_remove(java_object_t *o)
 
        /* get lock record */
 
-       lockword = o->lockword;
+       lockword = lock_lockword_get(t, o);
 
        assert(IS_FAT_LOCK(lockword));
 
@@ -523,8 +525,10 @@ static void lock_hashtable_remove(java_object_t *o)
 
        /* remove the lock-record from the hashtable */
 
-       slot  = heap_hashcode(o) % lock_hashtable.size;
+       LLNI_CRITICAL_START_THREAD(t);
+       slot  = heap_hashcode(LLNI_DIRECT(o)) % lock_hashtable.size;
        tmplr = lock_hashtable.ptr[slot];
+       LLNI_CRITICAL_END_THREAD(t);
 
        if (tmplr == lr) {
                /* special handling if it's the first in the chain */
@@ -549,6 +553,10 @@ static void lock_hashtable_remove(java_object_t *o)
        /* unlock the hashtable */
 
        pthread_mutex_unlock(&(lock_hashtable.mutex));
+
+       /* free the lock record */
+
+       lock_record_free(lr);
 }
 
 
@@ -560,32 +568,37 @@ static void lock_hashtable_remove(java_object_t *o)
 
 static void lock_record_finalizer(void *object, void *p)
 {
-       java_object_t *o;
-       uintptr_t      lockword;
-       lock_record_t *lr;
-
-       o = (java_object_t *) object;
-
-       /* check for a finalizer function */
+       java_handle_t *o;
+       classinfo     *c;
 
-       if (o->vftbl->class->finalizer != NULL)
-               finalizer_run(object, p);
+       o = (java_handle_t *) object;
 
-       /* remove the lock-record entry from the hashtable */
+#if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES)
+       /* XXX this is only a dirty hack to make Boehm work with handles */
 
-       lock_hashtable_remove(o);
+       o = LLNI_WRAP((java_object_t *) o);
+#endif
 
-       /* get lock record */
+       LLNI_class_get(o, c);
 
-       lockword = o->lockword;
+#if !defined(NDEBUG)
+       if (opt_DebugFinalizer) {
+               log_start();
+               log_print("[finalizer lockrecord: o=%p p=%p class=", object, p);
+               class_print(c);
+               log_print("]");
+               log_finish();
+       }
+#endif
 
-       assert(IS_FAT_LOCK(lockword));
+       /* check for a finalizer function */
 
-       lr = GET_FAT_LOCK(lockword);
+       if (c->finalizer != NULL)
+               finalizer_run(object, p);
 
-       /* now release the lock record */
+       /* remove the lock-record entry from the hashtable and free it */
 
-       lock_record_free(lr);
+       lock_hashtable_remove(THREADOBJECT, o);
 }
 
 
index 22c687733f70406e36a87421b506a71a39b1e248..dc7da8532e1f5222e23e8a7a6b079fc69160505c 100644 (file)
@@ -98,9 +98,19 @@ static void finalizer_thread(void)
 
                LOCK_MONITOR_EXIT(lock_thread_finalizer);
 
+#if !defined(NDEBUG)
+               if (opt_DebugFinalizer)
+                       log_println("[finalizer thread    : status=awake]");
+#endif
+
                /* and call the finalizers */
 
                gc_invoke_finalizers();
+
+#if !defined(NDEBUG)
+               if (opt_DebugFinalizer)
+                       log_println("[finalizer thread    : status=sleeping]");
+#endif
        }
 }
 #endif
@@ -138,6 +148,11 @@ bool finalizer_start_thread(void)
 
 void finalizer_notify(void)
 {
+#if !defined(NDEBUG)
+       if (opt_DebugFinalizer)
+               log_println("[finalizer notified]");
+#endif
+
 #if defined(ENABLE_THREADS)
        /* get the lock on the finalizer lock object, so we can call wait */
 
@@ -166,13 +181,39 @@ void finalizer_notify(void)
 
 void finalizer_run(void *o, void *p)
 {
-       java_object_t *ob;
+       java_handle_t *h;
+       classinfo     *c;
+
+       h = (java_handle_t *) o;
+
+#if !defined(ENABLE_GC_CACAO) && defined(ENABLE_HANDLES)
+       /* XXX this is only a dirty hack to make Boehm work with handles */
 
-       ob = (java_object_t *) o;
+       h = LLNI_WRAP((java_object_t *) h);
+#endif
+
+       LLNI_class_get(h, c);
+
+#if !defined(NDEBUG)
+       if (opt_DebugFinalizer) {
+               log_start();
+               log_print("[finalizer running   : o=%p p=%p class=", o, p);
+               class_print(c);
+               log_print("]");
+               log_finish();
+       }
+#endif
 
        /* call the finalizer function */
 
-       (void) vm_call_method(ob->vftbl->class->finalizer, ob);
+       (void) vm_call_method(c->finalizer, h);
+
+#if !defined(NDEBUG)
+       if (opt_DebugFinalizer && (exceptions_get_exception() != NULL)) {
+               log_println("[finalizer exception]");
+               exceptions_print_stacktrace();
+       }
+#endif
 
        /* if we had an exception in the finalizer, ignore it */
 
index 21d2a92e9552a4c19a2b9c3cecbb5b28675f48bb..e3addda71440b93b140d45a02e68afe852911053 100644 (file)
@@ -172,6 +172,7 @@ const char *opt_filter_show_method = 0;
 /* NOTE: For better readability keep these alpha-sorted. */
 
 int      opt_DebugExceptions           = 0;
+int      opt_DebugFinalizer            = 0;
 int      opt_DebugLocalReferences      = 0;
 int      opt_DebugLocks                = 0;
 int      opt_DebugPackage              = 0;
@@ -215,6 +216,7 @@ enum {
 
 enum {
        OPT_DebugExceptions,
+       OPT_DebugFinalizer,
        OPT_DebugLocalReferences,
        OPT_DebugLocks,
        OPT_DebugPackage,
@@ -246,6 +248,7 @@ enum {
 
 option_t options_XX[] = {
        { "DebugExceptions",           OPT_DebugExceptions,           OPT_TYPE_BOOLEAN, "debug exceptions" },
+       { "DebugFinalizer",            OPT_DebugFinalizer,            OPT_TYPE_BOOLEAN, "debug finalizer thread" },
        { "DebugLocalReferences",      OPT_DebugLocalReferences,      OPT_TYPE_BOOLEAN, "print debug information for local reference tables" },
        { "DebugLocks",                OPT_DebugLocks,                OPT_TYPE_BOOLEAN, "print debug information for locks" },
        { "DebugPackage",              OPT_DebugPackage,              OPT_TYPE_BOOLEAN, "debug Java boot-packages" },
@@ -538,6 +541,10 @@ void options_xx(JavaVMInitArgs *vm_args)
                        opt_DebugExceptions = enable;
                        break;
 
+               case OPT_DebugFinalizer:
+                       opt_DebugFinalizer = enable;
+                       break;
+
                case OPT_DebugLocalReferences:
                        opt_DebugLocalReferences = enable;
                        break;
index 89192e3e97467fa38197bcff9e88d81edf6ee896..54ee97de5483bbf213492d32696e408d83ede790 100644 (file)
@@ -187,6 +187,7 @@ extern const char *opt_filter_show_method;
 /* NOTE: For better readability keep these alpha-sorted. */
 
 extern int      opt_DebugExceptions;
+extern int      opt_DebugFinalizer;
 extern int      opt_DebugLocalReferences;
 extern int      opt_DebugLocks;
 extern int      opt_DebugPatcher;
diff --git a/tests/gc/LockRecords.java b/tests/gc/LockRecords.java
new file mode 100644 (file)
index 0000000..96e7868
--- /dev/null
@@ -0,0 +1,89 @@
+import java.util.Vector;
+
+public class LockRecords {
+       static final int N = 3;
+
+       static Vector v;
+
+       static class MyObject
+       {
+               public int id;
+               public int counter;
+
+               protected void finalize() throws Throwable
+               {
+                       System.out.println("\tObject #" + id + " gets finalized");
+                       throw new Exception("Object #" + id + " is nasty!");
+               }
+       };
+
+       static class MyThread extends Thread
+       {
+               public String name;
+               public synchronized void run()
+               {
+                       System.out.println("\t" + name + ": Starting ...");
+                       try {
+                               for (int i=0; i<N; i++) {
+                                       MyObject o = (MyObject) v.get(i);
+                                       System.out.println("\t" + name + ": Trying on #" + o.id);
+                                       synchronized(o) {
+                                               System.out.println("\t" + name + ": Locked on #" + o.id);
+                                               this.wait(200);
+                                               System.out.println("\t" + name + ": Releasing #" + o.id);
+                                               o.counter--;
+                                       }
+                                       while (o.counter > 0) {
+                                               this.wait(10);
+                                       }
+                               }
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                       }
+                       System.out.println("\t" + name + ": finished.");
+               }
+       };
+
+       public synchronized void test()
+       {
+               System.out.println("Creating Objects ...");
+               v = new Vector();
+               for (int i=0; i<N; i++) {
+                       MyObject o = new MyObject();
+                       o.id = i;
+                       o.counter = 2;
+                       v.add(o);
+               }
+
+               System.out.println("Starting Blocking Threads A + B...");
+               MyThread threadA = new MyThread();
+               MyThread threadB = new MyThread();
+               threadA.name = "A"; threadA.start();
+               threadB.name = "B"; threadB.start();
+               try {
+                       threadA.join();
+                       threadB.join();
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+
+               System.out.println("Cleaning up ...");
+               v = null;
+               System.gc();
+               //System.gc();
+               //System.gc();
+
+               System.out.println("Waiting some seconds ...");
+               try {
+                       wait(3000);
+               } catch (Exception e) {
+                       e.printStackTrace();
+               }
+       }
+
+       public static void main(String args[])
+       {
+               LockRecords test = new LockRecords();
+               test.test();
+       }
+}