commit-classpath
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

portable-native-sync-6.patch


From: Steven Augart
Subject: portable-native-sync-6.patch
Date: Wed, 26 May 2004 14:40:07 -0400
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7b) Gecko/20040421

OK to commit this time?
--
Steven Augart

Jikes RVM, a free, open source, Virtual Machine:
http://oss.software.ibm.com/jikesrvm
Changes to portable-native-sync-5.patch, as discussed on the
commit-classpath list.

ChangeLog entry:
2004-05-26  Steven Augart  <address@hidden>

        --portable-native-sync implemented for GTK2.
        
        * gnu/native/jni/gtk-peer/gthread-jni.c: Indentation fixes.
        
        Implemented missing functions for GTK2.

        Added error handling.
        
        Renamed static functions out of the g_ namespace.
        
        Added TRACE_API_CALLS, EXPLAIN_TROUBLE, EXPLAIN_BROKEN, 
        EXPLAIN_BADLY_BROKEN, and DELETE_LOCAL_REFS options.
        
        Rewrote global-reference code.
        
        Eliminated cascading errors.
        
        (mutex_trylock_jni_impl) Fully implemented.
        
        (cond_timed_wait_jni_impl) Went from millisecond to microsecond
        resolution.
        
        (setup_cache) New function.
        
        (mutex_cond_wait_jni_impl, mutex_cond_timed_wait_jni_impl) Fixed
        bug where they were not unlocking the GMutex associated with the
        condition variable during the wait on that condition variable.
        
        * native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c,
          native/jni/gtk-peer/gthread-jni.c,
          native/jni/gtk-peer/gthread-jni.h:
        
        (g_thread_jni_functions) Renamed to ...
        (portable_native_sync_jni_functions) this name.
        
        (gdk_vm) Renamed to...
        (the_vm) this name.
        
        * native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c: 
        (gdk_vm) Removed duplicate definition.
        
        (gtkInit) Removed stray message to stdout.
        
        (gtkInit) Use g_malloc and g_free instead of malloc and free.
        
        (gtkInit) Fix a const assignment bug.
        
        (gtkInit) Simplified code.
        
        * gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java,
        native/jni/gtk-peer/gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.c,
        native/jni/gtk-peer/gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h,
        gnu/java/awt/peer/gtk/GThreadMutex.java:
        New files.

--- /dev/null   2003-01-30 10:24:37.000000000 +0000
+++ gnu/java/awt/peer/gtk/GThreadMutex.java     2004-05-26 14:58:19.000000000 
+0000
@@ -0,0 +1,109 @@
+/* GThreadMutex.java -- Implements a mutex object for glib's gthread
+   abstraction, for use with GNU Classpath's --portable-native-sync option.
+   This is used in gthread-jni.c
+   
+   Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.java.awt.peer.gtk;
+
+/** Implements a mutex object for glib's gthread
+    abstraction, for use with GNU Classpath's --portable-native-sync option.
+    This is used in gthread-jni.c.
+
+    We use this object to implement the POSIX semantics for Mutexes.  They are
+    needed are needed for the function vector that is passed to glib's
+    g_thread subpackage's initialization function.
+
+    The GThreadMutex object itself serves as the Real Lock; if code has
+    entered the monitor for this GThreadMutex object (in Java language, if
+    it's synchronized on this object) then it holds the lock that this object
+    represents.
+
+    @author Steven Augart
+    May, 2004
+
+    
+*/
+   
+class GThreadMutex 
+{
+  /** Might "lock" be locked?  Is anyone waiting
+      to get that lock?  How long is the queue?
+
+      If zero, nobody holds a lock on this GThreadMutex object, and nobody is
+      trying to get one.   Before someone attempts to acquire a lock on this
+      object, they must increment potentialLockers.  After they release their
+      lock on this object, they must decrement potentialLockers.
+
+      Access to this field is guarded by synchronizing on the object
+      <code>lockForPotentialLockers</code>.
+
+      After construction, we only access this field via JNI.
+  */
+  volatile int potentialLockers;
+
+  /** An object to synchronize to if you want to examine or modify the
+      <code>potentialLockers</code> field.  Only hold this lock for brief
+      moments, just long enough to check or set the value of
+      <code>lockForPotentialLockers</code>.  
+      
+      We use this representation so that g_thread_mutex_trylock() will work
+      with the POSIX semantics.  This is the only case in which you ever hold a
+      lock on <code>lockForPotentialLockers</code> while trying to get another
+      lock -- if you are the mutex_trylock() implementation, and you have just
+      checked that <code>potentialLockers</code> has the value zero.  In that
+      case, mutex_trylock() holds the lock on lockForPotentialLockers so that
+      another thread calling mutex_trylock() or mutex_lock() won't increment
+      potentialLockers after we've checked it and before we've gained the lock
+      on the POSIX mutex.   Of course, in that case the operation of gaining
+      the POSIX lock itself will succeed immediately, and once it has
+      succeeded, trylock releases lockForPotentialLockers right away,
+      incremented to 1 (one).
+
+      After construction, we only access this field via JNI.
+  */     
+  Object lockForPotentialLockers;
+
+  GThreadMutex() 
+  {
+    potentialLockers = 0;
+    lockForPotentialLockers = new Object();
+  }
+}
+// Local Variables:
+// c-file-style: "gnu"
+// End:
--- /dev/null   2003-01-30 10:24:37.000000000 +0000
+++ gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java        2004-05-26 
17:26:27.000000000 +0000
@@ -0,0 +1,309 @@
+/* GThreadNativeMethodRunner.java -- Implements pthread_create(), under
+   glib's gthread abstraction, for use with GNU Classpath's
+   --portable-native-sync option. 
+   This is used by gthread-jni.c
+   
+   Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.java.awt.peer.gtk;
+import java.lang.ref.WeakReference;
+import java.util.Set;
+import java.util.Collections;
+import java.util.HashSet;
+
+/** Implements pthread_create(), under glib's gthread abstraction, for use
+    with GNU Classpath's --portable-native-sync option.  This is used in
+    gthread-jni.c
+
+    Also implements a registry for threads, mapping Thread objects to small
+    integers.  We use weak references for threads that aren't joinable, so
+    that they will be garbage collected.
+
+    There are a number of possible alternative implementations.  To see one of
+    them, search for the (commented-out) variable myThreadID.
+
+    The rest of this comment consists of an answer to a question that was
+    raised on the commit-classpath mailing list:
+
+    Mark Wielaard wrote:
+
+    > Can't we assume that jobject and gpointer are both (void *) so we don't
+    > need the int <-> Thread (global jobject ref) mapping?
+    > Maybe there are platforms where jobject and gpointer aren't the same,
+    > but I guess that is pretty unlikely.
+
+
+    I agree with you on the pointer size issues.  A gpointer is a void *, so
+    it's certainly guaranteed to be at least as large as any other
+    pointer. And a jobject is implicitly an opaque pointer (in Jikes RVM, we
+    use small integers, but we coerce them into the representation of a
+    pointer).
+
+    The int <==> Thread mapping addresses a different issue.  I realize that I
+    did not document this properly (two and a half lines in thread_create),
+    and the point is subtle (at least to me; took me a while to figure out).
+
+    The int => Thread mapping always returns jobjects that are local
+    references, not global ones.  This is because Thread objects need to be
+    able to go away and be garbage collected after the thread they refer to
+    has died.
+
+    If we keep a global object reference to a thread, then when do we delete
+    that global object reference?  We have an answer in the case of GThread
+    objects that were explicitly created with the joinable attribute.  It is
+    safe for us to maintain a global reference to any joinable thread, since
+    the joinable thread must sticks around (even if only in a zombie state)
+    until it's explicitly joined via a g_thread_join() call.  The global ref
+    could be cleaned up at that point too.
+
+    However, in the case of GThreads that were created non-joinable by
+    g_thread_create(), and in the case of Java threads that were created
+    within pure Java code (not via g_thread_create()), we don't want them to
+    linger around forever, and there is no way to tell when the last reference
+    to such threads needs to expire.  In the case of this application -- AWT
+    with GTK peers -- it would probably be safe anyway, since there are not
+    very many threads we create, but I was going for correctness even in the
+    case of long-running programs that might set up and tear down AWT
+    interfaces many times.
+
+    So, I duplicated the POSIX thread-ID semantics.  The thread ID of a
+    non-joinable thread remains valid as long as that thread is still alive.
+    Once that thread dies, the old thread ID may be reused at any moment.  And
+    that's why the array indexed by thread ID numbers is an array of weak
+    references.
+
+    That's also why the int => Thread jobject mapping function always returns
+    local references, since global references would lock the Thread in memory
+    forever.
+
+    I would dearly love there to be a cleaner solution.  I dislike the
+    repeated dips from C code into Java that are necessary to look up thread
+    ID numbers.  If anyone can think of one, I'm all ears.
+*/
+
+class GThreadNativeMethodRunner 
+  extends Thread 
+{
+  /** The C function pointer that was passed to g_thread_create().
+      Specifically, this the numeric address of an object of 
+      C type "void *(*funcPtr)(void *funcArg)".   
+  */
+  private final long funcPtr;
+
+  /** The argument for the function "funcPtr(funcArg)". */
+  private final long funcArg;
+  
+//   /** A numeric code.  We could use this to help along the system by 
deleting
+//       weak references as soon as we will no longer need them.  We don't
+//       actually use this at this time, though, since we would need to store
+//       additional information (whether a thread was joinable or not) in order
+//       to make this work.   And there is not a clear efficiency gain. */  
+//   private final int myThreadID;
+  
+  GThreadNativeMethodRunner(long funcPtr, long funcArg, boolean joinable) 
+  {
+    this.funcPtr = funcPtr;
+    this.funcArg = funcArg;
+//     this.myThreadID = registerThread(self); // register self numerically
+    if (joinable)
+      registerSelfJoinable();
+  }
+
+  public void run() 
+  {
+    nativeRun(funcPtr, funcArg);
+  }
+
+  private native void nativeRun(long funcPtr, long funcArg);
+
+  /** THREADS is an array of threads, indexed by thread ID codes.  Not sure
+      whether this is the "best" approach but it does make it O(1) to look up a
+      thread by its ID. 
+
+      Zero is a valid thread ID code.  Any negative number is invalid.
+
+      Possible future fixes (TODO?)
+
+     - The THREADS array will only grow. probably not a problem.
+        But we could keep count when nulling entries and shrink when we have
+        lots of nulls at the end. Probably not worth it. --mjw
+
+     - Could make this a set of Object; see the comment on "joinable" below.
+
+     The initial size of 17 is just a starting point.  Any number will do,
+     including zero.
+  */ 
+  private static WeakReference[] threads = new WeakReference[17]; 
+
+  /**  Used by threadToThreadID, below.  Returns the registration number of
+       the newly-registered thread.  
+  */
+  private static synchronized int registerThread(Thread t) 
+  {
+    int i;
+
+    for (i = 0; i < threads.length; ++i) 
+      {
+       WeakReference ref = threads[i];
+       if (ref == null)
+         break;                  // found an empty spot.
+      }
+
+    if (i == threads.length) 
+      {
+       /* expand the array */
+       WeakReference[] bigger = new WeakReference[threads.length * 2];
+        System.arraycopy(threads, 0, bigger, 0, threads.length);
+       threads = bigger;
+      }
+
+    threads[i] = new WeakReference(t);
+
+    return i;
+  }
+  
+  /**  Look up the Thread ID # for a Thread.  Assign a Thread ID # if none
+       exists.  This is a general routine for handling all threads, including
+       the VM's main thread, if appropriate.
+
+
+       Runs in O(n/2) time.
+
+       We can't just issue a threadID upon thread creation.  If we were to do
+       that, not all threads would have a threadID, because not all threads
+       are launched by GThreadNativeMethodRunner.
+  */ 
+  static synchronized int threadToThreadID(Thread t) 
+  {
+    for (int i = 0; i < threads.length; ++i ) 
+      {
+       if (threads[i] == null)
+         continue;
+       Thread referent = (Thread) threads[i].get();
+       if (referent == null) 
+         {
+           threads[i] = null;      // Purge the dead WeakReference.
+           continue;
+         }
+       if (referent.equals(t))
+         return i;
+      } // for()
+
+    /* No match found. */
+    return registerThread(t);
+  }
+
+  /** @param threadID Must be a non-negative integer.
+
+      Used to return null if the thread number was out of range or if
+      the thread was unregistered.   Now we throw an exception.
+
+      Possible Alternative Interface:  We could go back to returning null in
+           some sort of check-free mode, so code that calls this function must
+           be prepared to get null. 
+  */ 
+  static Thread threadIDToThread(int threadID) 
+    throws IllegalArgumentException
+  {
+    if (threadID < 0)
+      throw new IllegalArgumentException("Received a negative threadID, " 
+                                        + threadID); 
+    if (threadID >= threads.length)
+      throw new IllegalArgumentException("Received a threadID (" + threadID 
+                                        + ") higher than was" 
+                                        + " ever issued"); 
+    
+    /* Note: if the user is using a stale reference, things will just
+       break.    We might end up getting a different thread than the one
+       expected. 
+       
+       TODO: Add an error-checking mode where the user's problems with threads
+          are announced.  For instance, if the user asks for the thread
+          associated with a threadID that was never issued, we could print a
+          warning or even abort.
+       
+       TODO: Consider optionally disabling all of the error-checking we
+          already have; it probably slows down the implementation.  We could
+          just return NULL.  This is just the reverse of the above TODO item.
+    */ 
+
+    WeakReference threadRef = threads[threadID];
+
+    if (threadRef == null)
+      throw new IllegalArgumentException("Asked to look up a stale or unissued"
+                                        + "threadID (" + threadID + ")" );
+    
+      
+    Thread referent = (Thread) threadRef.get();
+    if (referent == null)
+      throw new IllegalArgumentException ("Asked to look up a stale threadID ("
+                                         + threadID + ")");
+    return referent;
+  }
+  
+  /** Joinable threads need a hard reference, so that they won't go away when
+      they die.  That is because their thread IDs need to stay valid until the
+      thread is joined via thread_join(threadID).  Joinable threads have to be
+      explicitly joined before they are allowed to go away completely.
+
+      Possible Alternative Implementation: Joinable set.  Hmmmm.  When calling
+          getThreadIDFromThread() you know whether or not the thread is
+          joinable.  So just store the Thread itself in the threads array?
+          Make that array an Object array and check with instanceof.  This
+          looks cleaner and more robust to me and it saves a native -> Java
+          call. But instanceof might be expensive.  --mjw
+  */
+  private static final Set joinable = 
+       Collections.synchronizedSet(new HashSet()); 
+  
+  /** Only called from the constructor. */
+  private void registerSelfJoinable() 
+  {
+    joinable.add(this);
+  }
+  
+  /** This method is only called from JNI, and only after we have succeeded in
+      a thread_join() operation.  */
+  static void deRegisterJoinable(Thread thread) 
+  {
+    joinable.remove(thread);
+  }
+}
+
+// Local Variables:
+// c-file-style: "gnu"
+// End:
--- /dev/null   2003-01-30 10:24:37.000000000 +0000
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.c       
2004-05-08 06:12:47.000000000 +0000
@@ -0,0 +1,68 @@
+/* Native implementation of functions in GThreadNativeMethodRunner
+   Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+#include "gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h"
+#include "gthread-jni.h"
+
+/*
+ * Class:     GThreadNativeMethodRunner
+ * Method:    nativeRun
+ * Signature: (J)V
+ *
+ * Purpose: Run the C function whose function pointer is
+ * 
+ */
+JNIEXPORT void JNICALL 
+Java_gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_nativeRun(JNIEnv 
*gdk_env, jobject lcl_obj, 
+                                        jlong funcAddr, jlong funcArg)
+{
+  /* Convert the function's address back into a pointer to a C function. */
+  void *(*funcPtr)(void *) = (void *(*)(void *)) funcAddr;
+  
+  /* We do not need to worry about the return value from funcPtr(); it's
+     just thrown away.  That is part of the g_threads spec, so no reason
+     to worry about returning it.  */
+  (void) funcPtr((void *) funcArg);
+  /* Fall off the end and terminate the thread of control. */
+}
+
+/* Local Variables: */
+/* c-file-style: "gnu" */
+/* End: */
+
+
--- /dev/null   2003-01-30 10:24:37.000000000 +0000
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h       
2004-05-12 07:02:30.000000000 +0000
@@ -0,0 +1,27 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class GThreadNativeMethodRunner */
+
+#ifndef _Included_GThreadNativeMethodRunner
+#define _Included_GThreadNativeMethodRunner
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef GThreadNativeMethodRunner_MIN_PRIORITY
+#define GThreadNativeMethodRunner_MIN_PRIORITY 1L
+#undef GThreadNativeMethodRunner_NORM_PRIORITY
+#define GThreadNativeMethodRunner_NORM_PRIORITY 5L
+#undef GThreadNativeMethodRunner_MAX_PRIORITY
+#define GThreadNativeMethodRunner_MAX_PRIORITY 10L
+/*
+ * Class:     GThreadNativeMethodRunner
+ * Method:    nativeRun
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL 
Java_gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_nativeRun (JNIEnv *, 
jobject, jlong funcAddr, jlong funcArg);
+    
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c
===================================================================
RCS file: 
/cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c,v
retrieving revision 1.17
diff -I*.class -u -r1.17 gnu_java_awt_peer_gtk_GtkMainThread.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c   29 Mar 2004 
07:07:27 -0000      1.17
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c   26 May 2004 
18:34:55 -0000
@@ -63,10 +63,6 @@
 
 JNIEnv *gdk_env;
 
-#ifdef PORTABLE_NATIVE_SYNC
-JavaVM *gdk_vm;
-#endif
-
 GtkWindowGroup *global_gtk_window_group;
 
 /*
@@ -87,18 +83,25 @@
   NSA_INIT (env, clazz);
 
   /* GTK requires a program's argc and argv variables, and requires that they
-     be valid.  */
-
-  argv = (char **) malloc (sizeof (char *) * 2);
-  argv[0] = "";
+     be valid.   Set it up. */
+  argv = (char **) g_malloc (sizeof (char *) * 2);
+  argv[0] = (char *) g_malloc(1);
+#if 1
+  strcpy(argv[0], "");
+#else  /* The following is a more efficient alternative, but less intuitively
+       * expresses what we are trying to do.   This code is only run once, so
+       * I'm going for intuitive. */
+  argv[0][0] = '\0';
+#endif
   argv[1] = NULL;
 
   /* until we have JDK 1.2 JNI, assume we have a VM with threads that 
      match what GLIB was compiled for */
 #ifdef PORTABLE_NATIVE_SYNC
-  (*env)->GetJavaVM( env, &gdk_vm );
-  g_thread_init ( &g_thread_jni_functions );
-  printf("called gthread init\n");
+  (*env)->GetJavaVM( env, &the_vm );
+  g_thread_init ( &portable_native_sync_jni_functions );
+  /* Debugging progress message; uncomment if needed: */
+  /*   printf("called gthread init\n"); */
 #else
   g_thread_init ( NULL );
 #endif
@@ -121,16 +124,15 @@
 
   if ((homedir = getenv ("HOME")))
     {
-      rcpath = (char *) malloc (strlen (homedir) + strlen (RC_FILE) + 2);
+      rcpath = (char *) g_malloc (strlen (homedir) + strlen (RC_FILE) + 2);
       sprintf (rcpath, "%s/%s", homedir, RC_FILE);
     }
   
   gtk_rc_parse ((rcpath) ? rcpath : RC_FILE);
 
-  if (rcpath)
-    free (rcpath);
-
-  free (argv);
+  g_free (rcpath);
+  g_free (argv[0]);
+  g_free (argv);
 
   /* setup cached IDs for posting GTK events to Java */
 /*    gtkgenericpeer = (*env)->FindClass (env,  */
Index: native/jni/gtk-peer/gthread-jni.c
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/gtk-peer/gthread-jni.c,v
retrieving revision 1.11
diff -I*.class -u -r1.11 gthread-jni.c
--- native/jni/gtk-peer/gthread-jni.c   30 Apr 2004 11:05:17 -0000      1.11
+++ native/jni/gtk-peer/gthread-jni.c   26 May 2004 18:34:55 -0000
@@ -40,142 +40,1112 @@
 /************************************************************************/
 
 /*
- * Julian Dolby (address@hidden)
- * February 7, 2003
+ * @author Julian Dolby (address@hidden)
+ * @date February 7, 2003
+ *
+ * @author Steven Augart 
+ * <steve+classpath at augart dot com>, <augart at watson dot ibm dot com>
+ * @date April 30, 2004 -- May 10 2004: Support new functions for Glib 2.0,
+ * fix cond_wait to free and re-acquire the mutex,
+ * replaced trylock stub implementation with a full one.
  *
  *  This code implements the GThreadFunctions interface for GLIB using 
  * Java threading primitives.  All of the locking and conditional variable
  * functionality required by GThreadFunctions is implemented using the
  * monitor and wait/notify functionality of Java objects.  The thread-
- * local fucntionality uses the java.lang.ThreadLocal class. 
+ * local functionality uses the java.lang.ThreadLocal class. 
  *
- *  This code is designed to be portable in that it makes no assumptions
+ *  This code should be portable; I believe it makes no assumptions
  * about the underlying VM beyond that it implements the JNI functionality
  * that this code uses.
  *
- *  The one piece that does not really work is trylock for mutexes.  The
- * Java locking model does not include such functionality, and I do not
- * see how to implement it without knowing something about how the VM
- * implements locking.  
+ *  Currently, use of this code is governed by the configuration option
+ * --enable-portable-native-sync.  We will soon add a VM hook so the VM can
+ * select which threading model it wants to use at run time; at that point,
+ * the configuration option will go away.
  *
  * NOTES:
  *
- *  I have tested it only on JikesRVM---the CVS head as of early February
- * 2003.
+ *  We have tested this only on Jikes RVM---the CVS head as of
+ *   early February 2003 and the CVS head as of May, 2004.
  *
- *  Currently, use of this code is governed by the configuration option
- * --enable-portable-native-sync
  *
+ * MINOR NITS:
+ *
+ *  - This file is only needed for --portable-native-sync.  We
+ *    conditionally compile it against the PORTABLE_NATIVE_SYNC macro.  Should
+ *    we manipulate Makefile.am instead?  Or should we go ahead and compile
+ *    it so that something which breaks this code will not be used?
+ *
+ *  - Using a jboolean in the arglist to "throw()" and "rethrow()"
+ *    triggers many warnings from GCC's -Wconversion operation, because that
+ *    is not the same as the conversion (upcast to an int) that would occur in
+ *    the absence of a prototype.
+ *    
+ *    It would be very slightly more efficient to just pass the jboolean, but
+ *    is not worth the clutter of messages.  The right solution would be to
+ *    turn off the -Wconversion warning for just this file, *except* that
+ *    -Wconversion also warns you against constructs such as:
+ *        unsigned u = -1;
+ *    and that is a useful warning.  So I went from a "jboolean" to a
+ *    "gboolean"  (-Wconversion is not enabled by default for GNU Classpath,
+ *    but it is in my own CFLAGS, which, for gcc 3.3.3, read: -pipe -ggdb3 -W
+ *    -Wall -Wbad-function-cast -Wcast-align -Wpointer-arith -Wcast-qual
+ *    -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
+ *    -fkeep-static-consts -fkeep-inline-functions -Wundef -Wwrite-strings
+ *    -Wno-aggregate-return -Wmissing-noreturn -Wnested-externs -Wtrigraphs
+ *    -Wconversion -Wsign-compare -Wno-float-equal -Wmissing-format-attribute
+ *    -Wno-unreachable-code -Wdisabled-optimization )
  */
 
+#include <config.h>
+
+/************************************************************************/
+/* Configuration                                                       */
+/************************************************************************/
+
+/** Tracing and Reporting  **/
+#define TRACE_API_CALLS            0   /* announce entry and exit into each 
method,
+                                  by printing to stderr. */
+
+#define TRACE_MONITORS      0  /* Every enterMonitor() and exitMonitor() goes
+                                  to stderr. */
+
+/** Trouble handling.  There is a discussion below of this.  **/ 
+#define EXPLAIN_TROUBLE            1   /* Describe any unexpected trouble that
+                                  happens.  This is a superset
+                                  of EXPLAIN_BROKEN, and if set trumps an
+                                  unset EXPLAIN_BROKEN.  It is not a strict
+                                  superset, since at the moment there is no
+                                  TROUBLE that is not also BROKEN.   
+
+                                  Use criticalMsg() to describe the problem.
+                                */
+
+#define EXPLAIN_BROKEN     1   /* Describe trouble that is serious enough to
+                                  be BROKEN.  Right now all trouble is at
+                                  least BROKEN.  EXPLAIN_TROUBLE, if set,
+                                  overrides this. */
+
+/* There is no EXPLAIN_BADLY_BROKEN definition.  We always explain
+   BADLY_BROKEN trouble, since there is no other way to report it.  */
+
+
+/** Error Handling  **/
+#define DIE_IF_BROKEN      1   /* Dies if serious trouble happens.  There is
+                                  really no non-serious trouble, except
+                                  possibly problems that arise during
+                                  pthread_create, which are reported by a
+                                  GError.
+
+                                  If you do not set DIE_IF_BROKEN, then
+                                  trouble will raise a Java RuntimeException.
+                                  We probably do want to die right away,
+                                  since anything that's BROKEN really
+                                  indicates a programming error or a
+                                  system-wide error, and that's what the glib
+                                  documentation says you should do in case of
+                                  that kind of error in a glib-style
+                                  function.  But it does work to turn this
+                                  off.  */
+
+#if  DIE_IF_BROKEN
+#define DIE_IF_BADLY_BROKEN 1  /* DIE_IF_BROKEN implies DIE_IF_BADLY_BROKEN */
+#else
+#define DIE_IF_BADLY_BROKEN 1  /* Die if the system is badly broken --
+                                  that is, if we have further trouble while
+                                  attempting to throw an exception
+                                  upwards, or if we are unable to generate
+                                  one of the classes we'll need in order to
+                                  throw wrapped exceptions upward.
+
+                                  If unset, we will print a warning message,
+                                  and limp along anyway.  Not that the system
+                                  is likely to work.  */
+#endif
+
+/** Performance tuning parameters **/
+
+#define ENABLE_EXPENSIVE_ASSERTIONS 0  /* Enable expensive assertions? */
+
+#define DELETE_LOCAL_REFS   1  /* Whether to delete local references.   
+
+                                  JNI only guarantees that there wil be 16
+                                  available.  (Jikes RVM provides an number
+                                  only limited by VM memory.)
+
+                                  Jikes RVM will probably perform faster if
+                                  this is turned off, but other VMs may need
+                                  this to be turned on in order to perform at
+                                  all, or might need it if things change.
+
+                                  Remember, we don't know how many of those
+                                  local refs might have already been used up
+                                  by higher layers of JNI code that end up
+                                  calling g_thread_self(),
+                                  g_thread_set_private(), and so on.
+
+                                  We set this to 1 for GNU Classpath, since
+                                  one of our principles is "always go for the
+                                  most robust implementation" */
+
+#define  HAVE_JNI_VERSION_1_2   0 /* Assume we don't.  We could
+                                    dynamically check for this.  We will
+                                    assume JNI 1.2 in later versions of
+                                    Classpath.  
+
+                                    TODO This code hasn't been tested yet.
+                                    And really hasn't been implemented yet.
+                                    */ 
 
 /************************************************************************/
 /* Global data                                                         */
 /************************************************************************/
 
+#include <stdint.h>            /* provides intptr_t */
+#include <stdarg.h>            /* va_list */
 #include "gthread-jni.h"
+#include <assert.h>            /* assert() */
+
+/* For Java thread priority constants. */
+#include "gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h"
+
+/*  The VM handle.  This is set in
+    Java_gnu_java_awt_peer_gtk_GtkMainThread_gtkInit */
+JavaVM *the_vm;
+
+/* Forward Declarations for Functions  */
+static int threadObj_set_priority (JNIEnv * env, jobject threadObj,
+                                  GThreadPriority gpriority);
+static void fatalMsg (const char fmt[], ...)
+     __attribute__ ((format (printf, 1, 2)))
+     __attribute__ ((noreturn));
 
-/*  The VM handle.  This is set in GtkToolkitMain.gtkInit */
-JavaVM *gdk_vm;
+static void criticalMsg (const char fmt[], ...)
+     __attribute__ ((format (printf, 1, 2)));
 
+static void tracing (const char fmt[], ...)
+     __attribute__ ((format (printf, 1, 2)));
+
+static jint javaPriorityLevel (GThreadPriority priority)
+     __attribute__ ((const));
 
 /************************************************************************/
-/* Utilities to reflect exceptions back to the VM                      */
+/* Trouble-handling, including utilities to reflect exceptions         */
+/* back to the VM.  Also some status reporting.                                
*/
 /************************************************************************/
 
-/*  This function checks for a pending exception, and rethrows it with
+/* How are we going to handle problems?
+
+   There are several approaches:
+
+   1)  Report them with the GError mechanism (this only works for 
+       thread_create(), and that only for something that maps onto a POSIX
+       EAGAIN.  Nothing maps onto EAGAIN.  So this idea is a non-starter.)
+
+   2)  Reflect the exception back to the VM, wrapped in a RuntimeException.
+       This will fail sometimes -- but you can enable DIE_IF_BADLY_BROKEN to
+       croak in such a situation.
+
+   3)  Abort execution.  Enable DIE_IF_BROKEN and/or DIE_IF_BADLY_BROKEN to
+       make this the default for BROKEN or BADLY_BROKEN trouble.  This is
+       what the glib functions do if it's not appropriate to report via
+       GError.   
+
+   4) Display messages to stderr.  We always do this for BADLY_BROKEN trouble.
+
+   There are some complications.
+
+   When I attempted to report a problem in g_thread_self() using g_critical (a
+   macro around g_log(), I found that g_log in turn looks for thread-private
+   data and calls g_thread_self() again.
+
+   We got a segfault -- exactly the sort of error this attempted to avoid.  
+   So I don't use the g_critical() and g_error() functions any more from this
+   code.   Nor do I use g_assert(); I use old-fashioned assert instead.
+
+   
+
+*/
+
+
+#define WHERE __FILE__ ":" G_STRINGIFY(__LINE__) ": "
+
+/* This is portable to older compilers that lack variable-argument macros.
+   This used to be just g_critical(), but then we ran into the error reporting
+   problem discussed above.
+*/
+static void
+fatalMsg (const char fmt[], ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+  fputs ("\nAborting execution\n", stderr);
+  abort ();
+}
+
+
+static void
+criticalMsg (const char fmt[], ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+  putc ('\n', stderr);
+}
+
+/* Unlike the other two, this one does not append a newline.  This is only
+   used if one of the TRACE_ macros is defined.  */
+static void
+tracing (const char fmt[], ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  vfprintf (stderr, fmt, ap);
+  va_end (ap);
+}
+
+#define assert_not_reached()                                           \
+  do                                                                   \
+    {                                                                  \
+      fputs(WHERE "You should never get here.  Aborting execution.\n",         
\
+           stderr);                                                    \
+      abort();                                                         \
+    }                                                                  \
+  while(0)
+
+
+#if DIE_IF_BADLY_BROKEN
+#define BADLY_BROKEN fatalMsg
+#else
+#define BADLY_BROKEN criticalMsg
+/* So, the user may still attempt to recover, even though we do not advise
+   this. */
+#endif
+
+/* I find it so depressing to have to use C without varargs macros. */
+#define BADLY_BROKEN_MSG WHERE "Something fundamental"         \
+       " to GNU Classpath's AWT JNI broke while we were trying to pass up a 
Java error message"
+
+#define BADLY_BROKEN0()                                \
+    BADLY_BROKEN(BADLY_BROKEN_MSG);
+#define            BADLY_BROKEN1(msg)                  \
+    BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg)
+#define            BADLY_BROKEN2(msg, arg)                     \
+    BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg)
+#define            BADLY_BROKEN3(msg, arg, arg2)               \
+    BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg1, arg2)
+#define            BADLY_BROKEN4(msg, arg, arg2, arg3)                 \
+    BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg1, arg2, arg3)
+
+#define DELETE_LOCAL_REF(env, ref)             \
+  do                                           \
+    {                                          \
+      if ( DELETE_LOCAL_REFS )                 \
+       {                                       \
+         (*env)->DeleteLocalRef (env, ref);    \
+         (ref) = NULL;                         \
+       }                                       \
+    }                                          \
+  while(0)
+
+/* Cached info for Exception-wrapping */
+
+jclass runtimeException_class; /* java.lang.RuntimeException */
+jmethodID runtimeException_ctor; /* constructor for it */
+
+
+/* Throw a new RuntimeException.  It may wrap around an existing exception.
+   1 if we did rethrow, -1 if we had trouble while rethrowing.
+   isBroken is always true in this case. */
+static int
+throw (JNIEnv * env, jthrowable cause, const char *message,
+       gboolean isBroken, const char *file, int line)
+{
+  jstring jmessage;
+  gboolean describedException = FALSE; /* Did we already describe the
+                                          exception to stderr or the
+                                          equivalent?   */
+  jthrowable wrapper;
+
+  /* allocate local message in Java */
+  const char fmt[] = "In AWT JNI, %s (at %s:%d)";
+  size_t len = strlen (message) + strlen (file) + sizeof fmt + 25;
+  char *buf;
+
+  if (EXPLAIN_TROUBLE || (isBroken && EXPLAIN_BROKEN))
+    {
+      criticalMsg ("%s:%d: AWT JNI failure%s: %s\n", file, line,
+                  isBroken ? " (BROKEN)" : "", message);
+      if (cause)
+       {
+         jthrowable currentException = (*env)->ExceptionOccurred (env);
+
+         if (cause == currentException)
+           {
+             criticalMsg ("Description follows to System.err:");
+             (*env)->ExceptionDescribe (env);
+             /* ExceptionDescribe has the side-effect of clearing the pending
+                exception; relaunch it.  */
+             describedException = TRUE;
+
+             if ((*env)->Throw (env, cause))
+               {
+                 BADLY_BROKEN1
+                   ("Relaunching an exception with Throw failed.");
+                 return -1;
+               }
+           }
+         else
+           {
+             DELETE_LOCAL_REF (env, currentException);
+             criticalMsg (WHERE
+                          "currentException != cause; something else happened"
+                          " while handling an exception.");
+           }
+       }
+    }                          /* if (EXPLAIN_TROUBLE) */
+
+  if (isBroken && DIE_IF_BROKEN)
+    fatalMsg ("%s:%d: Aborting execution; BROKEN: %s\n", file, line, message);
+
+  if ((buf = malloc (len)))
+    {
+      bzero (buf, len);
+      snprintf (buf, len, fmt, message, file, line);
+      jmessage = (*env)->NewStringUTF (env, buf);
+      free (buf);
+    }
+  else
+    {
+      jmessage = NULL;
+    }
+
+  /* Create the RuntimeException wrapper object and throw it.  It is OK for
+     CAUSE to be NULL. */
+  wrapper = (jthrowable) (*env)->NewObject
+    (env, runtimeException_class, runtimeException_ctor, jmessage, cause);
+  DELETE_LOCAL_REF (env, jmessage);
+
+  if (!wrapper)
+    {
+      /* I think this should only happen:
+         - if there are bugs in my JNI code, or
+         - if the VM is broken, or 
+         - if we run out of memory. 
+       */
+      if (EXPLAIN_TROUBLE)
+       {
+         criticalMsg (WHERE "GNU Classpath: JNI NewObject() could not create"
+                      " a new java.lang.RuntimeException.");
+         criticalMsg ("We were trying to warn about the following"
+                      " previous failure:");
+         criticalMsg ("%s:%d: %s", file, line, message);
+         criticalMsg ("The latest (NewObject()) exception's description"
+                      " follows, to System.err:");
+         (*env)->ExceptionDescribe (env);
+       }
+      BADLY_BROKEN1 ("Failure of JNI NewObject()"
+                    " to make a java.lang.RuntimeException");
+      return -1;
+    }
+
+
+  /* throw it */
+  if ((*env)->Throw (env, wrapper))
+    {
+      /* Throw() should just never fail, unless we're in such severe trouble
+         that we might as well die. */
+      BADLY_BROKEN1
+       ("GNU Classpath: Failure of JNI Throw to report an Exception");
+      return -1;
+    }
+
+  DELETE_LOCAL_REF (env, wrapper);
+  return 1;
+}
+
+
+
+/* Rethrow an exception we received, wrapping it with a RuntimeException.  1
+   if we did rethrow, -1 if we had trouble while rethrowing.
+   CAUSE should be identical to the most recent exception that happened, so
+   that ExceptionDescribe will work.  (Otherwise nix.) */
+static int
+rethrow (JNIEnv * env, jthrowable cause, const char *message,
+        gboolean isBroken, const char *file, int line)
+{
+  assert (cause);
+  return throw (env, cause, message, isBroken, file, line);
+}
+
+
+/* This function checks for a pending exception, and rethrows it with
  * a wrapper RuntimeException to deal with possible type problems (in
  * case some calling piece of code does not expect the exception being
  * thrown) and to include the given extra message.
+ *
+ * Returns 0 if no problems found (so no exception thrown), 1 if we rethrew an
+ * exception.   Returns -1 on failure. 
  */
-static void maybe_rethrow(JNIEnv *gdk_env, char *message, char *file, int 
line) {
-  jthrowable cause;
+static int
+maybe_rethrow (JNIEnv * env, const char *message, gboolean isBroken,
+              const char *file, int line)
+{
+  jthrowable cause = (*env)->ExceptionOccurred (env);
+  int ret = 0;
+
+  /* rethrow if an exception happened */
+  if (cause)
+    {
+      ret = rethrow (env, cause, message, isBroken, file, line);
+      DELETE_LOCAL_REF (env, cause);
+    }
+
+  return 0;
+}
+
+/* MAYBE_TROUBLE() is used to include a source location in the exception
+   message. Once we have run maybe_rethrow, if there WAS trouble, 
+   return TRUE, else FALSE.   
+
+   MAYBE_TROUBLE() is actually never used; all problems that throw exceptions
+   are BROKEN, at least.  Nothing is recoverable :(.  See the discussion of
+   possible errors at thread_create_jni_impl().  */
+#define MAYBE_TROUBLE(_env, _message)                          \
+       maybe_rethrow(_env, _message, FALSE, __FILE__, __LINE__)
+
+/* MAYBE_TROUBLE(), but something would be BROKEN if it were true. */
+#define MAYBE_BROKEN(_env, _message)                           \
+       maybe_rethrow(_env, _message, TRUE, __FILE__, __LINE__)
+
+/* Like MAYBE_TROUBLE(), TROUBLE() is never used. */
+#define TROUBLE(_env, _message)                                                
\
+       rethrow(_env, (*env)->ExceptionOccurred (env), _message, FALSE, \
+               __FILE__, __LINE__)
+
+#define BROKEN(_env, _message)                                         \
+       rethrow (_env, (*env)->ExceptionOccurred (env), _message, TRUE, \
+                __FILE__, __LINE__)
+
+/* Like MAYBE_TROUBLE(), NEW_TROUBLE() is never used. */
+#define NEW_TROUBLE(_env, _message)                                    \
+       throw (_env, NULL,  _message, FALSE, __FILE__, __LINE__)
+
+#define NEW_BROKEN(_env, _message)                             \
+       throw (_env, NULL, _message, TRUE, __FILE__, __LINE__)
+
+/* Like MAYBE_TROUBLE(), RETHROW_CAUSE() is never used. */
+#define RETHROW_CAUSE(_env, _cause, _message)                          \
+       rethrow (_env, _cause, _message, FALSE, __FILE__, __LINE__)
+
+#define BROKEN_CAUSE(_env, _cause, _message)                           \
+       rethrow (_env, _cause, _message, TRUE, __FILE__, __LINE__)
+
+/* Macros to handle the possibility that someone might have called one of the
+   GThreadFunctions API functions with a Java exception pending.  It is
+   generally discouraged to continue to use JNI after a Java exception has
+   been raised.  Sun's JNI book advises that one trap JNI errors immediately
+   and not continue with an exception pending.
+
+   These are #if'd out for these reasons:
+
+   1) They do not work in the C '89 subset that Classpath is currently 
+      (2004 May 10) sticking to; HIDE_OLD_TROUBLE() includes a declaration
+      that should be in scope for the rest of the function, so it needs a
+      language version that lets you mix declarations and statements.  (This
+      could be worked around if it were important.)
+
+   2) They chew up more time and resources.  
+
+   3) There does not ever seem to be old trouble -- the assertion in
+      HIDE_OLD_TROUBLE never goes off. 
+
+   You will want to re-enable them if this code needs to be used in a context
+   where old exceptions might be pending when the GThread functions are
+   called.
+
+   The implementations in this file are responsible for skipping around calls
+   to SHOW_OLD_TROUBLE() if they've raised exceptions during the call.  So, if
+   we reach SHOW_OLD_TROUBLE, we are guaranteed that there are no exceptions
+   pending. */
+#if 1
+#define HIDE_OLD_TROUBLE(env)                          \
+    assert ( NULL == (*env)->ExceptionOccurred (env) )
+
+#define SHOW_OLD_TROUBLE()     \
+    assert ( NULL == (*env)->ExceptionOccurred (env) )
+#else  /* 0 */
+#define HIDE_OLD_TROUBLE(env)                                  \
+   jthrowable savedTrouble = (*env)->ExceptionOccurred (env);  \
+   (*env)->ExceptionClear (env);
+
+#define SHOW_OLD_TROUBLE() do                                  \
+{                                                              \
+  assert ( NULL == (*env)->ExceptionOccurred (env) )           \
+  if (savedTrouble)                                            \
+    {                                                          \
+      if ((*env)->Throw (env, savedTrouble))                   \
+         BADLY_BROKEN ("ReThrowing the savedTrouble failed");  \
+    }                                                          \
+  DELETE_LOCAL_REF (env, savedTrouble);                                \
+} while(0)
+
+#endif /* 0 */
+
+/* Set up the cache of jclass and jmethodID primitives we need
+   in order to throw new exceptions and rethrow exceptions.  We do this
+   independently of the other caching.  We need to have this cache set up
+   first, so that we can then report errors properly. 
+
+   If any errors while setting up the error cache, the world is BADLY_BROKEN.
+
+   May be called more than once.
+
+   Returns -1 if the cache was not initialized properly, 1 if it was.  
+*/
+static int
+setup_exception_cache (JNIEnv * env)
+{
+  static int exception_cache_initialized = 0;  /* -1 for trouble, 1 for proper
+                                                  init.  */
+
+  jclass lcl_class;            /* a class used for local refs */
+
+  if (exception_cache_initialized)
+    return exception_cache_initialized;
+  lcl_class = (*env)->FindClass (env, "java/lang/RuntimeException");
+  if ( ! lcl_class )
+    {
+      BADLY_BROKEN1 ("Broken Class library or VM?"
+                    "  Couldn't find java/lang/RuntimeException");
+      return exception_cache_initialized = -1;
+    }
+  /* Pin it down. */
+  runtimeException_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+  DELETE_LOCAL_REF (env, lcl_class);
+  if (!runtimeException_class)
+    {
+      BADLY_BROKEN1 ("Serious trouble: could not turn"
+                    " java.lang.RuntimeException into a global reference");
+      return exception_cache_initialized = -1;
+    }
+
+  runtimeException_ctor = 
+    (*env)->GetMethodID (env, runtimeException_class, "<init>",
+                          "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+  if ( ! runtimeException_ctor )
+    {
+      BADLY_BROKEN1 ("Serious trouble: classpath couldn't find a"
+                    " two-arg constructor for java/lang/RuntimeException");
+      return exception_cache_initialized = -1;
+    }
+
+  return exception_cache_initialized = 1;
+}
+
+
+/**********************************************************/
+/***** The main cache *************************************/
+/**********************************************************/
+
+/** This is a cache of all classes, methods, and field IDs that we use during
+   the run.  We maintain a permanent global reference to each of the classes
+   we cache, since otherwise the (local) jclass that refers to that class
+   would go out of scope and possibly be reused in further calls.
+
+   The permanent global reference also achieves the secondary goal of
+   protecting the validity of the methods and field IDs in case the classes
+   were otherwise unloaded and then later loaded again.  Obviously, this will
+   never happen to classes such as java.lang.Thread and java.lang.Object, but
+   the primary reason for maintaining permanent global refs is sitll valid.
+
+   The code in jnilink.c has a similar objective.  TODO: Consider using that
+   code instead.
+
+   --Steven Augart
+*/
+
+/* All of these are cached classes and method IDs: */
+/* java.lang.Object */
+static jclass obj_class;               /* java.lang.Object */
+static jmethodID obj_ctor;             /* no-arg Constructor for 
java.lang.Object */
+static jmethodID obj_notify_mth;       /* java.lang.Object.notify() */
+static jmethodID obj_notifyall_mth;    /* java.lang.Object.notifyall() */
+static jmethodID obj_wait_mth;         /* java.lang.Object.wait() */
+static jmethodID obj_wait_nanotime_mth; /* java.lang.Object.wait(JI) */
+
+/* GThreadMutex and its methods */
+static jclass mutex_class;
+static jmethodID mutex_ctor;
+static jfieldID mutex_lockForPotentialLockers_fld;
+static jfieldID mutex_potentialLockers_fld;
+
+/* java.lang.Thread and its methods*/
+static jclass thread_class;            /* java.lang.Thread */
+static jmethodID thread_current_mth;   /* Thread.currentThread() */
+static jmethodID thread_equals_mth;    /* Thread.equals() */
+static jmethodID thread_join_mth;      /* Thread.join() */
+static jmethodID thread_setPriority_mth; /* Thread.setPriority() */
+static jmethodID thread_stop_mth;      /* Thread.stop() */
+static jmethodID thread_yield_mth;     /* Thread.yield() */
+
+/* java.lang.ThreadLocal and its methods */
+static jclass threadlocal_class;       /* java.lang.ThreadLocal */
+static jmethodID threadlocal_ctor;     /* Its constructor */
+static jmethodID threadlocal_set_mth;  /* ThreadLocal.set() */
+static jmethodID threadlocal_get_mth;  /* ThreadLocal.get() */
+
+/* java.lang.Long and its methods */
+static jclass long_class;              /* java.lang.Long */
+static jmethodID long_ctor;            /* constructor for it: (J) */
+static jmethodID long_longValue_mth;   /* longValue()J */
+
+
+/* GThreadNativeMethodRunner */
+static jclass runner_class;
+static jmethodID runner_ctor;
+static jmethodID runner_threadToThreadID_mth;
+static jmethodID runner_threadIDToThread_mth;
+static jmethodID runner_deRegisterJoinable_mth;
+static jmethodID runner_start_mth;     /* Inherited Thread.start() */
+
+
+/* java.lang.InterruptedException */
+static jclass interrupted_exception_class;
+
+
+
+
+/* Returns a negative value if there was trouble during initialization.
+   Returns a positive value of the cache was initialized correctly.
+   Never returns zero. */
+static int
+setup_cache (JNIEnv * env)
+{
+  jclass lcl_class;
+  static int initialized = 0;  /* 1 means initialized, 0 means uninitialized,
+                                  -1 means mis-initialized */
+
+  if (initialized)
+    return initialized;
+
+  /* make sure we can report on trouble */
+  if (setup_exception_cache (env) < 0)
+    return initialized = -1;
+
+  assert ( ! (*env)->ExceptionCheck (env));
+
+  /* java.lang.Object and its methods */
+  lcl_class = (*env)->FindClass (env, "java/lang/Object");
+  if (!lcl_class)
+    {
+      BROKEN (env, "cannot find java.lang.Object");
+      return initialized = -1;
+    }
+
+  /* Pin it down. */
+  obj_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+  DELETE_LOCAL_REF (env, lcl_class);
+  if (!obj_class)
+    {
+      BROKEN (env, "Cannot get a global reference to java.lang.Object");
+      return initialized = -1;
+    }
+
+  obj_ctor = (*env)->GetMethodID (env, obj_class, "<init>", "()V");
+  if (!obj_ctor)
+    {
+      BROKEN (env, "cannot find constructor for java.lang.Object");
+      return initialized = -1;
+    }
+
+  obj_notify_mth = (*env)->GetMethodID (env, obj_class, "notify", "()V");
+  if ( ! obj_notify_mth )
+    {
+      BROKEN (env, "cannot find java.lang.Object.notify()V");
+      return initialized = -1;
+    }
+
+  obj_notifyall_mth =
+    (*env)->GetMethodID (env, obj_class, "notifyAll", "()V");
+  if ( ! obj_notifyall_mth)
+    {
+      BROKEN (env, "cannot find java.lang.Object.notifyall()V");
+      return initialized = -1;
+    }
+
+  obj_wait_mth = (*env)->GetMethodID (env, obj_class, "wait", "()V");
+  if ( ! obj_wait_mth )
+    {
+      BROKEN (env, "cannot find Object.<wait()V>");
+      return initialized = -1;
+    }
+
+  obj_wait_nanotime_mth = 
+    (*env)->GetMethodID (env, obj_class, "wait", "(JI)V");
+  if ( ! obj_wait_nanotime_mth )
+    {
+      BROKEN (env, "cannot find Object.<wait(JI)V>");
+      return initialized = -1;
+    }
+
+  /* GThreadMutex and its methods */
+  lcl_class = (*env)->FindClass (env, "gnu/java/awt/peer/gtk/GThreadMutex");
+  if ( ! lcl_class)
+    {
+      BROKEN (env, "cannot find gnu.java.awt.peer.gtk.GThreadMutex");
+      return initialized = -1;
+    }
+  /* Pin it down. */
+  mutex_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+  DELETE_LOCAL_REF (env, lcl_class);
+  if ( ! mutex_class)
+    {
+      BROKEN (env, "Cannot get a global reference to GThreadMutex");
+      return initialized = -1;
+    }
+
+  mutex_ctor = (*env)->GetMethodID (env, mutex_class, "<init>", "()V");
+  if ( ! mutex_ctor)
+    {
+      BROKEN (env, "cannot find zero-arg constructor for GThreadMutex");
+      return initialized = -1;
+    }
+
+  mutex_potentialLockers_fld = (*env)->GetFieldID
+    (env, mutex_class, "potentialLockers", "I");
+  if ( ! mutex_class )
+    {
+      BROKEN (env, "cannot find GThreadMutex.potentialLockers");
+      return initialized = -1;
+    }
+
+  if (! (mutex_lockForPotentialLockers_fld = (*env)->GetFieldID
+        (env, mutex_class, "lockForPotentialLockers", "Ljava/lang/Object;")))
+    {
+      BROKEN (env, "cannot find GThreadMutex.lockForPotentialLockers");
+      return initialized = -1;
+    }
+
+
+  /* java.lang.Thread */
+  if (! (lcl_class = (*env)->FindClass (env, "java/lang/Thread")))
+    {
+      BROKEN (env, "cannot find java.lang.Thread");
+      return initialized = -1;
+    }
+
+  /* Pin it down. */
+  thread_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+  DELETE_LOCAL_REF (env, lcl_class);
+  if (!thread_class)
+    {
+      BROKEN (env, "Cannot get a global reference to java.lang.Thread");
+      return initialized = -1;
+    }
+
+  thread_current_mth =
+    (*env)->GetStaticMethodID (env, thread_class, "currentThread",
+                              "()Ljava/lang/Thread;");
+  if (!thread_current_mth)
+    {
+      BROKEN (env, "cannot find Thread.currentThread() method");
+      return initialized = -1;
+    }
+
+  thread_equals_mth = 
+    (*env)->GetMethodID (env, thread_class, "equals", "(Ljava/lang/Object;)Z");
+  if (!thread_equals_mth)
+    {
+      BROKEN (env, "cannot find Thread.equals() method");
+      return initialized = -1;
+    }
+
+  thread_join_mth = (*env)->GetMethodID (env, thread_class, "join", "()V");
+  if (!thread_join_mth)
+    {
+      BROKEN (env, "cannot find Thread.join() method");
+      return initialized = -1;
+    }
+
+  thread_stop_mth = (*env)->GetMethodID (env, thread_class, "stop", "()V");
+  if ( ! thread_stop_mth )
+    {
+      BROKEN (env, "cannot find Thread.stop() method");
+      return initialized = -1;
+    }
+
+  thread_setPriority_mth = 
+    (*env)->GetMethodID (env, thread_class, "setPriority", "(I)V");
+  if ( ! thread_setPriority_mth )
+    {
+      BROKEN (env, "cannot find Thread.setPriority() method");
+      return initialized = -1;
+    }
+
+  thread_yield_mth = 
+    (*env)->GetStaticMethodID (env, thread_class, "yield", "()V");
+  if ( ! thread_yield_mth )
+    {
+      BROKEN (env, "cannot find Thread.yield() method");
+      return initialized = -1;
+    }
+
+  /* java.lang.ThreadLocal */
+  lcl_class = (*env)->FindClass (env, "java/lang/ThreadLocal");
+  if ( ! lcl_class )
+    {
+      BROKEN (env, "cannot find class java.lang.ThreadLocal");
+      return initialized = -1;
+    }
+
+  /* Pin it down. */
+  threadlocal_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+  DELETE_LOCAL_REF (env, lcl_class);
+  if ( ! threadlocal_class )
+    {
+      BROKEN (env, "Cannot get a global reference to java.lang.ThreadLocal");
+      return initialized = -1;
+    }
+
+  threadlocal_ctor = (*env)->GetMethodID (env, threadlocal_class, 
+                                          "<init>", "()V");
+  if ( ! threadlocal_ctor )
+    {
+      BROKEN (env, "cannot find ThreadLocal.<init>()V");
+      return initialized = -1;
+    }
+  
+  threadlocal_get_mth = (*env)->GetMethodID (env, threadlocal_class,
+                                             "get", "()Ljava/lang/Object;");
+  if ( ! threadlocal_get_mth )
+    {
+      BROKEN (env, "cannot find java.lang.ThreadLocal.get()Object");
+      return initialized = -1;
+    }
+
+  threadlocal_set_mth = (*env)->GetMethodID (env, threadlocal_class,
+                                             "set", "(Ljava/lang/Object;)V");
+  if ( ! threadlocal_set_mth )
+    {
+      BROKEN (env, "cannot find ThreadLocal.set(Object)V");
+      return initialized = -1;
+    }
+
+  /* java.lang.Long */
+  lcl_class = (*env)->FindClass (env, "java/lang/Long");
+  if ( ! lcl_class )
+    {
+      BROKEN (env, "cannot find class java.lang.Long");
+      return initialized = -1;
+    }
+
+  /* Pin it down. */
+  long_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+  DELETE_LOCAL_REF (env, lcl_class);
+  if (!long_class)
+    {
+      BROKEN (env, "Cannot get a global reference to java.lang.Long");
+      return initialized = -1;
+    }
+
+  long_ctor = (*env)->GetMethodID (env, long_class, "<init>", "(J)V");
+  if (!long_ctor)
+    {
+      BROKEN (env, "cannot find method java.lang.Long.<init>(J)V");
+      return initialized = -1;
+    }
+
+  long_longValue_mth =
+    (*env)->GetMethodID (env, long_class, "longValue", "()J");
+  if (!long_longValue_mth)
+    {
+      BROKEN (env, "cannot find method java.lang.Long.longValue()J");
+      return initialized = -1;
+    }
+
+
+  /* GThreadNativeMethodRunner */
+  lcl_class = 
+    (*env)->FindClass (env,
+                       "gnu/java/awt/peer/gtk/GThreadNativeMethodRunner");
+  if ( ! lcl_class )
+    {
+      BROKEN (env,
+             "cannot find gnu.java.awt.peer.gtk.GThreadNativeMethodRunner");
+      return initialized = -1;
+    }
+
+  /* Pin it down. */
+  runner_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+  DELETE_LOCAL_REF (env, lcl_class);
+  if (!runner_class)
+    {
+      BROKEN (env,
+             "Cannot get a global reference to the class 
GThreadNativeMethodRunner");
+      return initialized = -1;
+    }
+
+  runner_ctor = (*env)->GetMethodID (env, runner_class, "<init>", "(JJZ)V");
+  if ( ! runner_ctor )
+    {
+      BROKEN (env,
+             "cannot find method GThreadNativeMethodRunner.<init>(JJZ)");
+      return initialized = -1;
+    }
+      
+  runner_start_mth = (*env)->GetMethodID (env, runner_class, "start", "()V");
+  if ( ! runner_start_mth )
+    {
+      BROKEN (env, "cannot find method GThreadNativeMethodRunner.start()V");
+      return initialized = -1;
+    }
+
+
+  runner_threadToThreadID_mth = 
+    (*env)->GetStaticMethodID (env, runner_class,
+                               "threadToThreadID", "(Ljava/lang/Thread;)I");
+  if ( ! runner_threadToThreadID_mth )
+    {
+      BROKEN (env,
+             "cannot find method 
GThreadNativeMethodRunner.threadToThreadID(java.lang.Thread)I");
+      return initialized = -1;
+    }
+
+
+  runner_threadIDToThread_mth = 
+    (*env)->GetStaticMethodID (env, runner_class,
+                               "threadIDToThread", "(I)Ljava/lang/Thread;");
+  if ( ! runner_threadIDToThread_mth )
+    {
+      BROKEN (env,
+             "cannot find method 
GThreadNativeMethodRunner.threadIDToThread(I)java.lang.Thread");
+      return initialized = -1;
+    }
+
+
+  runner_deRegisterJoinable_mth =
+    (*env)->GetStaticMethodID (env, runner_class, "deRegisterJoinable",
+                              "(Ljava/lang/Thread;)V");
+  if (!runner_deRegisterJoinable_mth)
+    {
+      BROKEN (env,
+             "cannot find method 
GThreadNativeMethodRunner.deRegisterJoinable(java.lang.Thread)V");
+      return initialized = -1;
+    }
+
+
+  /* java.lang.InterruptedException */
+  lcl_class = (*env)->FindClass (env, "java/lang/InterruptedException");
+  if ( ! lcl_class )
+    {
+      BROKEN (env, "cannot find class java.lang.InterruptedException");
+      return initialized = -1;
+    }
+
+  /* Pin it down. */
+  interrupted_exception_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+  DELETE_LOCAL_REF (env, lcl_class);
+  if (!interrupted_exception_class)
+    {
+      BROKEN (env, "Cannot make a global reference"
+             " to java.lang.InterruptedException");
+      return initialized = -1;
+    }
+
+#ifdef JNI_VERSION_1_2
+  if (HAVE_JNI_VERSION_1_2)
+    assert ( ! (*env)->ExceptionCheck (env));
+  else
+#endif
+    assert ( ! (*env)->ExceptionOccurred (env));
+
+
+  return initialized = 1;
+}
+
+
 
-    jstring jmessage;
-    jclass obj_class;
-    jobject obj;
-    jmethodID ctor;
-    int len;
-    char *buf;
-
-    /* rethrow if an exception happened */
-    if ((cause = (*gdk_env)->ExceptionOccurred(gdk_env)) != NULL)
-      {
-
-       /* allocate local message in Java */
-       len = strlen(message) + strlen(file) + 25;
-       buf = (char *) malloc(len);
-       if (buf != NULL)
-         {
-           bzero(buf, len);
-           sprintf(buf, "%s (at %s:%d)", message, file, line);
-           jmessage = (*gdk_env)->NewStringUTF(gdk_env, buf);
-           free(buf);
-         }
-       else
-         jmessage = NULL;
-    
-       /* create RuntimeException wrapper object */
-       obj_class = (*gdk_env)->FindClass (gdk_env,
-                       "java/lang/RuntimeException");
-       ctor = (*gdk_env)->GetMethodID(gdk_env, obj_class, "<init>",
-       "(Ljava/langString;Ljava/lang/Throwable)V");
-       obj = (*gdk_env)->NewObject (gdk_env, obj_class, ctor, jmessage, cause);
-
-       /* throw it */
-       (*gdk_env)->Throw(gdk_env, (jthrowable)obj);
-      }
-}
-
-/* This macro is used to include a source location in the exception message */
-#define MAYBE_RETHROW(_class, _message) \
-maybe_rethrow(_class, _message, __FILE__, __LINE__)
 
 
 /************************************************************************/
 /* Utilities to allocate and free java.lang.Objects                    */
 /************************************************************************/
 
-/*  Both the mutexes and the condition variables are java.lang.Object objects,
+/* The condition variables are java.lang.Object objects,
  * which this method allocates and returns a global ref.  Note that global
  * refs must be explicitly freed (isn't C fun?).
  */
-static jobject *allocatePlainObject() {
-  jclass obj_class;
-  jobject *obj;
-  JNIEnv *gdk_env;
-  jmethodID ctor;
-
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
-
-  obj_class = (*gdk_env)->FindClass (gdk_env, "java/lang/Object");
-  MAYBE_RETHROW(gdk_env, "cannot find Object");
-
-  ctor = (*gdk_env)->GetMethodID(gdk_env, obj_class, "<init>", "()V");
-  MAYBE_RETHROW(gdk_env, "cannot find constructor");
-
-  obj = (jobject *) g_malloc (sizeof (jobject));
-  *obj = (*gdk_env)->NewObject (gdk_env, obj_class, ctor);
-  MAYBE_RETHROW(gdk_env, "cannot allocate object");
-  
-  *obj = (*gdk_env)->NewGlobalRef (gdk_env, *obj);
-  MAYBE_RETHROW(gdk_env, "cannot make global ref");
+static jobject
+allocatePlainObject (JNIEnv * env)
+{
+  jobject lcl_obj, global_obj;
+
+  lcl_obj = (*env)->NewObject (env, obj_class, obj_ctor);
+  if (!lcl_obj)
+    {
+      BROKEN (env, "cannot allocate object");
+      return NULL;
+    }
+
+  global_obj = (*env)->NewGlobalRef (env, lcl_obj);
+  DELETE_LOCAL_REF (env, lcl_obj);
+  if (!global_obj)
+    {
+      NEW_BROKEN (env, "cannot make global ref for a new plain Java object");
+      /* Deliberate fall-through */
+    }
 
-  return obj;
+  return global_obj;
 }
 
-/*  Frees a Java object given a global ref (isn't C fun?) */
-static void freePlainObject(jobject *obj) {
-  JNIEnv *gdk_env;
+/*  Frees any Java object given a global ref (isn't C fun?) */
+static void
+freeObject (JNIEnv * env, jobject obj)
+{
+  if (obj)
+    {
+      (*env)->DeleteGlobalRef (env, obj);
+      /* DeleteGlobalRef can never fail */
+    }
+}
 
-  if (obj) {
-    (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
 
-    (*gdk_env)->DeleteGlobalRef (gdk_env, *obj);
-    MAYBE_RETHROW(gdk_env, "cannot delete global ref");
-  
-    g_free (obj);
-  }
+/************************************************************************/
+/* Utilities to allocate and free Java mutexes                         */
+/************************************************************************/
+
+/* The mutexes are gnu.java.awt.peer.gtk.GThreadMutex objects,
+ * which this method allocates and returns a global ref.  Note that global
+ * refs must be explicitly freed (isn't C fun?).
+ *
+ * Free this with freeObject()
+ */
+static jobject
+allocateMutexObject (JNIEnv * env)
+{
+  jobject lcl_obj, global_obj;
+
+  lcl_obj = (*env)->NewObject (env, mutex_class, mutex_ctor);
+  if (!lcl_obj)
+    {
+      BROKEN (env, "cannot allocate a GThreadMutex");
+      return NULL;
+    }
+
+  global_obj = (*env)->NewGlobalRef (env, lcl_obj);
+  DELETE_LOCAL_REF (env, lcl_obj);
+  if (!global_obj)
+    {
+      NEW_BROKEN (env, "cannot make global ref");
+      /* Deliberate fallthrough */
+    }
+
+  return global_obj;
 }
 
 
@@ -184,195 +1154,721 @@
 /************************************************************************/
 
 /* Lock a Java object */
-static void takeLock(JNIEnv *gdk_env, void *mutex) {
-  (*gdk_env)->MonitorEnter (gdk_env, *((jobject *)mutex));
-  MAYBE_RETHROW(gdk_env, "cannot get lock");
+#define ENTER_MONITOR(env, m)                  \
+    enterMonitor(env, m, G_STRINGIFY(m))
+
+/* Return -1 on failure, 0 on success. */
+static int
+enterMonitor (JNIEnv * env, jobject monitorObj, const char monName[])
+{
+  if (TRACE_MONITORS)
+    tracing ("  <MonitorEnter(%s)>", monName);
+  assert (monitorObj);
+  if ((*env)->MonitorEnter (env, monitorObj) < 0)
+    {
+      BROKEN (env, "cannot enter monitor");
+      return -1;
+    }
+  return 0;
 }
 
+
 /* Unlock a Java object */
-static void releaseLock(JNIEnv *gdk_env, void *mutex) {
-    (*gdk_env)->MonitorExit (gdk_env, *((jobject *)mutex));
-  MAYBE_RETHROW(gdk_env, "cannot release lock");
+#define EXIT_MONITOR(env, m)                   \
+    exitMonitor(env, m, G_STRINGIFY(m))
+
+static int
+exitMonitor (JNIEnv * env, jobject mutexObj, const char monName[])
+{
+  if (TRACE_MONITORS)
+    tracing (" <MonitorExit(%s)>", monName);
+  assert (mutexObj);
+  if ((*env)->MonitorExit (env, mutexObj) < 0)
+    {
+      BROKEN (env, "cannot exit monitor ");
+      return -1;
+    }
+  return 0;
+}
+
+
+/************************************************************************/
+/* Miscellaneous utilities                                             */
+/************************************************************************/
+
+/* Get the Java Thread object that corresponds to a particular thread ID. 
+   A negative thread Id gives us a null object.
+
+   Returns a local reference. 
+*/
+static jobject
+getThreadFromThreadID (JNIEnv * env, gpointer gThreadID)
+{
+  jint threadNum = (jint) gThreadID;
+  jobject thread;
+
+  if (threadNum < 0)
+    {
+      NEW_BROKEN (env, "getThreadFromThreadID asked to look up"
+                      " a negative thread index");
+      return NULL;
+    }
+
+  thread = (*env)->CallStaticObjectMethod
+    (env, runner_class, runner_threadIDToThread_mth, threadNum);
+
+  if (MAYBE_BROKEN (env, "cannot get Thread for threadID "))
+    return NULL;
+
+  return thread;
+}
+
+/** Return the unique threadID of THREAD.
+
+   Error handling: Return (gpointer) -1 on all failures, 
+   and propagate an exception. 
+*/
+static gpointer
+getThreadIDFromThread (JNIEnv * env, jobject thread)
+{
+  jint threadNum;
+
+  if (ENABLE_EXPENSIVE_ASSERTIONS)
+    assert ((*env)->IsInstanceOf (env, thread, thread_class));
+
+  HIDE_OLD_TROUBLE (env);
+
+  threadNum = (*env)->CallStaticIntMethod
+    (env, runner_class, runner_threadToThreadID_mth, thread);
+
+  if (MAYBE_BROKEN (env, "cannot get ThreadID for a Thread "))
+    {
+      threadNum = -1;
+      goto done;
+    }
+
+
+  SHOW_OLD_TROUBLE ();
+
+done:
+  return (gpointer) threadNum;
+}
+
+
+/************************************************************************/
+/* The Actual JNI functions that we pass to the function vector.       */
+/************************************************************************/
+
+
+/************************************************************************/
+/* Mutex Functions                                                     */
+/************************************************************************/
+
+/*** Mutex Utilities  ****/
+struct mutexObj_cache
+{
+  jobject lockForPotentialLockersObj;  /* Lock for the potentialLockers
+                                          field.  Local reference. */
+  jobject lockObj;             /* The real lock we use.  This is a GLOBAL
+                                  reference and must not be freed. */
+};
+
+/* Initialize the cache of sub-locks for a particular mutex object.
+
+  -1 on error, 0 on success.  The caller is not responsible for freeing the
+   partially-populated cache in case of failure (but in practice does anyway)
+   (This actually never fails, though, since GetObjectField allegedly never
+   fails.)  
+
+   Guaranteed to leave all fields of the cache initialized, even if only to
+   zero. 
+*/
+static int
+populate_mutexObj_cache (JNIEnv * env, jobject mutexObj,
+                        struct mutexObj_cache *mcache)
+{
+  mcache->lockObj = mutexObj;  /* the mutexObj is its own lock.  */
+  assert (mcache->lockObj);
+
+  mcache->lockForPotentialLockersObj = (*env)->GetObjectField
+    (env, mutexObj, mutex_lockForPotentialLockers_fld);
+  /* GetObjectField can never fail. Or so the JNI book says.
+     I personally fail to see how it can never fail -- one might give it a
+     NULL object, for instance.  That would throw a NullPointerException in
+     Java, so why not here?
+
+     In any case, retrieving a NULL object is cause for an internal error
+     -- there would then be a mutex object that was not properly intialized. */
+  assert (mcache->lockForPotentialLockersObj);
+
+  return 0;
+}
+
+
+/* Clean out the mutexObj_cache, even if it was never populated. */
+static void
+clean_mutexObj_cache (JNIEnv * env, struct mutexObj_cache *mcache)
+{
+  /* OK to pass NULL refs to DELETE_LOCAL_REF */
+  DELETE_LOCAL_REF (env, mcache->lockForPotentialLockersObj);
+  /* mcache->lockObj is a GLOBAL reference. */
+  mcache->lockObj = NULL;
+}
+
+/* -1 on failure, 0 on success.
+   The mutexObj_cache is already populated for this particular object. */
+static int
+mutexObj_lock (JNIEnv * env, jobject mutexObj, struct mutexObj_cache *mcache)
+{
+  jint potentialLockers;
+
+  if (ENTER_MONITOR (env, mcache->lockForPotentialLockersObj))
+    return -1;
+
+  potentialLockers = (*env)->GetIntField
+    (env, mutexObj, mutex_potentialLockers_fld);
+  if (MAYBE_BROKEN
+      (env, "cannot read GThreadMutex.potentialLockers field's value"))
+    return -1;
+
+  ++potentialLockers;
+
+  (*env)->SetIntField
+    (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+
+  if (EXIT_MONITOR (env, mcache->lockForPotentialLockersObj))
+    return -1;
+
+  if (ENTER_MONITOR (env, mcache->lockObj))
+    return -1;
+
+  SHOW_OLD_TROUBLE ();
+
+  return 0;
+}
+
+/* Unlock a GMutex, once we're already in JNI and have already gotten the
+   mutexObj for it.  This skips the messages that TRACE_API_CALLS would
+   print.
+
+   Returns -1 on error, 0 on success. */
+static int
+mutexObj_unlock (JNIEnv * env, jobject mutexObj,
+                struct mutexObj_cache *mcache)
+{
+  jint potentialLockers;
+  int ret = -1;                        /* assume failure until we suceed.  */
+
+  /* Free the lock first, so that someone waiting for the lock can get it
+     ASAP. */
+  /* This is guaranteed not to block. */
+  if (EXIT_MONITOR (env, mcache->lockObj) < 0)
+    goto done;
+
+  /* Kick down potentialLockers by one.  We do this AFTER we free the lock, so
+     that we hold it no longer than necessary. */
+  if (ENTER_MONITOR (env, mcache->lockForPotentialLockersObj) < 0)
+    goto done;
+
+  potentialLockers = (*env)->GetIntField
+    (env, mutexObj, mutex_potentialLockers_fld);
+  /* Never fails, so the JNI book says. */
+
+  assert (potentialLockers >= 1);
+  --potentialLockers;
+
+  (*env)->SetIntField
+    (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+  /* Never fails, so the JNI book says. */
+
+  /* Clean up. */
+  if (EXIT_MONITOR (env, mcache->lockForPotentialLockersObj) < 0)
+    goto done;
+  ret = 0;
+
+done:
+  return ret;
 }
 
-/* Create a mutex, which is a java.lang.Object for us */
-static GMutex *g_mutex_new_jni_impl (void) {
-  return (GMutex*) allocatePlainObject();
+/*** Mutex Implementations ****/
+
+/* Create a mutex, which is a java.lang.Object for us.
+   In case of failure, we'll return NULL.  Which will implicitly 
+   cause future calls to fail. */
+static GMutex *
+mutex_new_jni_impl (void)
+{
+  jobject mutexObj;
+  JNIEnv *env;
+
+  if (TRACE_API_CALLS)
+    tracing ("mutex_new_jni_impl()");
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+
+  if (setup_cache (env) < 0)
+    {
+      mutexObj = NULL;
+      goto done;
+    }
+
+  mutexObj = allocateMutexObject (env);
+
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> %p \n", mutexObj);
+
+  return (GMutex *) mutexObj;
+
 }
 
 /* Lock a mutex. */
-static void g_mutex_lock_jni_impl (GMutex *mutex __attribute__((unused))) {
-  JNIEnv *gdk_env;
+static void
+mutex_lock_jni_impl (GMutex * mutex)
+{
+  struct mutexObj_cache mcache;
+  jobject mutexObj = (jobject) mutex;
+  JNIEnv *env;
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  if (TRACE_API_CALLS)
+    tracing ("mutex_lock_jni_impl( mutexObj = %p )", mutexObj);
 
-  takeLock(gdk_env, mutex);
+  assert (mutexObj);
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+
+  if (setup_cache (env) < 0)
+    goto done;
+
+  HIDE_OLD_TROUBLE (env);
+
+  if (populate_mutexObj_cache (env, mutexObj, &mcache) < 0)
+    goto done;
+
+  mutexObj_lock (env, mutexObj, &mcache);
+  /* No need to error check; we've already reported it in any case. */
+
+done:
+  clean_mutexObj_cache (env, &mcache);
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID \n");
 }
 
-/*  Try to lock a mutex.  Actually, do not try because Java objects
- * do not provide such an interface.  To be at least minimally correct,
- * pretend we tried and failed.
- */
-static gboolean g_mutex_trylock_jni_impl
-  (GMutex *mutex __attribute__((unused)))
+
+/*  Try to lock a mutex.  Return TRUE if we succeed, FALSE if we fail.  
+    FALSE on error. */
+static gboolean
+mutex_trylock_jni_impl (GMutex * gmutex)
 {
-  /* XXX Shall we implement this in a VM-specific way under a flag? */
-  return FALSE;
+  jobject mutexObj = (jobject) gmutex;
+  jint potentialLockers;
+  gboolean ret = FALSE;
+  JNIEnv *env;
+  struct mutexObj_cache mcache;
+
+  if (TRACE_API_CALLS)
+    tracing ("mutex_trylock_jni_impl(mutexObj=%p)", mutexObj);
+
+  assert (mutexObj);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
+
+  if (populate_mutexObj_cache (env, mutexObj, &mcache) < 0)
+    goto done;
+
+  if (ENTER_MONITOR (env, mcache.lockForPotentialLockersObj))
+    goto done;
+
+  potentialLockers = (*env)->GetIntField
+    (env, mutexObj, mutex_potentialLockers_fld);
+
+  assert (potentialLockers >= 0);
+
+  if (potentialLockers)
+    {
+      /* Already locked.  Clean up and leave. */
+      EXIT_MONITOR (env, mcache.lockForPotentialLockersObj);   
+      /* Ignore any error code from EXIT_MONITOR; there's nothing we could do
+        at this level, in any case. */
+      goto done;
+    }
+
+  /* Guaranteed not to block. */
+  if (ENTER_MONITOR (env, mcache.lockObj))
+    goto done;
+
+
+  /* We have the monitor.  Record that fact. */
+  potentialLockers = 1;
+  (*env)->SetIntField
+    (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+  /* Set*Field() never fails */
+
+  /* Clean up. */
+  if (EXIT_MONITOR (env, mcache.lockForPotentialLockersObj))
+    goto done;
+
+  SHOW_OLD_TROUBLE ();
+
+  ret = TRUE;                  /* We have the lock. */
+done:
+  clean_mutexObj_cache (env, &mcache);
+  if (TRACE_API_CALLS)
+    tracing (" ==> %s\n", ret ? "TRUE" : "FALSE");
+  return ret;
 }
 
+
 /* Unlock a mutex. */
-static void g_mutex_unlock_jni_impl (GMutex *mutex) {
-  JNIEnv *gdk_env;
+static void
+mutex_unlock_jni_impl (GMutex * gmutex)
+{
+  jobject mutexObj = (jobject) gmutex;
+  struct mutexObj_cache mcache;
+  JNIEnv *env;
+
+  if (TRACE_API_CALLS)
+    tracing ("mutex_unlock_jni_impl(mutexObj=%p)", mutexObj);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  assert (mutexObj);
 
-  releaseLock(gdk_env, mutex);
+  if ( populate_mutexObj_cache (env, mutexObj, &mcache) < 0)
+    goto done;
+
+  (void) mutexObj_unlock (env, mutexObj, &mcache);
+
+  SHOW_OLD_TROUBLE ();
+
+done:
+  clean_mutexObj_cache (env, &mcache);
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID\n");
 }
 
-/* Free a mutex (isn't C fun?) */
-static void g_mutex_free_jni_impl (GMutex *mutex)
+
+
+/* Free a mutex (isn't C fun?).  OK this time for it to be NULL.  
+   No failure conditions, for a change.  */
+static void
+mutex_free_jni_impl (GMutex * mutex)
 {
-  freePlainObject( (jobject*)mutex );
+  jobject mutexObj = (jobject) mutex;
+  JNIEnv *env;
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+
+  if (TRACE_API_CALLS)
+    tracing ("mutex_free_jni_impl(%p)", mutexObj);
+
+  freeObject (env, mutexObj);
+
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID\n");
 }
 
 
+
+
 /************************************************************************/
 /* Condition variable code                                             */
 /************************************************************************/
 
 /* Create a new condition variable.  This is a java.lang.Object for us. */
-static GCond *g_cond_new_jni_impl () {
-  return (GCond*)allocatePlainObject();
+static GCond *
+cond_new_jni_impl (void)
+{
+  jobject condObj;
+  JNIEnv *env;
+
+  if (TRACE_API_CALLS)
+    tracing ("mutex_free_jni_impl()");
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+
+  condObj = allocatePlainObject (env);
+
+  if (TRACE_API_CALLS)
+    tracing (" ==> %p\n", condObj);
+
+  return (GCond *) condObj;
 }
 
 /*  Signal on a condition variable.  This is simply calling Object.notify
  * for us.
  */
-static void g_cond_signal_jni_impl (GCond *cond) {
-  jclass lcl_class;
-  jmethodID signal_mth;
-  JNIEnv *gdk_env;
+static void
+cond_signal_jni_impl (GCond * gcond)
+{
+  JNIEnv *env;
+  jobject condObj = (jobject) gcond;
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  if (TRACE_API_CALLS)
+    tracing ("cond_signal_jni_impl(condObj = %p)", condObj);
 
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
-  MAYBE_RETHROW(gdk_env, "cannot find Object");
-  
-  signal_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notify", "()V");
-  MAYBE_RETHROW(gdk_env, "cannot find Object.<notify>");
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
+
+  assert (condObj);
 
   /* Must have locked an object to call notify */
-  takeLock(gdk_env, cond);
+  if (ENTER_MONITOR (env, condObj))
+    goto done;
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, signal_mth);
-  MAYBE_RETHROW(gdk_env, "cannot signal mutex");
+  (*env)->CallVoidMethod (env, condObj, obj_notify_mth);
+  if (MAYBE_BROKEN (env, "cannot signal mutex with Object.notify()"))
+    {
+      if (EXIT_MONITOR (env, condObj))
+       BADLY_BROKEN1 ("Failed to unlock a monitor; the VM may deadlock.");
+      goto done;
+    }
+
+  EXIT_MONITOR (env, condObj);
 
-  releaseLock(gdk_env, cond);
+  SHOW_OLD_TROUBLE ();
+
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID\n");
 }
 
 /*  Broadcast to all waiting on a condition variable.  This is simply 
  * calling Object.notifyAll for us.
  */
-static void g_cond_broadcast_jni_impl (GCond *cond) {
-  jclass lcl_class;
-  jmethodID bcast_mth;
-  JNIEnv *gdk_env;
+static void
+cond_broadcast_jni_impl (GCond * gcond)
+{
+  jobject condObj = (jobject) gcond;
+  JNIEnv *env;
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  if (TRACE_API_CALLS)
+    tracing ("cond_broadcast_jni_impl(condObj=%p)", condObj);
 
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
-  MAYBE_RETHROW(gdk_env, "cannot find Object");
-  
-  bcast_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notifyAll", "()V");
-  MAYBE_RETHROW(gdk_env, "cannot find Object.<notifyAll>");
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
 
+  assert (condObj);
   /* Must have locked an object to call notifyAll */
-  takeLock(gdk_env, cond);
+  if (ENTER_MONITOR (env, condObj))
+    goto done;
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, bcast_mth);
-  MAYBE_RETHROW(gdk_env, "cannot broadcast to mutex");
+  (*env)->CallVoidMethod (env, condObj, obj_notifyall_mth);
+  if (MAYBE_BROKEN (env, "cannot broadcast to mutex with Object.notify()"))
+    {
+      EXIT_MONITOR (env, condObj);
+      goto done;
+    }
 
-  releaseLock(gdk_env, cond);
+  EXIT_MONITOR (env, condObj);
+
+  SHOW_OLD_TROUBLE ();
+
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID\n");
 }
 
 
-/*  Wait on a condition variable.  For us, this simply means call
+/* Wait on a condition variable.  For us, this simply means calling
  * Object.wait.
+ *
+ * Throws a Java exception on trouble; may leave the mutexes set arbitrarily.
+ * XXX TODO: Further improve error recovery.
  */
-static void g_cond_wait_jni_impl
-  (GCond *cond, GMutex *mutex __attribute__((unused)))
+static void
+cond_wait_jni_impl (GCond * gcond, GMutex * gmutex)
 {
-  jclass lcl_class;
-  jmethodID wait_mth;
-  JNIEnv *gdk_env;
-
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
-
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
-  MAYBE_RETHROW(gdk_env, "cannot find Object");
-  
-  wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "()V");
-  MAYBE_RETHROW(gdk_env, "cannot find Object.<wait>");
-
-  /* Must have locked an object to call wait */
-  takeLock(gdk_env, cond);
-
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth);
-  MAYBE_RETHROW(gdk_env, "cannot wait on mutex");
+  struct mutexObj_cache cache;
+  jobject condObj = (jobject) gcond;
+  jobject mutexObj = (jobject) gmutex;
+  JNIEnv *env;
+
+  if (TRACE_API_CALLS)
+    tracing ("cond_wait_jni_impl(condObj=%p, mutexObj=%p)",
+            condObj, mutexObj);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
+
+  assert (condObj);
+  assert (mutexObj);
+  /* Must have locked a Java object to call wait on it */
+  if (ENTER_MONITOR (env, condObj) < 0)
+    goto done;
+
+  /* Our atomicity is now guaranteed; we're protected by the Java monitor on
+     condObj.  Unlock the GMutex. */
+  if (mutexObj_unlock (env, mutexObj, &cache))
+    goto done;
+
+  (*env)->CallVoidMethod (env, condObj, obj_wait_mth);
+  if (MAYBE_BROKEN (env, "cannot wait on condObj"))
+    {
+      EXIT_MONITOR (env, condObj);     /* ignore err checking */
+      goto done;
+    }
 
-  releaseLock(gdk_env, cond);
+  /* Re-acquire the lock on the GMutex.  Do this while we're protected by the
+     Java monitor on condObj. */
+  if (mutexObj_lock (env, mutexObj, &cache))
+    goto done;
+
+  EXIT_MONITOR (env, condObj);
+
+  SHOW_OLD_TROUBLE ();
+
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID\n");
 }
 
-/*  Wait on a condition vairable until a timeout.  This is a little tricky
+
+/** Wait on a condition variable until a timeout.  This is a little tricky
  * for us.  We first call Object.wait(J) giving it the appropriate timeout
  * value.  On return, we check whether an InterruptedException happened.  If
- * so, that is Java-speak for wait timing out.
+ * so, that is Java-speak for wait timing out.  
+ * 
+ * We return FALSE if we timed out.  Return TRUE if the condition was
+ * signalled first, before we timed out.
+ *
+ * In case of trouble we throw a Java exception.  Whether we return FALSE or
+ * TRUE depends upon whether the condition was raised before the trouble
+ * happened. 
+ *
+ * I believe that this function goes to the proper lengths to try to unlock
+ * all of the locked mutexes and monitors, as appropriate, and that it further
+ * tries to make sure that the thrown exception is the current one, not any
+ * future cascaded one from something like a failure to unlock the monitors.
  */
 static gboolean
-g_cond_timed_wait_jni_impl
-  (GCond *cond, GMutex *mutex __attribute__((unused)),
-   GTimeVal *end_time)
+cond_timed_wait_jni_impl (GCond * gcond, GMutex * gmutex, GTimeVal * end_time)
 {
-  jclass lcl_class;
-  jmethodID wait_mth;
-  JNIEnv *gdk_env;
-  jlong time;
+  JNIEnv *env;
+  jlong time_millisec;
+  jint time_nanosec;
   jthrowable cause;
+  jobject condObj = (jobject) gcond;
+  jobject mutexObj = (jobject) gmutex;
+  gboolean condRaised = FALSE; /*  Condition has not been raised yet. */
+  struct mutexObj_cache cache;
+  gboolean interrupted;
+
+  if (TRACE_API_CALLS)
+    {
+      tracing ("cond_timed_wait_jni_impl(cond=%p, mutex=%p,"
+              " end_time=< sec=%lu, usec=%lu >)", condObj, mutexObj,
+              (unsigned long) end_time->tv_sec,
+              (unsigned long) end_time->tv_usec);
+    }
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
 
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
-  MAYBE_RETHROW(gdk_env, "cannot find Object");
-  
-  wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "(J)V");
-  MAYBE_RETHROW(gdk_env, "cannot find Object.<wait(J)>");
-  
-  time = end_time->tv_sec*1000;
-  time += end_time->tv_usec/1000;
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
+
+  time_millisec = end_time->tv_sec * 1000 + end_time->tv_usec / 1000;
+  time_nanosec = 1000 * (end_time->tv_usec % 1000);
 
   /* Must have locked an object to call wait */
-  takeLock(gdk_env, cond);
+  if (ENTER_MONITOR (env, condObj) < 0)
+    goto done;
+
+  if (mutexObj_unlock (env, mutexObj, &cache) < 0)
+    {
+      if (EXIT_MONITOR (env, condObj) < 0)
+       criticalMsg
+         ("Unable to unlock an existing lock on a condition; your proram may 
deadlock");
+      goto done;
+    }
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth, time);
 
-  if ((cause = (*gdk_env)->ExceptionOccurred(gdk_env)) != NULL) {
-    jclass intr = (*gdk_env)->FindClass (gdk_env, 
"java.lang.InterruptedException");
-    if ( (*gdk_env)->IsInstanceOf(gdk_env, cause, intr) ) {
-      releaseLock(gdk_env, cond);
-  return FALSE;
-    } else {
-      MAYBE_RETHROW(gdk_env, "error in timed wait");
+  (*env)->CallVoidMethod (env, condObj, obj_wait_nanotime_mth,
+                         time_millisec, time_nanosec);
+
+  /* If there was trouble, save that fact, and the reason for the trouble.  We
+     want to respond to this condition as fast as possible. */
+  cause = (*env)->ExceptionOccurred (env);
+
+  if ( ! cause )
+    {
+      condRaised = TRUE;       /* condition was signalled */
+    }
+  else if ((*env)->IsInstanceOf (env, cause, interrupted_exception_class))
+    {
+      condRaised = FALSE;      /* Condition was not raised before timeout.
+                                  (This is redundant with the initialization
+                                  of condRaised above) */
+      (*env)->ExceptionClear (env);    /* Clear the InterruptedException. */
+      cause = NULL;            /* no pending cause now.  */
+    }
+  else
+    {
+      interrupted = FALSE;     /* Trouble, but not because of
+                                  InterruptedException.  Assume the condition
+                                  was not raised. */
+      /* Leave condRaised set to FALSE */
     }
-  }
 
-  releaseLock(gdk_env, cond);
+  /* Irrespective of whether there is a pending problem to report, go ahead
+     and try to clean up.  This may end up throwing an exception that is
+     different from the one that was thrown by the call to Object.wait().
+     So we will override it with the first exception (don't want to have
+     cascading problems). */
+  if (mutexObj_lock (env, mutexObj, &cache) && !cause)
+    {
+      cause = (*env)->ExceptionOccurred (env);
+      assert (cause);
+    }
+
+  if (EXIT_MONITOR (env, condObj) && !cause)
+    {
+      cause = (*env)->ExceptionOccurred (env);
+      assert (cause);
+    }
 
-  return TRUE;
+  if (cause)                   /* Raise the first cause. */
+    {
+      BROKEN_CAUSE (env, cause, "error in timed wait or during its cleanup");
+      goto done;
+    }
+
+  SHOW_OLD_TROUBLE ();
+
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> condRaised = %s\n", condRaised ? "TRUE" : "FALSE");
+  return condRaised;
 }
 
-/* Free a condition variable.  (isn't C fun?) */
-static void g_cond_free_jni_impl (GCond *cond) {
-  freePlainObject( (jobject*)cond );
+
+/* Free a condition variable.  (isn't C fun?).  Can not fail. */
+static void
+cond_free_jni_impl (GCond * cond)
+{
+  jobject condObj = (jobject) cond;
+  JNIEnv *env;
+
+  if (TRACE_API_CALLS)
+    tracing ("cond_free_jni_impl(condObj = %p)", condObj);
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+
+  freeObject (env, condObj);
+
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID\n");
 }
 
 
@@ -380,128 +1876,610 @@
 /* Thread-local data code                                              */
 /************************************************************************/
 
-/*  Create a new thread-local key.  We use java.lang.ThreadLocal objects
- * for this.
+/* Create a new thread-local key.  We use java.lang.ThreadLocal objects
+ * for this.  This returns the pointer representation of a Java global
+ * reference. 
+ * 
+ * We will throw a Java exception and return NULL in case of failure.
  */
-static GPrivate *g_private_new_jni_impl
-  (GDestroyNotify notify __attribute__((unused)))
+static GPrivate *
+private_new_jni_impl (GDestroyNotify notify __attribute__ ((unused)))
 {
-  jclass lcl_class;
-  jobject *local;
-  JNIEnv *gdk_env;
-  jmethodID ctor;
-
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  JNIEnv *env;
+  jobject lcl_key;
+  jobject global_key;
+  GPrivate *gkey = NULL;       /* Error return code */
+
+  if (TRACE_API_CALLS)
+    tracing ("private_new_jni_impl()");
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
+
+  lcl_key = (*env)->NewObject (env, threadlocal_class, threadlocal_ctor);
+  if ( ! lcl_key )
+    {
+      BROKEN (env, "cannot allocate a ThreadLocal");
+      goto done;
+    }
 
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
+  global_key = ((*env)->NewGlobalRef (env, lcl_key));
+  DELETE_LOCAL_REF (env, lcl_key);
+  if ( ! global_key)
+    {
+      NEW_BROKEN (env, "cannot create a GlobalRef to a new ThreadLocal");
+      goto done;
+    }
 
-  ctor = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "<init>", "()V");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<init>");
+  gkey = (GPrivate *) global_key;
+  SHOW_OLD_TROUBLE ();
 
-  local = (jobject *) g_malloc (sizeof (jobject));
-  *local = (*gdk_env)->NewObject(gdk_env, lcl_class, ctor);
-  MAYBE_RETHROW(gdk_env, "cannot allocate a ThreadLocal");
-  
-  *local = ((*gdk_env)->NewGlobalRef (gdk_env, *local));
-  MAYBE_RETHROW(gdk_env, "cannot create a GlobalRef");
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> %p\n", (void *) gkey);
 
-  return (GPrivate*) local;
+  return gkey;
 }
 
 /*  Get this thread's value for a thread-local key.  This is simply
- * ThreadLocal.get for us.
+ * ThreadLocal.get for us.  Return NULL if no value.  (I can't think of
+ * anything else to do.)
  */
-static gpointer g_private_get_jni_impl (GPrivate *private) {
-  jclass lcl_class;
-  jobject lcl_obj;
-  JNIEnv *gdk_env;
-  jmethodID get_mth;
-  jclass int_class;
-  jmethodID val_mth;
-  jint int_val;
-
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+static gpointer
+private_get_jni_impl (GPrivate * gkey)
+{
+  JNIEnv *env;
+  jobject val_wrapper;
+  jobject keyObj = (jobject) gkey;
+  gpointer thread_specific_data = NULL;        /* Init to the error-return 
value */
+
+  jlong val;
+
+  if (TRACE_API_CALLS)
+    tracing ("private_get_jni_impl(keyObj=%p)", keyObj);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
+
+  val_wrapper = (*env)->CallObjectMethod (env, keyObj, threadlocal_get_mth);
+  if (MAYBE_BROKEN (env, "cannot find thread-local object"))
+    goto done;
+
+  if (! val_wrapper ) 
+    {
+      /* It's Java's "null" object.  No ref found.  This is OK; we must never
+         have set a value in this thread.  Note that this next statement is
+         not necessary, strictly speaking, since we're already initialized to
+         NULL.  A good optimizing C compiler will detect that and optimize out
+         this statement. */
+      thread_specific_data = NULL;
+      goto done;
+    }
 
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
+  val = (*env)->CallLongMethod (env, val_wrapper, long_longValue_mth);
 
-  get_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "get", 
"()Ljava/lang/Object;");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<get>");
+  if (MAYBE_BROKEN (env, "cannot get thread local value"))
+    goto done;
 
-  lcl_obj = (*gdk_env)->CallObjectMethod(gdk_env, *(jobject*)private, get_mth);
-  MAYBE_RETHROW(gdk_env, "cannot find thread-local object");
+  thread_specific_data = (gpointer) (intptr_t) val;
 
-  int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer");
+  /* Only re-raise the old pending exception if a new one hasn't come along to
+     supersede it.  */
+  SHOW_OLD_TROUBLE ();
 
-  val_mth = (*gdk_env)->GetMethodID(gdk_env, int_class, "intValue", "()I");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer.<intValue>");
+done:
 
-  int_val = (*gdk_env)->CallIntMethod(gdk_env, lcl_obj, val_mth);
-  MAYBE_RETHROW(gdk_env, "cannot get thread local value");
+  if (TRACE_API_CALLS)
+    tracing (" ==> %p\n", thread_specific_data);
 
-  return (gpointer) int_val;
+  return thread_specific_data;
 }
 
-/*  Set this thread's value for a thread-local key.  This is simply
- * ThreadLocal.set for us.
+/* Set this thread's value for a thread-local key.  This is simply
+ * ThreadLocal.set() for us.
  */
-static void g_private_set_jni_impl (GPrivate *private, gpointer data) {
-  jclass lcl_class, int_class;
-  jobject lcl_obj;
-  JNIEnv *gdk_env;
-  jmethodID new_int, set_mth;
+static void
+private_set_jni_impl (GPrivate * gkey, gpointer thread_specific_data)
+{
+  JNIEnv *env;
+  jobject val_wrapper;
+  jobject keyObj = (jobject) gkey;
+
+
+  if (TRACE_API_CALLS)
+    tracing ("private_set_jni_impl(keyObj=%p, thread_specific_data=%p)",
+            keyObj, thread_specific_data);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
+
+  /* We are just going to always use a Java long to represent a C pointer.
+     Otherwise all of the code would end up being conditionalized for various
+     pointer sizes, and that seems like too much of a hassle, in order to save
+     a paltry few bytes, especially given the horrendous overhead of JNI in
+     any case. 
+   */
+
+  val_wrapper = (*env)->NewObject (env, long_class, long_ctor,
+                                  (jlong) (intptr_t) thread_specific_data);
+  if ( ! val_wrapper )
+    {
+      BROKEN (env, "cannot create a java.lang.Long");
+      goto done;
+    }
+
+  /* At this point, we now have set lcl_obj as a numeric class that wraps
+     around the thread-specific data we were given. */
+  (*env)->CallVoidMethod (env, keyObj, threadlocal_set_mth, val_wrapper);
+  if (MAYBE_BROKEN (env, "cannot set thread local value"))
+    goto done;
+
+  SHOW_OLD_TROUBLE ();
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID\n");
+}
+
+
+/** Create an object of type gnu.java.awt.peer.gtk.GThreadNativeMethodRunner.
+    Run it.
+
+    We need to create joinable threads.  We handle the notion of a joinable
+    thread by determining whether or not we are going to maintain a permanent
+    hard reference to it until it croaks.
+
+    Posix does not appear to have a Java-like concept of daemon threads, where
+    the JVM will exit when there are only daemon threads running.
+
+    Error handling: 
+
+    To quote from the glib guide:
+       "GError should only be used to report recoverable runtime errors, never
+        to report programming errors."   
+
+    So how do we consider the failure to create a thread?  Well, each of the
+    failure cases in this function are discussed, and none of them are really
+    recoverable.
+
+    The glib library is really designed so that you should fail
+    catastrophically in case of "programming errors".  But they don't seem to
+    have thoroughly considered cases like resource exhaustion, at least not
+    with respect to these functions.  The only error defined for the GThread
+    functions is G_THREAD_ERROR_AGAIN, and that for thread_create.  Most of 
these GThread functions could
+    fail if we run out of memory, for example.  And if we run out of Java
+    memory, we are almost certainly not going to get any more.  Never the
+    less, it does not seem appropriate for 
+*/
+static void
+thread_create_jni_impl (GThreadFunc        func,
+                       gpointer            data,
+                       gulong              stack_size __attribute__((unused)),
+                       gboolean            joinable,
+                       gboolean            bound __attribute__((unused)),
+                       GThreadPriority     gpriority,
+                       /* This prototype is horrible.  threadIDp is actually
+                          a gpointer to the thread's thread-ID.  Which is, 
+                          of course, itself a gpointer-typed value.  Ouch. */ 
+                       gpointer            threadIDp, 
+                       /* Do not touch the GError stuff unless you have
+                          RECOVERABLE trouble.   There is no recoverable
+                          trouble in this implementation.  */ 
+                       GError        **errorp) 
+{
+  JNIEnv *env;
+  jboolean jjoinable = joinable;
+  jobject newThreadObj;
+  gpointer threadID;           /* to be filled in */
+
+  if (TRACE_API_CALLS)
+    tracing ("thread_create_jni_impl(func=%p, data=%p, joinable=%s,"
+            " threadIDp=%p, *(int *) threadIDp = %d)",
+            (void *) func, data, joinable ? "TRUE" : "FALSE",
+            threadIDp, *(int *) threadIDp);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    {
+      /*  The failed call to setup the cache is certainly not recoverable;
+         not appropriate for G_THREAD_ERROR_AGAIN.  */
+      *(gpointer *) threadIDp = NULL;
+      goto done;
+    }
+  HIDE_OLD_TROUBLE (env);
+
+  /* If a thread is joinable, then notify its constructor.  The constructor
+     will enter a hard reference for it, and the hard ref. won't go away until
+     the thread has been joined. */
+  newThreadObj = 
+    (*env)->NewObject (env, runner_class, runner_ctor, 
+                       (jlong) (intptr_t) func, (jlong) (intptr_t) data, 
+                       jjoinable);
+  if ( ! newThreadObj )
+    {
+      BROKEN (env, "creating a new thread failed in the constructor");
+      *(gpointer *) threadIDp = NULL;
+      /*  The failed call to the constructor does not throw any errors such
+         that G_THREAD_ERROR_AGAIN is appropriate.  No other recoverable
+         errors defined.  Once again, we go back to the VM. */
+      goto done;
+    }
+
+  if (threadObj_set_priority (env, newThreadObj, gpriority) < 0)
+    {
+      *(gpointer *) threadIDp = NULL;
+      /* None of these possible exceptions from Thread.setPriority() are
+        recoverable, so they are not appropriate for EAGAIN.  So we should
+        fail. */  
+      goto done;
+    }
+
+  (*env)->CallVoidMethod (env, runner_class, runner_start_mth);
+
+  if (MAYBE_BROKEN (env, "starting a new thread failed"))
+    {
+      *(gpointer *) threadIDp = NULL;
+      /* The only exception Thread.start() throws is
+        IllegalStateException.  And that would indicate a programming error. 
+
+        So there are no situations such that G_THREAD_ERROR_AGAIN would be
+        OK. 
+
+        So, we don't use g_set_error() here to perform any error reporting.
+        */
+      goto done;
+    }
+
+  threadID = getThreadIDFromThread (env, newThreadObj);
+
+  *(gpointer *) threadIDp = threadID;
+  SHOW_OLD_TROUBLE ();
+
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> (threadID = %p) \n", threadID);
+}
+
+
+/* Wraps a call to g_thread_yield. */
+static void
+thread_yield_jni_impl (void)
+{
+  JNIEnv *env;
+
+  if (TRACE_API_CALLS)
+    tracing ("thread_yield_jni_impl()");
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
+
+  (*env)->CallStaticVoidMethod (env, thread_class, thread_yield_mth);
+  if (MAYBE_BROKEN (env, "Thread.yield() failed"))
+    goto done;
+
+  SHOW_OLD_TROUBLE ();
+
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID\n");
+}
+
+
+static void
+thread_join_jni_impl (gpointer threadID)
+{
+  JNIEnv *env;
+  jobject threadObj = NULL;
+
+  if ( TRACE_API_CALLS )
+    tracing ("thread_join_jni_impl(threadID=%p) ", threadID);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
+
+  threadObj = getThreadFromThreadID (env, threadID);
+  if ( ! threadObj )           /* Already reported with BROKEN  */
+    goto done;
+
+  (*env)->CallVoidMethod (env, threadObj, thread_join_mth);
+  if (MAYBE_BROKEN (env, "Thread.join() failed"))
+    goto done;
+
+
+  (*env)->CallStaticVoidMethod
+    (env, runner_class, runner_deRegisterJoinable_mth, threadObj);
+  if (MAYBE_BROKEN (env, "Thread.deRegisterJoinableThread() failed"))
+    goto done;
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  SHOW_OLD_TROUBLE ();
 
-  int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer");
+done:
+  DELETE_LOCAL_REF (env, threadObj);
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID \n");
+}
+
+/* Terminate the current thread.  Unlike pthread_exit(), here we do not need
+   to bother with a return value or exit value for the thread which is about
+   to croak.  (The gthreads abstraction doesn't use it.)  However, we *do*
+   need to bail immediately.  We handle this with Thread.stop(), which is
+   a deprecated method.
+
+   It's deprecated since we might leave objects protected by monitors in
+   half-constructed states on the way out -- Thread.stop() throws a
+   ThreadDeath exception, which is usually unchecked.  There is no good
+   solution that I can see. */ 
+static void
+thread_exit_jni_impl (void)
+{
+  JNIEnv *env;
+  jobject this_thread;
+
+  if (TRACE_API_CALLS)
+    tracing ("thread_exit_jni_impl() ");
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+
+  HIDE_OLD_TROUBLE (env);
+
+  this_thread = (*env)->
+    CallStaticObjectMethod (env, thread_class, thread_current_mth);
+
+  if ( ! this_thread )
+    {
+      BROKEN (env, "cannot get current thread");
+      goto done;
+    }
+
+  (*env)->CallVoidMethod (env, this_thread, thread_stop_mth);
+  if (MAYBE_BROKEN (env, "cannot call Thread.stop() on current thread"))
+    goto done;
+
+  SHOW_OLD_TROUBLE ();
+
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID \n");
+}
+
+
+/* Translate a GThreadPriority to a Java priority level. */
+static jint
+javaPriorityLevel (GThreadPriority priority)
+{
+  /* We have these fields in java.lang.Thread to play with:
 
-  new_int = (*gdk_env)->GetMethodID(gdk_env, int_class, "<init>", "(I)V");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer.<init>");
+     static int MIN_PRIORITY     The minimum priority that a thread can have.
+     static int NORM_PRIORITY    The default priority that is assigned to a 
+     thread.
+     static int MAX_PRIORITY     The maximum priority that a thread can have.
+   */
+  static const jint minJPri    = GThreadNativeMethodRunner_MIN_PRIORITY;
+  static const jint normJPri   = GThreadNativeMethodRunner_NORM_PRIORITY;
+  static const jint maxJPri    = GThreadNativeMethodRunner_MAX_PRIORITY;
+
+  switch (priority)
+    {
+    case G_THREAD_PRIORITY_LOW:
+      return minJPri;
+      break;
+
+    default:
+      assert_not_reached ();
+      /* Deliberate fall-through; shut up GCC warnings.  Apparently glib (at
+         least the installation on my machine) doesn't tell GNU C that
+         assert_not_reached() will never return.  */
+    case G_THREAD_PRIORITY_NORMAL:
+      return normJPri;
+      break;
+
+    case G_THREAD_PRIORITY_HIGH:
+      return (normJPri + maxJPri) / 2;
+      break;
+
+    case G_THREAD_PRIORITY_URGENT:
+      return maxJPri;
+      break;
+    }
+}
+
+
+/** It would be safe not to implement this, according to the JNI docs, since
+    not all platforms do thread priorities.  However, we might as well
+    provide the hint for those who want it. 
+*/
+static void
+thread_set_priority_jni_impl (gpointer gThreadID, GThreadPriority gpriority)
+{
+  jobject threadObj = NULL;
+  JNIEnv *env;
+
+  if (TRACE_API_CALLS)
+    tracing ("thread_set_priority_jni_impl(gThreadID=%p, gpriority = %u) ",
+            gThreadID, gpriority);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
 
-  lcl_obj = (*gdk_env)->NewObject(gdk_env, int_class, new_int, (jint)data);
-  MAYBE_RETHROW(gdk_env, "cannot create an Integer");
+  if (setup_cache (env) < 0)
+    goto done;
 
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
+  HIDE_OLD_TROUBLE (env);
 
-  set_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "set", 
"(Ljava/lang/Object;)V");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<set>");
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)private, set_mth, lcl_obj);
-  MAYBE_RETHROW(gdk_env, "cannot set thread local value");
+  threadObj = getThreadFromThreadID (env, gThreadID);
+  if ( ! threadObj)            /* Reported with BROKEN already.  */
+    goto done;
+
+  if (threadObj_set_priority (env, threadObj, gpriority))
+    goto done;
+
+  SHOW_OLD_TROUBLE ();
+
+done:
+  DELETE_LOCAL_REF (env, threadObj);
+
+  if (TRACE_API_CALLS)
+    tracing (" ==> VOID\n");
 }
 
 
+/** It would be safe not to implement this, according to the JNI docs, since
+    not all platforms do thread priorities.  However, we might as well
+    provide the hint for those who want it.
+
+    -1 on failure, 0 on success. */
+static int
+threadObj_set_priority (JNIEnv * env, jobject threadObj,
+                       GThreadPriority gpriority)
+{
+  jint javaPriority = javaPriorityLevel (gpriority);
+  (*env)->CallVoidMethod (env, threadObj, thread_setPriority_mth,
+                         javaPriority);
+  return MAYBE_BROKEN (env, "Thread.setPriority() failed");
+}
+
+
+/** Return the result of Thread.currentThread(), a static method. */
+static void
+thread_self_jni_impl (/* Another confusing glib prototype.  This is
+                        actually  a gpointer to the thread's thread-ID.
+                        Which is, of course, a gpointer. */
+                     gpointer my_thread_IDp)
+{
+  JNIEnv *env;
+  jobject this_thread;
+  gpointer my_threadID;
+
+  if (TRACE_API_CALLS)
+    tracing ("thread_self_jni_impl(my_thread_IDp=%p)", my_thread_IDp);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+
+  if (setup_cache (env) < 0)
+    return;
+
+  HIDE_OLD_TROUBLE (env);
+
+  this_thread = (*env)->
+    CallStaticObjectMethod (env, thread_class, thread_current_mth);
+  if (! this_thread )
+    {
+      BROKEN (env, "cannot get current thread");
+      my_threadID = NULL;
+      goto done;
+    }
+
+  my_threadID = getThreadIDFromThread (env, this_thread);
+  SHOW_OLD_TROUBLE ();
+
+done:
+  if (TRACE_API_CALLS)
+    tracing (" ==> (my_threadID = %p) \n", my_threadID);
+
+  *(gpointer *) my_thread_IDp = my_threadID;
+}
+
+
+static gboolean
+thread_equal_jni_impl (gpointer thread1, gpointer thread2)
+{
+  JNIEnv *env;
+
+  gpointer threadID1 = *(gpointer *) thread1;
+  gpointer threadID2 = *(gpointer *) thread2;
+
+  jobject thread1_obj = NULL;
+  jobject thread2_obj = NULL;
+  gboolean ret;
+
+  if (TRACE_API_CALLS)
+    tracing ("thread_equal_jni_impl(threadID1=%p, threadID2=%p)",
+            threadID1, threadID2);
+
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    {
+      ret = FALSE;             /* what is safer?  We really don't ever want
+                                  to return from here.  */
+      goto done;
+    }
+
+  HIDE_OLD_TROUBLE (env);
+  thread1_obj = getThreadFromThreadID (env, threadID1);
+  thread2_obj = getThreadFromThreadID (env, threadID2);
+
+  ret = (*env)->CallBooleanMethod (env, thread1_obj,
+                                  thread_equals_mth, thread2_obj);
+
+  if (MAYBE_BROKEN (env, "Thread.equals() failed"))
+    {
+      ret = FALSE;
+      goto done;
+    }
+
+  SHOW_OLD_TROUBLE ();
+
+
+done:
+  DELETE_LOCAL_REF (env, thread1_obj);
+  DELETE_LOCAL_REF (env, thread2_obj);
+
+  if (TRACE_API_CALLS)
+    tracing (" ==> %s\n", ret ? "TRUE" : "FALSE");
+
+  return ret;
+}
+
+
+
+
 /************************************************************************/
 /* GLIB interface                                                      */
 /************************************************************************/
 
 /* set of function pointers to give to glib. */
-GThreadFunctions g_thread_jni_functions =
-{
-  g_mutex_new_jni_impl,              /* mutex_new */
-  g_mutex_lock_jni_impl,      /* mutex_lock */
-  g_mutex_trylock_jni_impl,   /* mutex_try_lock */
-  g_mutex_unlock_jni_impl,    /* mutex_unlock */
-  g_mutex_free_jni_impl,      /* mutex_free */
-  g_cond_new_jni_impl,        /* cond_new */
-  g_cond_signal_jni_impl,     /* cond_signal */
-  g_cond_broadcast_jni_impl,  /* cond_broadcast */
-  g_cond_wait_jni_impl,       /* cond_wait */
-  g_cond_timed_wait_jni_impl, /* cond_timed_wait */
-  g_cond_free_jni_impl,       /* cond_free */
-  g_private_new_jni_impl,     /* private_new */
-  g_private_get_jni_impl,     /* private_get */
-  g_private_set_jni_impl,     /* private_set */
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL,
-  NULL
+GThreadFunctions portable_native_sync_jni_functions = {
+  mutex_new_jni_impl,          /* mutex_new */
+  mutex_lock_jni_impl,         /* mutex_lock */
+  mutex_trylock_jni_impl,      /* mutex_trylock */
+  mutex_unlock_jni_impl,       /* mutex_unlock */
+  mutex_free_jni_impl,         /* mutex_free */
+  cond_new_jni_impl,           /* cond_new */
+  cond_signal_jni_impl,                /* cond_signal */
+  cond_broadcast_jni_impl,     /* cond_broadcast */
+  cond_wait_jni_impl,          /* cond_wait */
+  cond_timed_wait_jni_impl,    /* cond_timed_wait */
+  cond_free_jni_impl,          /* cond_free */
+  private_new_jni_impl,                /* private_new */
+  private_get_jni_impl,                /* private_get */
+  private_set_jni_impl,                /* private_set */
+  thread_create_jni_impl,      /* thread_create */
+  thread_yield_jni_impl,       /* thread_yield */
+  thread_join_jni_impl,                /* thread_join */
+  thread_exit_jni_impl,                /* thread_exit */
+  thread_set_priority_jni_impl,        /* thread_set_priority */
+  thread_self_jni_impl,                /* thread_self */
+  thread_equal_jni_impl,       /* thread_equal */
 };
+
 
+/* Keep c-font-lock-extra-types in alphabetical order. */
+/* Local Variables: */
+/* c-file-style: "gnu" */
+/* c-font-lock-extra-types: ("\\sw+_t" "gboolean" "GError" "gpointer"
+   "GPrivate" "GThreadFunc" "GThreadFunctions" "GThreadPriority" 
+   "gulong" 
+   "JNIEnv" 
+   "jboolean" "jclass" "jint" "jlong" "jobject" "jstring" "jthrowable" ) */
+/* End: */
Index: NEWS
===================================================================
RCS file: /cvsroot/classpath/classpath/NEWS,v
retrieving revision 1.39
diff -I*.class -u -r1.39 NEWS
--- NEWS        2 May 2004 13:47:32 -0000       1.39
+++ NEWS        26 May 2004 18:34:55 -0000
@@ -1,3 +1,13 @@
+New in release 0.10 (in preparation)
+
+* Java primitives can once again be used to support AWT native
+  threading; this had been broken since Classpath release 0.06, when
+  we upgraded to GTK+2.  This is enabled with the
+  --enable-portable-native-sync configure option.
+* We intend that the next release of GNU Classpath will require the VM 
+  to provide JNI 1.2.  If there are serious problems with this, please
+   raise them now. 
+
 New in release 0.09 (2004/02/02)
 
 * Includes updated GNU JAXP version from 2004-02-01.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]