(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.
#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 */
/* 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;
/* get lock record */
- lockword = o->lockword;
+ lockword = lock_lockword_get(t, o);
assert(IS_FAT_LOCK(lockword));
/* 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 */
/* unlock the hashtable */
pthread_mutex_unlock(&(lock_hashtable.mutex));
+
+ /* free the lock record */
+
+ lock_record_free(lr);
}
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);
}
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
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 */
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 */
/* 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;
enum {
OPT_DebugExceptions,
+ OPT_DebugFinalizer,
OPT_DebugLocalReferences,
OPT_DebugLocks,
OPT_DebugPackage,
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" },
opt_DebugExceptions = enable;
break;
+ case OPT_DebugFinalizer:
+ opt_DebugFinalizer = enable;
+ break;
+
case OPT_DebugLocalReferences:
opt_DebugLocalReferences = enable;
break;
/* 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;
--- /dev/null
+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();
+ }
+}