Background:
The relationship between the various objects are quite tricky. It takes some time to
think them through. Basically, to reference a Tcl_Obj, the C structure representing a
Tcl object in the C Tcl interpreter, TclBlend creates a TclObject, a Java class
instance using the following relationship:
TclObject (tcl.lang.TclObject, Java class)
CObject (tcl.lang.CObject, Java class)
a pointer to Tcl_Obj
CObject is a subclass of InternalObject (tcl.lang.InternalObject). InternalObject is
an abstract class containing an interface so that TclObject can be used as the common
interface to not only Tcl_Obj, but also other types of TclBlend objects such as
ReflectObject, TclInteger, etc, all of those are subclass of InternalObject.
On the C side, the C Tcl interpreter needs access to Java objects defined in TclBlend,
such as ReflectObject, TclInteger, etc. TclBlend uses a Tcl_Obj implementation to
wrap around any TclObject using the following relationship:
Tcl_Obj (A C struct with function pointers to functions
that are specifically designed to manipulate
TclBlend's TclObject. Defined in javaObj.c in
TclBlend.)
reference/pointer to the Java TclObject
It seems that you can get infinite recusive containment as in:
Tcl_Object
Tcl_Obj
Tcl_Object
Tcl_Obj
...
TclBlend is smart enough to detect such attempt. When it is detected, TclBlend
removes the extra level containment so that most of the time you will have either:
Tcl_Object Tcl_Obj
or
Tcl_Obj Tcl_Object
The Problem:
There are 2 conditions, which taken together causes problem with the above setup in
TclBlend.
1. How are the objects been cleaned up (free'ed)? Tcl_Obj needs to be cleaned up
excplictly using reference counting. Tcl_Object can be implicitly cleaned up using
the GC. The original implementation in TclBlend is to use the GC to implicitly clean
up Tcl_Object, which in turns cleans up the Tcl_Obj using an explict reference count
decrement. By transitivity, this means that the Tcl_Obj are cleaned up implicitly by
the GC. There is no problem by itself.
2. Tcl_Obj can only be free'ed by the same thread that created it (due to performance
benefits). This behavior is by design and does not cause any problem by itself either
because Tcl programs are usually written in C/C++, which always uses explicit memory
management without anything like a GC thread.
Taken 1 and 2 together causes a problem. The GC thread may be freeing a Tcl_Obj that
is not created by the GC thread. This produces concurrency problems in Tcl.
The proposed solution is to never use the GC thread to free Tcl_Obj and always use the
thread that created the Tcl_Obj to free the Tcl_Obj.
See below for more analysis of the problem. You can search the maling list archive
for more discussion on this under the general subject of "GC".
-- Jiang Wu
[EMAIL PROTECTED]
--- Start of forwarded message ---
Subject: TclBlend Ref Counting Bug (GC related)
To: [EMAIL PROTECTED], [EMAIL PROTECTED], [EMAIL PROTECTED]
From: Jiang Wu <[EMAIL PROTECTED]>
Cc: [EMAIL PROTECTED]
Date: 08 Aug 2000 00:12:48 PDT
Currently, there is a plan to make the Java GC perform some cleanup on Tcl_Obj in
TclBlend. Looking at the root cause for the GC trouble, I think there is a problem
with how TclBlend uses Tcl_Obj reference counting. Here is my analysis. I think if
we fix the reference counting, we don't need to do anything in the GC.
TclBlend does not follow the normal Tcl_Obj reference counting usage. In TclBlend, a
Java TclObject is used as the handle to the underlining Tcl_Obj:
TclObject
CObject
Tcl_Obj
Both Tcl_Obj and TclObject have reference counting. However, when incrementing or
decrementing the reference count on the TclObject, this incrementing or decrementing
is proxied into the native Tcl_Obj. This doesn't seem correct.
In addition, when a new TclObject is created to wrap around a Tcl_Obj, the reference
count of the TclObject is 0, but implicitly, the reference count of Tcl_Obj is
incremented by 1. This doesn't seem right either.
For example, in C, a call to Tcl_GetVar2Ex(...) returns a Tcl_Obj, of which, "The ref
count for the returned object is _not_ incremented to reflect the returned reference;
if you want to keep a reference to the object you must increment its ref count
yourself."
In TclBlend, the corresponding call is "interp->getVar()", which uses
Tcl_GetVar2Ex(...). However, the resulting Tcl_Obj does have its reference count
incremented by 1 implicitly by TclBlend.
The problem with this implicit increment of native Tcl_Obj's reference count is that
someone, namely the GC thread, must decrement the reference count. This causes all
sort of undesirable behaviors such as the GC thread deadlocking because Tcl_Obj's
reference count can only be modified by the thread that first created the Tcl