commit-classpath
[Top][All Lists]
Advanced

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

portable-native-sync-5.patch


From: Steven Augart
Subject: portable-native-sync-5.patch
Date: Fri, 21 May 2004 15:25:13 -0400
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7b) Gecko/20040421

I first submitted this patch eleven days ago. I've gotten positive feedback and only minor suggestions on previous versions of it.

This version cleans up the last gripes I can think of, with the possible exception of differences of opinion over what should go into a ChangeLog entry. I believe that the earlier version of this patch (portable-native-sync-4.patch) is already in Kaffe's CVS tree, driving Kaffe's AWT.

I intend to commit this to Classpath on Monday, May 24th, or Tuesday, May 25th, unless I receive either additional feedback on it or a request for more time from someone who has taken on the personal commitment to review it.

Sincerely,

--Steve Augart


Changes to portable-native-sync-4.patch, as discussed in 
the ChangeLog entry:

* One bug fix (volatile)
* An efficiency improvement
* Match glib error handling 
* And a number of maintainability improvements 
  (formatting changes and comments)

Please add this to NEWS:
   
* 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.

And please commit it with the following ChangeLog entries:

2004-05-21  Steven Augart  <address@hidden>

        * gnu/java/awt/peer/gtk/GThreadMutex.java: The potentialLockers
        field is now volatile.  The earlier version could have lead to
        incorrect behavior on SMP systems.

        * gnu/native/jni/gtk-peer/gthread-jni.c: Ran it through GNU
        indent.  Modified macro defs so that they, too, meet GNU style.
        
        Updated some comments.  Better documentation on our error
        handling system.

        Reset it so that we always handle errors in the "glib" way.  The
        glib way is to err out (dump core or exit) in case of a serious
        functionality problem, such as a broken threads implementation.
        Turned on DIE_IF_BROKEN.  Removed the three calls to
        g_set_error(); none of them were recoverable situations, so they
        were not appropriate for g_set_error().
        
        Removed the #if PORTABLE_NATIVE_SYNC I'd added that wraps this
        whole file.  We do want to compile this with the rest of classpath
        after all.  It is going to be a run-time option.
        
        (thread_create_jni_impl) We no longer attempt to use g_set_error()
        to report trouble.  Discussed in comments.

        * gnu/java/awt/peer/gtk/GThreadMutex.java,
          gnu/native/jni/gtk-peer/gthread-jni.c: 
        
        GThreadMutex.lock no longer exists; the GThreadMutex object is now
        its own primary lock.  Eliminates one JNI call per invocation of
        the mutex_lock and mutex_unlock functions.
        

2004-05-21  Steven Augart  <address@hidden>

        Patch sent 2004-05-10:
        
        --portable-native-sync implemented for GTK2, by adding the missing
        old implementations to the GThreadFunctions structure.  Fully
        implemented mutex_trylock(), which had been a stub before.  Some
        refactoring and cleanup of previous implementation.
        
        * 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:
        
        Renamed g_thread_jni_functions to
        portable_native_sync_jni_functions, in the interest of not
        polluting the g_ namespace.
        
        Rename gdk_vm to the_vm, so we do not appropriate portions of the
        gdk_ namespace (or at least, so we don't do it in code that I've
        taken responsibility for!)
        
        * native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c: 
        Removed redundant definition of gdk_vm (now the_vm).
        
        (gtkInit) Got rid of a progress message to stdout that had crept
        in somehow; probably old debugging code.
        
        Use g_malloc() instead of a (buggy; incorrect) malloc() call
        without a check for NULL!  Similar substitution with g_free().

        Fix a const assignment bug where we had been assigning a const string
        to a regular "char *" pointer.

        It's OK to give g_free() a NULL pointer.  Exploit this fact.
        
        * native/jni/gtk-peer/gthread-jni.c: Major refactoring.
        
        Implemented missing functions for GTK2.

        Added debugging #defines so we can get progress reports while we run.
        
        Added an #ifdef surrounding the whole file so we don't build it
        and include it in the library on configurations that will never
        use it. 
        
        Removed the g_ prefix from all of our own functions, to avoid
        namespace pollution and possible confusion.
        
        Added TRACE_API_CALLS option.
        
        Added the EXPLAIN_TROUBLE option, to generate messages to stderr
        in addition to throwing or rethrowing an exception.  Also added
        EXPLAIN_BROKEN and EXPLAIN_BADLY_BROKEN, for progressively worse
        cases. 
        
        Added the DIE_IF_BADLY_BROKEN option, enabled by default.  Die if
        we can't throw an exception.  The weaker new DIE_IF_BROKEN option
        is not enabled by default.

        Added DELETE_LOCAL_REFS option and code that polices the quantity
        of local references.  It is not enabled by default -- Jikes RVM
        does not have a fixed limit on the number of local refs -- but it
        might need to be for some other VM's JNI implementation.
        
        Reformatted the code largely to GNU standards.  I probably have
        not gotten every spot.
        
        Rewrote all of the global-reference code so that we never
        call malloc or g_malloc().  Eliminated a layer of indirection
        where we were returning a reference to a global reference; that
        extra layer would only be necessary if a jobject did not fit into
        a machine pointer (void *).  But it is guaranteed to always do so.

        If any of the functions encounter errors, they now detect that
        fact and clean up and exit, rather than throwing a Java exception,
        but then continuing to run and causing further errors to cascade.
        Well, that isn't perfect yet, but it *is* a lot better than it
        was.

        (cond_timed_wait_jni_impl) Went from millisecond to microsecond
        resolution.
        
        (setup_cache) Cache all methods and classes we will use throughout
        the code.
        
        (mutex_trylock_jni_impl) Used to be a stub; now properly
        implemented.

        (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.
        That could have lead to deadlock.
        
        * gnu/java/awt/peer/gtk/GThreadMutex.java: New file.  Implements a
        mutex object that supports glib's GThreadFunctions.mutex_trylock
        operation.
        
        * 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:
        New files.
        
        Implement the Java portions of glib's
        GThreadFunctions.thread_create operation.

        Provide the utility methods threadToThreadID and threadIDToThread,
        which are used in several places in the GThreadFunctions code.

--- /dev/null   2003-01-30 10:24:37.000000000 +0000
+++ gnu/java/awt/peer/gtk/GThreadMutex.java     2004-05-20 08:17:41.000000000 
+0000
@@ -0,0 +1,105 @@
+/* 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
+
+    
+*/
+   
+public 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>.
+
+      We only access this field from C 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).
+  */     
+  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-12 
20:24:15.000000000 +0000
@@ -0,0 +1,219 @@
+/* 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.  Otherwise, Eclipse and other systems
+    that generate more than 2^16 threads would place a burden on the VM.
+*/
+
+public 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. */ 
+  private static WeakReference[] threads 
+  = new WeakReference[17];    /* 17 is just a starting point.  Any number will 
+                                 do, including zero. */ 
+
+  /** This is a private method, only 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[] nxt = new WeakReference[threads.length * 2];
+      for (int j = 0; j < threads.length; ++j) {
+        nxt[j] = threads[j];
+      }
+      // NXT is now the expanded array.  It is the new THREADS.
+      threads = nxt;
+    }
+    threads[i] = new WeakReference(t);
+    return i;
+  }
+  
+  /** This is O(n/2) lookup of threads by thread ID.
+
+      TODO We could use a WeakHashMap to store this info instead; the current
+      implementation is grossly inefficient if we have a huge number of
+      threads.  I don't think the AWT code generates a huge number of them,
+      though, so we are probably safe.
+
+      Consider that not all threads will have a threadID, because not all
+      threads were launched by GThreadNativeMethodRunner.
+      This is a general routine for handling all threads, including the VM's
+      main thread, if appropriate.
+  */ 
+  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 bail.  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.  Giving a value higher than the top
+       issued one, for instance, would be a good use of this.
+       
+       TODO: Consider disabling all of this error-checking; it probably
+       slows down the implementation.  We could just return NULL*/
+    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 permanent 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 g_thread_join(threadID).  Joinable
+      threads have to be explicitly joined before they are allowed to go away
+      completely.
+  */
+  static final Set joinable = Collections.synchronizedSet(new HashSet());
+  
+  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);
+  }
+
+//   private int registerSelf() {
+//     return registerThread(self);
+//   }
+
+  
+}
+
+// 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   21 May 2004 
18:56:19 -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   21 May 2004 18:56:19 -0000
@@ -40,142 +40,1109 @@
 /************************************************************************/
 
 /*
- * 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
  *
  * 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   0  /* 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. */
+
+#define  HAVE_JNI_VERSION_1_2   0 /* Assume we don't.  We could
+                                    dynamically check for this.
+
+                                    TODO This code hasn't been tested 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 does.  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)
+
+/* Needed for exception-wrapping */
+#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.  That is
+   *highly* unlikely, but perhaps not absolutely impossible, since we have no
+   way of knowing what is really going on.
+
+   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, since HIDE_OLD_TROUBLE includes declarations.
+
+   2) They chew up more time and resources.  
+
+   3) There does not ever seem to be old trouble -- the assertion that I
+      replaced HIDE_OLD_TROUBLE with never goes off.
+
+   4) I'd put them in because I hadn't wanted to confuse the error reporting
+      while debugging, by possibly showing old exceptions.  That is no longer
+      a problem. 
+
+   HIDE_OLD_TROUBLE has to be called after the local variable (env) is
+   already set.
+
+*/
+#if 1
+#define HIDE_OLD_TROUBLE(env)                          \
+    assert ( NULL == (*env)->ExceptionOccurred (env) )
+
+#define SHOW_OLD_TROUBLE()     \
+    assert ( NULL == (*env)->ExceptionOccurred (env) )
+#else
+/* These only work with C compilers that let you mix declarations and
+   statements, to wit, gcc and C '99.   Further, they assume that these
+   functions might be called with an exception pending.  That should never be
+   the case ... but it could be!   */
+#define HIDE_OLD_TROUBLE(env)                                  \
+   jthrowable savedTrouble = (*env)->ExceptionOccurred (env);  \
+   (*env)->ExceptionClear (env);
+
+/* 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. */
+#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
+   for throw().  We do this independently of the others, since we need
+   to have this cache set up first, so that we can then report errors
+   properly. 
+
+*/
+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;
+    }
+
+  assert (runtimeException_class);
+
+  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.
+
+   --Steven Augart
+*/
+
+/* All of these are cached classes and method IDs: */
+/* java.lang.Object */
+jclass obj_class;              /* java.lang.Object */
+jmethodID obj_ctor;            /* no-arg Constructor for java.lang.Object */
+jmethodID obj_notify_mth;      /* java.lang.Object.notify() */
+jmethodID obj_notifyall_mth;   /* java.lang.Object.notifyall() */
+jmethodID obj_wait_mth;                /* java.lang.Object.wait() */
+jmethodID obj_wait_nanotime_mth; /* java.lang.Object.wait(JI) */
+
+/* GThreadMutex and its methods */
+jclass mutex_class;
+jmethodID mutex_ctor;
+jfieldID mutex_lockForPotentialLockers_fld;
+jfieldID mutex_potentialLockers_fld;
+
+/* java.lang.Thread and its methods*/
+jclass thread_class;           /* java.lang.Thread */
+jmethodID thread_current_mth;  /* Thread.currentThread() */
+jmethodID thread_equals_mth;   /* Thread.equals() */
+jmethodID thread_join_mth;     /* Thread.join() */
+jmethodID thread_setPriority_mth; /* Thread.setPriority() */
+jmethodID thread_stop_mth;     /* Thread.stop() */
+jmethodID thread_yield_mth;    /* Thread.yield() */
+
+/* java.lang.ThreadLocal and its methods */
+jclass threadlocal_class;      /* java.lang.ThreadLocal */
+jmethodID threadlocal_ctor;    /* Its constructor */
+jmethodID threadlocal_set_mth; /* ThreadLocal.set() */
+jmethodID threadlocal_get_mth; /* ThreadLocal.get() */
+
+/* java.lang.Long and its methods */
+jclass long_class;             /* java.lang.Long */
+jmethodID long_ctor;           /* constructor for it: (J) */
+jmethodID long_longValue_mth;  /* longValue()J */
+
+
+/* GThreadNativeMethodRunner */
+jclass runner_class;
+jmethodID runner_ctor;
+jmethodID runner_threadToThreadID_mth;
+jmethodID runner_threadIDToThread_mth;
+jmethodID runner_deRegisterJoinable_mth;
+jmethodID runner_start_mth;    /* Inherited Thread.start() */
+
+
+/* java.lang.InterruptedException */
+jclass interrupted_exception_class;
+
+
+
+
+/* Return a negative value if there was trouble during initialization.  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;
+    }
+  assert (thread_class);
+
+  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;
+    }
+  assert (threadlocal_class);
+
+
+  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;
+    }
+  assert (long_class);
+
+
+  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 */
+  runner_class
+    = (*env)->FindClass (env,
+                        "gnu/java/awt/peer/gtk/GThreadNativeMethodRunner");
+  if ( ! runner_class )
+    {
+      BROKEN (env,
+             "cannot find gnu.java.awt.peer.gtk.GThreadNativeMethodRunner");
+      return initialized = -1;
+    }
+
+  /* Pin it down. */
+  runner_class = (jclass) (*env)->NewGlobalRef (env, runner_class);
+  if (!runner_class)
+    {
+      BROKEN (env,
+             "Cannot get a global reference to the class 
GThreadNativeMethodRunner");
+      return initialized = -1;
+    }
+  assert (runner_class);
+
+  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;
+    }
+  assert (interrupted_exception_class);
+
+#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;
+    }
 
-  return obj;
+  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 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 +1151,722 @@
 /************************************************************************/
 
 /* 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 the pointer equivalent -1 on all failures.
+
+   A null thread means we call NEW_BROKEN().
+   For other errors, return -1 on error and call BROKEN.  */
+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. */
+  jobject lockObj;             /* The real lock we use. */
+};
+
+/* 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 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;
 }
 
-/* Create a mutex, which is a java.lang.Object for us */
-static GMutex *g_mutex_new_jni_impl (void) {
-  return (GMutex*) allocatePlainObject();
+
+/* 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);
+  DELETE_LOCAL_REF (env, mcache->lockObj);
+}
+
+/* -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;
+}
+
+/*** 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;
+
+  if (TRACE_API_CALLS)
+    tracing ("mutex_lock_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;
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  HIDE_OLD_TROUBLE (env);
 
-  takeLock(gdk_env, mutex);
+  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 error 
code;
+                                                                  nothing to 
be done.
+                                                                */
+      goto done;
+    }
+
+  assert (potentialLockers == 0);
+  /* This is guaranteed not to block, since we've done our testing */
+  if (ENTER_MONITOR (env, mcache.lockObj))
+    goto done;
+
+
+  /* We have the monitor.  Record that fact. */
+  potentialLockers = 1;                /* Was zero. */
+  (*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 it! */
+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);
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+  if (setup_cache (env) < 0)
+    goto done;
+  HIDE_OLD_TROUBLE (env);
 
-  releaseLock(gdk_env, mutex);
+  assert (mutexObj);
+
+  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;
+
+  (*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);
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, signal_mth);
-  MAYBE_RETHROW(gdk_env, "cannot signal mutex");
+  SHOW_OLD_TROUBLE ();
 
-  releaseLock(gdk_env, cond);
+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;
+    }
+
+  EXIT_MONITOR (env, condObj);
 
-  releaseLock(gdk_env, cond);
+  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);
+  (*env)->CallVoidMethod (env, condObj, obj_wait_nanotime_mth,
+                         time_millisec, time_nanosec);
 
-  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");
+  /* 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 +1874,609 @@
 /* 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 worst thing here, though, is that 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 g_thread functions is G_THREAD_ERROR_AGAIN.  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 certainloy not going to get any
+    more.  
+*/
+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;
+         can't use G_THREAD_ERROR_AGAIN here!  */
+      *(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.
+        */
+#if 0
+      g_set_error(errorp, G_THREAD_ERROR, G_THREAD_ERROR_AGAIN, 
+                 "starting the new thread failed");
+#endif
+      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;
+
+  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;
+
+  SHOW_OLD_TROUBLE ();
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+done:
+  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;
+    }
+  assert (this_thread);
+
+  (*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:
+
+     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;
+  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);
+
+  if (setup_cache (env) < 0)
+    goto done;
+
+  HIDE_OLD_TROUBLE (env);
+
+
+  threadObj = getThreadFromThreadID (env, gThreadID);
+  if ( ! threadObj)            /* Reported with BROKEN already.  */
+    goto done;
 
-  int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer");
+  if (threadObj_set_priority (env, threadObj, gpriority))
+    goto done;
 
-  new_int = (*gdk_env)->GetMethodID(gdk_env, int_class, "<init>", "(I)V");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer.<init>");
+  SHOW_OLD_TROUBLE ();
+
+done:
+  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;
+}
 
-  lcl_obj = (*gdk_env)->NewObject(gdk_env, int_class, new_int, (jint)data);
-  MAYBE_RETHROW(gdk_env, "cannot create an Integer");
 
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
+static gboolean
+thread_equal_jni_impl (gpointer thread1, gpointer thread2)
+{
+  JNIEnv *env;
+
+  gpointer threadID1 = *(gpointer *) thread1;
+  gpointer threadID2 = *(gpointer *) thread2;
+
+  jobject thread1_obj, thread2_obj;
+  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:
 
-  set_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "set", 
"(Ljava/lang/Object;)V");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<set>");
+  if (TRACE_API_CALLS)
+    tracing (" ==> %s\n", ret ? "TRUE" : "FALSE");
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)private, set_mth, lcl_obj);
-  MAYBE_RETHROW(gdk_env, "cannot set thread local value");
+  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: */

reply via email to

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