Boy, I must have been on crack yesterday. The patch I posted
that used a global table to keep a legal ref to a cleanup
object was way way too complex. After rereading section 12.6.1
of the JLS, I realized the error of my ways.

http://java.sun.com/docs/books/jls/second_edition/html/execution.doc.html#44760

When the finalize() method gets invoked, it moves the
object getting finalized back into a reachable state.

unreachable -> f-reachable -> reachable -> finalize() -> unreachable

Then after the finalize() method has been run
it transitions to the unreachable state and
it later cleaned up.

That means that while the finalize() method is
getting run, all the objects it contains are
reachable, so we can "rescue" a member object.

What that means, is that in our CObject class,
we can just keep a ref to a cleanup object in
the CObject instance and then append it
to the queue in the finalize() method. No
external table is required. Needless to say,
this makes things much much simpler. Now all
we need to do is create a cleanup object in
the CObject constructor, and then queue it up
in the finalize() method.

CObject(
    long objPtr)                // Pointer to Tcl_Obj from C.
{
    this.objPtr = objPtr;
    incrRefCount(objPtr);

    notifier = Notifier.getNotifierForThread(Thread.currentThread());
    cleanup = new CleanupCallback();
}

protected void finalize() throws Throwable
{
    if (objPtr != 0) {
        /*
         * If a CObject is finalized while the reference is still valid, 
         * we need to send the reference back to the thread that created it.
         * We can then sever the connection to the underlying Tcl_Obj* by
         * calling decrRefCount() in that thread. Note that we can not wait
         * for the event to be processed, as it would block the GC thread.
         */
        cleanup.objPtr = objPtr;
        notifier.queueEvent(cleanup, TCL.QUEUE_TAIL);
    }
    super.finalize();
}


The patch that implements this change is attached to
this email. Folks should try this patch out instead
of the one I posted yesteday. I think this
implementation is much better, but there is
one more thing we could try to avoid the
extra object in the CObject. It might be possible
to turn the CObject into a TclEvent and queue
that up. I am not sure if this is legal so I
am going to have to read the spec a bit more.
It would mean that it would have to be possible
to invoke a method on the object after the finalize()
method had been run.

cheers
Mo DeJong
Red Hat Inc
Index: CObject.java
===================================================================
RCS file: /cvsroot/tcljava/tcljava/src/tclblend/tcl/lang/CObject.java,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 CObject.java
--- CObject.java        1998/10/14 21:09:10     1.1.1.1
+++ CObject.java        2000/10/22 23:19:01
@@ -24,14 +24,22 @@
 
 class CObject extends InternalRep {
 
-/*
- * This long really contains a Tcl_Obj*.  It is declared with package
- * visibility so that subclasses that define type specific functionality
- * can get to the Tcl_Obj.
- */
+// This long really contains a Tcl_Obj*.  It is declared with package
+// visibility so that subclasses that define type specific functionality
+// can get to the Tcl_Obj. This field can be read from C code.
 
 long objPtr;
 
+// A cleanup instance is stored in each CObject so that in the case
+// where a dangling reference to a Tcl_Obj* is found, an event
+// can be queued to free up the dangling pointer in the original thread.
+
+CleanupCallback cleanup;
+
+// The notifier for the thread that created this CObject.
+
+Notifier notifier;
+
 
 /*
  *----------------------------------------------------------------------
@@ -51,8 +59,7 @@
 
 CObject()
 {
-    this.objPtr = newCObject(null);
-    incrRefCount(objPtr);
+    this(newCObject(null));
 }
 
 /*
@@ -76,6 +83,10 @@
 {
     this.objPtr = objPtr;
     incrRefCount(objPtr);
+
+    notifier = Notifier.getNotifierForThread(Thread.currentThread());
+
+    cleanup = new CleanupCallback();
 }
 
 /*
@@ -97,6 +108,7 @@
 public void
 dispose()
 {
+    cleanup = null;
     decrRefCount(objPtr);
     objPtr = 0;
 }
@@ -180,6 +192,7 @@
  * newInstance --
  *
  *     Construct a new TclObject from a Tcl_Obj*.  This routine is only
+ *     called from C. It is also the only CObject method that can be
  *     called from C.
  *
  * Results:
@@ -218,11 +231,19 @@
 {
     if (objPtr != 0) {
        /*
-        * If the object is finalized while the reference is still valid, 
-        * we need to sever the connection to the underlying Tcl_Obj*.
+        * If a CObject is finalized while the reference is still valid, 
+        * we need to send the reference back to the thread that created it.
+        * We can then sever the connection to the underlying Tcl_Obj* by
+        * calling decrRefCount() in that thread. Note that we can not wait
+        * for the event to be processed, as it would block the GC thread.
         */
 
-       decrRefCount(objPtr);
+//FIXME: see if it would be possible to put the this object itself
+// back onto the queue, thus making it reachable again?????
+// we could then implement TclEvent on CObject and reduce mem use.
+        cleanup.objPtr = objPtr;
+        //System.err.println("queueing cleanup for " + cleanup.objPtr);
+        notifier.queueEvent(cleanup, TCL.QUEUE_TAIL);
     }
     super.finalize();
 }
@@ -230,6 +251,34 @@
 /*
  *----------------------------------------------------------------------
  *
+ * CleanupCallback --
+ *
+ *     This is a little helper class used to store the objPtr
+ *     and to take on the role of the TclEvent that will be
+ *     called after the event is queued.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static class CleanupCallback extends TclEvent {
+    long objPtr;
+
+    public int processEvent(int flags) {
+        //System.err.println("calling decrRefCount from processEvent for " + objPtr);
+        CObject.decrRefCount(objPtr);
+        return 1;
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * decrRefCount --
  *
  *     Decrement the refcount of a Tcl_Obj.
@@ -282,7 +331,6 @@
  *
  *----------------------------------------------------------------------
  */
-
 
 static final native long
 newCObject(

Reply via email to