On Wed, 14 Jun 2000, W. John Guineau wrote:

> Without having looked into the innerds of TclBlend's JNI implementation
> (though it looks quite clean at least) I'd say that removing the monitor
> calls and JAVA_LOCK are probably fine.
> 
> I say this because we have a decent size app (~250K lines of C/C++/Java)
> that uses a JNI layer I wrote. I never use locking in the JNI layer (in
> general) and have multithreaded access without any apparent problems (the
> JNI part is the backend of a Java based server that provides access to
> common C code.)



I am still a bit worried about the gc thread walking around
in the Tcl interp at any random time.


>From the deadlock problem report.

3) The sequence of events leading up to deadlock is the following:

a) the main thread calls MonitorExit in JavaCmdProc
b) the garbage collection thread calls MonitorExit in JavaCmdDeleteProc
c) the garbage collection thread calls MonitorEnter in JavaCmdDeleteProc
d) the main thread calls MonitorEnter in JavaCmdProc (and blocks)


>From JavaCmdProc():

    /*
     * Invoke the command by calling Interp.callCommand().  Be sure to
     * leave the monitor since we are assuming nothing about the state
     * of the world after this call.
     */

    JAVA_UNLOCK();
    result = (*env)->CallIntMethod(env, interpObj,
            java.callCommand, cmd, args);
    exception = (*env)->ExceptionOccurred(env);
    JAVA_LOCK();



Inside Java_tcl_lang_Interp_createCommand, we tell the Tcl command
to call JavaCmdDeleteProc when it gets deleted:


   Tcl_CreateObjCommand(interp, (/*UNCONST*/ char *) name, JavaCmdProc,
            (ClientData) cmd, JavaCmdDeleteProc);



This calls interface CommandWithDispose.dispose() method:

static void
JavaCmdDeleteProc(
    ClientData clientData)
{
    jobject cmd = (jobject)clientData;
    JNIEnv *env = JavaGetEnv(NULL);

    if ((*env)->IsInstanceOf(env, cmd, java.CommandWithDispose)) {
        /*
         * We need to release the monitor while running arbitrary Java code.
         */

        JAVA_UNLOCK();
        (*env)->CallVoidMethod(env, cmd, java.disposeCmd);
        JAVA_LOCK();
    }
    (*env)->DeleteGlobalRef(env, (jobject)clientData);
}



It seems like the call to JavaCmdDeleteProc() in the GC thread
would not need to unlock anything because it did not have the
monitor in the first place, it was released before calling
the Java method in JavaCmdProc(). Also, this seems
to only happen in the wacky case where the command we
are invoking is getting deleted from the interp, I am
not sure why this has never come up before, perhaps is
is a race condition or the Sun GC thread is running
earlier than it did before.


I did a detailed trace of how JavaCmdDeleteProc() would be
called by the GC thread (I could not figure out how that
would ever happen at first).


We start at the CObject.finalize() method, which is getting
called by the GC thread.

/* Clean up the native Tcl_Obj when a CObject is collected. */

protected void finalize() throws Throwable
{
    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*.
         */

        decrRefCount(objPtr);
    }
    super.finalize();
}


That calls CObject.decrRefCount() which is implemented in
the native method Java_tcl_lang_CObject_decrRefCount.
This native method then calls Tcl_DecrRefCount().

Then, a bunch of calls into Tcl and Java:

Tcl_DecrRefCount() -> TclFreeObj() -> freeIntRepProc() ->
FreeJavaCmdInternalRep() -> FreeTclObject() -> TclObject.release() ->
InternalRep.dispose() -> ReflectObject.dispose() ->
Interp.deleteCommand() -> Java_tcl_lang_Interp_deleteCommand() ->
Tcl_DeleteCommand() -> Tcl_DeleteCommandFromToken() -> JavaCmdDeleteProc()


So finally, after all that, we know how to GC thread ended up
calling JavaCmdDeleteProc().


The real question here is what should we do about the GC thread?
Should we have a lock for each interp that will need to be grabbed
by the interp thread or the GC thread before the internals of
the Interp can be accessed? It would be a real drag if every Tcl
event needed to grab a global GC monitor before the event could
be processed.

I don't think we really want to halt the GC thread for long periods
of time waiting for the Interp to get done doing something. This
is what would happen if put an event into the Tcl event queue
and made the GC thread block until the event was processed.

This is kind of wacky because in the case where Tcl is not compiled with 
threads enabled, we could still have a GC thread knocking on the door.
Perhaps we should just require Tcl compiled with thread support.

Mo DeJong
Red Hat Inc

----------------------------------------------------------------
The TclJava mailing list is sponsored by Scriptics Corporation.
To subscribe:    send mail to [EMAIL PROTECTED]  
                 with the word SUBSCRIBE as the subject.
To unsubscribe:  send mail to [EMAIL PROTECTED] 
                 with the word UNSUBSCRIBE as the subject.
To send to the list, send email to '[EMAIL PROTECTED]'. 
An archive is available at http://www.mail-archive.com/tcljava@scriptics.com

Reply via email to