classpath
[Top][All Lists]
Advanced

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

--portable-native-sync; possibly final patch available


From: Steven Augart
Subject: --portable-native-sync; possibly final patch available
Date: Thu, 13 May 2004 10:48:00 -0400
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8a) Gecko/20040504

I've sent a possibly-final patch for the --portable-native-sync stuff to commit-classpath. I'm mentioning this becaues I know that Jim Huang was waiting for it so he could fix Kaffe's AWT code. Jim, I think this one is safe to use. I've beaten on it for a while.

--Steve A


Steven Augart wrote:
I have a working version of the --portable-native-sync stuff and have attached a temporary patch, in case someone else actually needs it. I'd appreciate any feedback. I'll stress-test it a bit more this week before submitting a final version with a ChangeLog entry, but with it in place, Jikes RVM runs AWT code again.

Performance is not very good at the moment; I hope to improve it before submitting a final version. However, it does seem to work correctly.

Please write both to me and to the classpath list if you have any comments on the code.


------------------------------------------------------------------------

This patch fixes the --portable-native-sync support in GNU Classpath.
--- /dev/null   2003-01-30 10:24:37.000000000 +0000
+++ gnu/java/awt/peer/gtk/GThreadMutex.java     2004-05-08 17:40:59.000000000 
+0000
@@ -0,0 +1,68 @@
+/* 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 to support posix's pthread_mutex_trylock() semantics, which
+    are needed for the function vector that is passed to the g_thread
+    initialization function. */
+ +/************************************************************************/
+/* Header                                                              */
+/************************************************************************/
+
+public class GThreadMutex {
+  Object lock;                 // The real lock.
+
+  int potentialLockers;                /* Might "lock" be locked?  Is anyone 
waiting
+                                   to get that lock?  How long is the queue? */
+
+  Object lockForPotentialLockers;      // For checking the value of isLocked.
+
+  GThreadMutex() {
+    lock = new Object();
+    potentialLockers = 0;
+    lockForPotentialLockers = new Object();
+  }
+}
--- /dev/null   2003-01-30 10:24:37.000000000 +0000
+++ gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java        2004-05-08 
14:45:38.000000000 +0000
@@ -0,0 +1,164 @@
+/* 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 by gthread-jni.c */
+
+public class GThreadNativeMethodRunner extends Thread {
+ +
+  private final long funcPtr;   /* The C function pointer that was passed to
+                                  g_thread_create().  Specifically, this is
+                                  an object of C type  void *(*funcPtr)().  */
+
+  /** The argument we'll pass. */
+  private final long funcArg;
+ + + // private static long nextThreadID = 0; + + // final long threadID = ++nextThreadID; // ID of this thread.
+
+  GThreadNativeMethodRunner(long funcPtr, long funcArg, boolean joinable) {
+    this.funcPtr = funcPtr;
+    this.funcArg = funcArg;
+    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. */ + 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. */
+  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 should need to 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. */ + 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);
+  }
+
+  static Thread threadIDToThread(int threadID) {
+    if (threadID < threads.length) {
+      /* Note : if the user is using a stale reference, things will just
+         break.  TODO: Add an error-checking mode where the user problems with
+         threads are announced. */
+      WeakReference threadRef = threads[threadID];
+      if (threadRef == null)
+        return null;
+ return (Thread) threadRef.get(); /* Return the referent. Return null + if the thread has already been + garbage-collected. */ + } else {
+      return null;
+    }
+  }
+ + /* Joinable threads need a permanent reference, so that they won't go away
+     when they die.  Joinable threads have to be explicitly joined before they
+     may die. */
+  static final Set joinable = Collections.synchronizedSet(new HashSet());
+
+  void registerSelfJoinable() {
+    joinable.add(this);
+  }
+ + static void deRegisterJoinable(Thread thread) {
+    joinable.remove(thread);
+  }
+
+ +}
+
+// Local Variables:
+// c-file-style: "gnu"
+// End:
--- /dev/null   2003-01-30 10:24:37.000000000 +0000
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.c       
2004-05-08 06:12:47.000000000 +0000
@@ -0,0 +1,68 @@
+/* Native implementation of functions in GThreadNativeMethodRunner
+   Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+#include "gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h"
+#include "gthread-jni.h"
+
+/*
+ * Class:     GThreadNativeMethodRunner
+ * Method:    nativeRun
+ * Signature: (J)V
+ *
+ * Purpose: Run the C function whose function pointer is
+ * + */ +JNIEXPORT void JNICALL +Java_gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_nativeRun(JNIEnv *gdk_env, jobject lcl_obj, + jlong funcAddr, jlong funcArg)
+{
+  /* Convert the function's address back into a pointer to a C function. */
+  void *(*funcPtr)(void *) = (void *(*)(void *)) funcAddr;
+ + /* We do not need to worry about the return value from funcPtr(); it's
+     just thrown away.  That is part of the g_threads spec, so no reason
+     to worry about returning it.  */
+  (void) funcPtr((void *) funcArg);
+  /* Fall off the end and terminate the thread of control. */
+}
+
+/* Local Variables: */
+/* c-file-style: "gnu" */
+/* End: */
+
+
--- /dev/null   2003-01-30 10:24:37.000000000 +0000
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h       
2004-05-08 06:11:47.000000000 +0000
@@ -0,0 +1,29 @@
+/* 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
+/* Inaccessible static: threadInitNumber */
+/* Inaccessible static: stopThreadPermission */
+#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: ChangeLog
===================================================================
RCS file: /cvsroot/classpath/classpath/ChangeLog,v
retrieving revision 1.2177
diff -I*.class -u -r1.2177 ChangeLog
--- ChangeLog   8 May 2004 06:02:00 -0000       1.2177
+++ ChangeLog   10 May 2004 05:40:57 -0000
@@ -1,3 +1,22 @@
+2004-05-10  Steven Augart  <address@hidden>
+
+       * --portable-native-sync fixed for GTK2.  Code cleanup still to be
+       done (including a better changelog entry); do not apply this patch
+       to the permanent CVS sources.
+
+2004-05-08  Steven Augart  <address@hidden>
+
+ * ChangeLog: Restore corrupted umlauts. +
+       Some time between 2004-04-07 and 2004-05-01, somebody used
+       an editing tool on ChangeLog that converted Bernd Mösli's surname
+       to Mvsli, and similarly corrupted the names of Jörg Prante and
+       H. Väisänen.  It looks as if the editing tool read in latin1
+       characters, then masked them with 0x7F and wrote them out again.
+       
+       * ChangeLog: Explicitly set coding system to latin-1.
+       Added a "Local Variables" section.
+       
 2004-05-08  Casey Marshall  <address@hidden>
* java/security/Signature.java
@@ -9437,7 +9456,7 @@
        * java/sql/Timestamp.java
        (valueOf): Fixed confusion of java.sql.Date and java.util.Date
-2003-07-24 H. Vdisdnen <address@hidden>
+2003-07-24  H. Väisänen  <address@hidden>
* java/text/SimpleDateFormat.java (format) [YEAR_FIELD]: Zero pad
        unless field size is 2.
@@ -23757,7 +23776,7 @@
* java/util/zip/ZipFile.java (readEntries): Search for the End Of
        Central Directory.  When a zip comment is present the directory
-       may start earlier.  Patch suggested by Jvrg Prante
+       may start earlier.  Patch suggested by Jörg Prante
        <address@hidden>.
* java/util/zip/ZipConstants.java: Renamed constants to their SUN
@@ -23962,7 +23981,7 @@
* java/util/zip/ZipFile.java: Return -1 in PartialInputStream.read(byte[],int,int) when end of stream reached.
-       Reported by Bernd Mvsli <address@hidden>.
+       Reported by Bernd Mösli <address@hidden>.
2001-10-01 Mark Wielaard <address@hidden> @@ -34646,3 +34665,6 @@
        * java/lang/reflect/Field.java: Changed so that it uses native
peers +Local Variables:
+coding: iso-latin-1-unix       
+End:
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   10 May 2004 05:40:58 -0000
@@ -43,11 +43,18 @@
  * Julian Dolby (address@hidden)
  * February 7, 2003
  *
+ * Updated (new functions) for Glib 2.0 by Steven Augart
+ *         <steve+classpath at augart dot com>
+ * Also fixed cond_wait to free the mutex.
+ * Also fixed trylock to actually try to get the lock.
+ * <augart at watson dot ibm dot com>, + * April 30, 2004 -- May 6 2004
+ *
* 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
  * about the underlying VM beyond that it implements the JNI functionality
@@ -60,8 +67,8 @@
  *
  * 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
@@ -73,109 +80,487 @@
 /* Global data                                                         */
 /************************************************************************/
+#include <stdint.h> /* provides intptr_t */
 #include "gthread-jni.h"
+#define VERBOSE 0              /* announce entry and exit into each method. */
+#define DEBUG 0                 /* a few bits of special DEBUG code.  */
+#define DEBUG_MONITORS 0        /* really verbose. */
+
+#if 0                           /* TODO: Uncomment this when we start setting 
priorities */
+#include "gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h"
+#endif
/* The VM handle. This is set in GtkToolkitMain.gtkInit */
 JavaVM *gdk_vm;
+/******************************************************/
+/** Cached field IDs and method IDs for Exceptions  ***/
+/******************************************************/
+
+jclass runtimeException_class;  /* java.lang.RuntimeException */
+jmethodID runtimeException_ctor; /* constructor for it */
+
+
+/* Set up the cache of jclass and jmethodID primitives we need
+   for maybe_rethrow().  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. */
+/* XXX There has to be a better way to handle this.  I am not sure what sort of
+   error reporting we want to do.  XXX*/
+static void +setup_exception_cache(JNIEnv *env) +{
+  static int exception_cache_initialized = 0;
+ + if (exception_cache_initialized)
+    return;
+  runtimeException_class = (*env)->FindClass (env,
+                                        "java/lang/RuntimeException");
+  if (! runtimeException_class) {
+    /* XXX TODO Improve error handling here. */
+    g_error("Serious trouble: classpath couldn't find 
java/lang/RuntimeException");
+    g_assert_not_reached();
+  }
+  /* Pin it down. */
+ runtimeException_class + = (jclass) (*env)->NewGlobalRef(env, runtimeException_class);
+  g_assert(runtimeException_class);
+ + + + runtimeException_ctor + = (*env)->GetMethodID(env, runtimeException_class, "<init>",
+                              "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+  if ((*env)->ExceptionOccurred(env)) {
+    /* XXX TODO Improve error handling here. */
+    g_error("Serious trouble: classpath couldn't find a two-arg constructor for 
java/lang/RuntimeException");
+    g_assert_not_reached();
+  }
+ + ++exception_cache_initialized;
+}
+
+
+/* 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(JJ) */
+
+/* GThreadMutex and its methods */
+jclass mutex_class;
+jmethodID mutex_ctor;
+jfieldID mutex_lock_fld;
+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_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; + /************************************************************************/
 /* Utilities to reflect exceptions back to the VM                      */
 /************************************************************************/
-/* This function checks for a pending exception, and rethrows it with
+
+
+/* Rethrow an exception we received, as a runtime exception.  1 if we did
+   rethrow, -1 if we did not.   This must be the most recent exception that
+   happened, so that ExceptionDescribe will work. */
+static int
+rethrow(JNIEnv *env, jthrowable cause, const char *message, const char *file, 
int line)
+{
+  jstring jmessage;
+  jobject obj;
+
+  /* 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 = g_try_malloc(len);
+
+  g_assert(cause);
+  g_critical("%s:%d: AWT JNI failure: %s\n", file, line, message);
+#if  DEBUG
+  (*env)->ExceptionDescribe(env);
+#endif
+
+  if (buf)
+    {
+      bzero(buf, len);
+      snprintf(buf, len, fmt, message, file, line);
+      jmessage = (*env)->NewStringUTF(env, buf);
+      g_free(buf);
+    } else {
+      jmessage = NULL;
+    }
+ + setup_exception_cache(env); + + /* Create the RuntimeException wrapper object and throw it */ + obj = (*env)->NewObject (env, runtimeException_class, + runtimeException_ctor, jmessage, cause); + + if (! obj) + {
+      /* I think this should never happen.  Unless there are bugs in my
+        JNI code. */
+      g_critical("GNU Classpath: Failure of JNI NewObject() while making a newly 
created java.lang.RuntimeException object.  We were trying to warn about the following 
failure:");
+      g_critical("%s:%d: %s", file, line, message);
+      g_assert_not_reached();
+    }
+ + + /* throw it */
+  if ((*env)->Throw(env, (jthrowable) obj)) {
+    fprintf(stderr, "GNU Classpath: Failure of JNI Throw.");
+    /* This should just never fail. */
+    g_assert_not_reached();
+  }
+#if DEBUG
+  fprintf(stderr, "GNU Classpath: We really should never even hit this code -- 
there just shouldn't be any java failures");
+  g_assert_not_reached();
+#endif
+  return 1;
+}
+
+
+/* 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, 1 if some were */
-static void maybe_rethrow(JNIEnv *gdk_env, char *message, char *file, int 
line) {
-  jthrowable cause;
+static int
+maybe_rethrow(JNIEnv *env, const char *message, const char *file, int line) +{
+  jthrowable cause = (*env)->ExceptionOccurred(env);
- 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);
-      }
+  /* rethrow if an exception happened */
+  if (cause)
+    return rethrow(env, cause, message, file, line);
+  return 0;
 }
-/* This macro is used to include a source location in the exception message */
-#define MAYBE_RETHROW(_class, _message) \
-maybe_rethrow(_class, _message, __FILE__, __LINE__)
+/* This macro is used to include a source location in the exception message.
+   Once we have maybe rethrown, if there WAS trouble, return nonzero, else 0 */
+#define MAYBE_RETHROW(_env, _message)                          \
+       maybe_rethrow(_env, _message, __FILE__, __LINE__)
+
+#define RETHROW(_env, _message)                                \
+       rethrow(_env, (*env)->ExceptionOccurred(env), _message, __FILE__, 
__LINE__)
+
+#define REPORT_TROUBLE_AND_RETURN(_env, _message) do { \
+    report_trouble(_env, _message, __FILE__, __LINE__);        \
+    return;                                            \
+} while(0)
+ +#define WHERE __FILE__ ":" G_STRINGIFY(__LINE__) ": " +
+#define HIDE_OLD_TROUBLE                                       \
+   jthrowable savedTrouble = (*env)->ExceptionOccurred(env);        \
+   (*env)->ExceptionClear(env);
+
+#define SHOW_OLD_TROUBLE() do {                                 \
+  if (savedTrouble) {                                           \
+    if ((*env)->Throw(env, savedTrouble)) {                     \
+      g_critical(WHERE "ReThrowing the savedTrouble failed");   \
+      g_assert_not_reached();                                   \
+    }                                                           \
+  }                                                             \
+} while(0)
+
+
+/**********************************************************/
+/***** The main cache *************************************/
+/* With all of this caching, strictly speaking we should maintain permanent
+   global references to each of the classes for which we have cached methods,
+   since the classes might otherwise theoretically be unloaded and then later
+   loaded again.  In practice, though, all of this stuff is pretty basic, so
+   we don't worry about it. --Steven Augart */
+static void
+setup_cache(JNIEnv *env)
+{
+  static int initialized = 0;
+ + if (initialized)
+    return;
+  setup_exception_cache(env); /* make sure we can report on trouble */
+
+  /* java.lang.Object and its methods */
+  obj_class = (*env)->FindClass (env, "java/lang/Object");
+  if (! obj_class) {
+    RETHROW(env, "cannot find java.lang.Object");
+  }
+  /* Pin it down. */
+  obj_class = (jclass) (*env)->NewGlobalRef(env, obj_class);
+  g_assert(obj_class);
+
+  obj_ctor = (*env)->GetMethodID(env, obj_class, "<init>", "()V");
+  if (!obj_ctor){
+    RETHROW(env, "cannot find constructor for java.lang.Object");
+  }
+ +
+  obj_notify_mth = (*env)->GetMethodID(env, obj_class, "notify", "()V");
+  MAYBE_RETHROW(env, "cannot find java.lang.Object.notify()V");
+
+  obj_notifyall_mth = (*env)->GetMethodID(env, obj_class, "notifyAll", "()V");
+  MAYBE_RETHROW(env, "cannot find java.lang.Object.notifyAll()V");
+
+  obj_wait_mth = (*env)->GetMethodID(env, obj_class, "wait", "()V");
+  MAYBE_RETHROW(env, "cannot find Object.<wait()V>");
+
+  obj_wait_nanotime_mth = (*env)->GetMethodID(env, obj_class, "wait", "(JI)V");
+  MAYBE_RETHROW(env, "cannot find Object.<wait(JI)V>");
+ +
+  /* GThreadMutex and its methods */
+  mutex_class = (*env)->FindClass (env, "gnu/java/awt/peer/gtk/GThreadMutex");
+  MAYBE_RETHROW(env, "cannot find gnu.java.awt.peer.gtk.GThreadMutex");
+
+  /* Pin it down. */
+  mutex_class = (jclass) (*env)->NewGlobalRef(env, mutex_class);
+  g_assert(mutex_class);
+
+  mutex_ctor = (*env)->GetMethodID(env, mutex_class, "<init>", "()V");
+  MAYBE_RETHROW(env, "cannot find zero-arg constructor for GThreadMutex");
+
+  mutex_potentialLockers_fld = (*env)->GetFieldID
+    (env, mutex_class, "potentialLockers", "I");
+  MAYBE_RETHROW(env, "cannot find GThreadMutex.potentialLockers");
+
+  mutex_lockForPotentialLockers_fld = (*env)->GetFieldID
+    (env, mutex_class, "lockForPotentialLockers", "Ljava/lang/Object;");
+  MAYBE_RETHROW(env, "cannot find GThreadMutex.lockForPotentialLockers");
+
+  mutex_lock_fld = (*env)->GetFieldID(env, mutex_class,
+                                             "lock", "Ljava/lang/Object;");
+  MAYBE_RETHROW(env, "cannot find GThreadMutex.lock");
+ + /* java.lang.Thread */
+  thread_class = (*env)->FindClass (env, "java/lang/Thread");
+  MAYBE_RETHROW(env, "cannot find java.lang.Thread");
+
+  /* Pin it down. */
+  thread_class = (jclass) (*env)->NewGlobalRef(env, thread_class);
+  g_assert(thread_class);
+
+  thread_current_mth
+    = (*env)->GetStaticMethodID(env, thread_class, "currentThread", 
"()Ljava/lang/Thread;");
+  MAYBE_RETHROW(env, "cannot find Thread.currentThread() method");
+
+  thread_equals_mth
+    = (*env)->GetMethodID(env, thread_class, "equals", 
"(Ljava/lang/Object;)Z");
+  MAYBE_RETHROW(env, "cannot find Thread.equals() method");
+
+  thread_join_mth
+    = (*env)->GetMethodID(env, thread_class, "join", "()V");
+  MAYBE_RETHROW(env, "cannot find Thread.join() method");
+
+  thread_stop_mth
+    = (*env)->GetMethodID(env, thread_class, "stop", "()V");
+  MAYBE_RETHROW(env, "cannot find Thread.stop() method");
+
+  thread_yield_mth
+    = (*env)->GetStaticMethodID(env, thread_class, "yield", "()V");
+  MAYBE_RETHROW(env, "cannot find Thread.yield() method");
+
+
+  /* java.lang.ThreadLocal */
+  threadlocal_class = (*env)->FindClass (env, "java/lang/ThreadLocal");
+  MAYBE_RETHROW(env, "cannot find ThreadLocal");
+
+  /* Pin it down. */
+  threadlocal_class = (jclass) (*env)->NewGlobalRef(env, threadlocal_class);
+  g_assert(threadlocal_class);
+
+
+  threadlocal_ctor = (*env)->GetMethodID(env, threadlocal_class, "<init>", 
"()V");
+  MAYBE_RETHROW(env, "cannot find ThreadLocal.<init>()V");
+ + threadlocal_set_mth = (*env)->GetMethodID(env, threadlocal_class, "set", "(Ljava/lang/Object;)V");
+  MAYBE_RETHROW(env, "cannot find ThreadLocal.set(Object)V");
+
+  threadlocal_get_mth = (*env)->GetMethodID(env, threadlocal_class, "get", 
"()Ljava/lang/Object;");
+  MAYBE_RETHROW(env, "cannot find java.lang.ThreadLocal.get()Object");
+
+
+  /* java.lang.Long */
+  long_class = (*env)->FindClass (env, "java/lang/Long");
+#if 0 && DEBUG
+  fprintf(stderr, "long_class=%d\n", long_class);
+#endif
+  MAYBE_RETHROW(env, "cannot find class java.lang.Long");
+ + /* Pin it down. */
+  long_class = (jclass) (*env)->NewGlobalRef(env, long_class);
+  g_assert(long_class);
+
+
+  long_ctor = (*env)->GetMethodID(env, long_class, "<init>", "(J)V");
+#if 0 && DEBUG
+  fprintf(stderr, "long_ctor=%d\n", long_ctor);
+#endif
+  MAYBE_RETHROW(env, "cannot find method java.lang.Long.<init>(J)V");
+ + long_longValue_mth = (*env)->GetMethodID(env, long_class, "longValue", "()J");
+  MAYBE_RETHROW(env, "cannot find method java.lang.Long.longValue()J");
+#if 0 && DEBUG
+  fprintf(stderr, "long_longValue_mth=%d\n", long_longValue_mth);
+#endif
+
+ + /* GThreadNativeMethodRunner */
+  runner_class = (*env)->FindClass (env, 
"gnu/java/awt/peer/gtk/GThreadNativeMethodRunner");
+  MAYBE_RETHROW(env, "cannot find 
gnu.java.awt.peer.gtk.GThreadNativeMethodRunner");
+
+  /* Pin it down. */
+  runner_class = (jclass) (*env)->NewGlobalRef(env, runner_class);
+  g_assert(runner_class);
+
+  runner_ctor = (*env)->GetMethodID(env, runner_class, "<init>", "(JJZ)V");
+  MAYBE_RETHROW(env, "cannot find method 
GThreadNativeMethodRunner.<init>(JJZ)");
+ + runner_start_mth = (*env)->GetMethodID(env, runner_class, "start", "()V");
+  MAYBE_RETHROW(env, "cannot find method GThreadNativeMethodRunner.start()V");
+
+  runner_threadToThreadID_mth = (*env)->GetStaticMethodID(env, runner_class, 
"threadToThreadID", "(Ljava/lang/Thread;)I");
+  MAYBE_RETHROW(env, "cannot find method 
GThreadNativeMethodRunner.threadToThreadID(java.lang.Thread)I");
+
+  runner_threadIDToThread_mth = (*env)->GetStaticMethodID(env, runner_class, 
"threadIDToThread", "(I)Ljava/lang/Thread;");
+  MAYBE_RETHROW(env, "cannot find method 
GThreadNativeMethodRunner.threadIDToThread(I)java.lang.Thread");
+
+  runner_deRegisterJoinable_mth = (*env)->GetStaticMethodID(env, runner_class, 
"deRegisterJoinable", "(Ljava/lang/Thread;)V");
+  MAYBE_RETHROW(env, "cannot find method 
GThreadNativeMethodRunner.deRegisterJoinable(java.lang.Thread)V");
+
+
+
+  /* java.lang.InterruptedException */
+ interrupted_exception_class + = (*env)->FindClass (env, "java/lang/InterruptedException");
+  MAYBE_RETHROW(env, "cannot find class java.lang.InterruptedException");
+
+  /* Pin it down. */
+  interrupted_exception_class = (jclass) (*env)->NewGlobalRef(env, 
interrupted_exception_class);
+  g_assert(interrupted_exception_class);
+
+  g_assert(! (*env)->ExceptionOccurred(env));
+
+  ++initialized;
+}
+
+
+
/************************************************************************/
 /* 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");
+static jobject
+allocatePlainObject(void) +{
+  jobject obj;
+  JNIEnv *env;
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  obj = (*env)->NewObject (env, obj_class, obj_ctor);
+  if (! obj ) {
+    int r = MAYBE_RETHROW(env, "cannot allocate object");
+    g_assert(r);
+    g_assert_not_reached();
+  }
- *obj = (*gdk_env)->NewGlobalRef (gdk_env, *obj);
-  MAYBE_RETHROW(gdk_env, "cannot make global ref");
+  obj = (*env)->NewGlobalRef (env, obj);
+  g_assert(obj);
+  MAYBE_RETHROW(env, "cannot make global ref");
return 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(jobject obj) +{
   if (obj) {
-    (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+    JNIEnv *env;
+    (*gdk_vm)->GetEnv (gdk_vm, (void **)&env, JNI_VERSION_1_1);
+ + (*env)->DeleteGlobalRef (env, obj);
+    /* DeleteGlobalRef should never fail */
+    g_assert (! (*env)->ExceptionOccurred(env));
+  }
+}
+
- (*gdk_env)->DeleteGlobalRef (gdk_env, *obj);
-    MAYBE_RETHROW(gdk_env, "cannot delete global ref");
+/************************************************************************/
+/* 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(void) +{
+  jobject obj1;
+  JNIEnv *env;
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  obj1 = (*env)->NewObject (env, mutex_class, mutex_ctor);
+  if (MAYBE_RETHROW(env, "cannot allocate a GThreadMutex"))
+    g_assert_not_reached();
- g_free (obj);
-  }
+  obj1 = (*env)->NewGlobalRef (env, obj1);
+  if (MAYBE_RETHROW(env, "cannot make global ref"))
+    g_assert_not_reached();
+
+  return obj1;
 }
@@ -184,195 +569,485 @@
 /************************************************************************/
/* 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(m)                       \
+    enterMonitor(env, m, G_STRINGIFY(m))
+
+static int
+enterMonitor(JNIEnv *env, jobject monitorObj, const char monName[]) +{
+#if DEBUG_MONITORS
+  fprintf(stderr, " <MonitorEnter(%s)>", monName);
+#endif
+  (*env)->MonitorEnter (env, monitorObj);
+  return MAYBE_RETHROW(env, "cannot enter monitor");
 }
+
 /* 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(m)                        \
+    exitMonitor(env, m, G_STRINGIFY(m))
+
+static int
+exitMonitor(JNIEnv *env, jobject mutexObj, const char monName[])
+{
+#if DEBUG_MONITORS
+  fprintf(stderr, " <MonitorExit(%s)>", monName);
+#endif
+  (*env)->MonitorExit (env, mutexObj);
+  return MAYBE_RETHROW(env, "cannot exit monitor ");
 }
+
+/************************************************************************/
+/* Miscellaneous utilities                                             */
+/************************************************************************/
+
+/* Get the Java Thread object that corresponds to a particular thread ID. + A negative thread Id gives us a null object. */ +static jobject +getThreadFromThreadID(JNIEnv *env, gpointer gThreadID)
+{
+  jint threadNum = (jint) gThreadID;
+
+  if (threadNum < 0)
+    return NULL;
+
+  jobject thread = (*env)->CallStaticObjectMethod
+    (env, runner_class, runner_threadIDToThread_mth, threadNum);
+  if (MAYBE_RETHROW(env, "cannot get Thread for threadID "))
+    return NULL;;
+
+  return thread;
+}
+
+/* Return the unique threadID of THREAD.  A null thread gives us ID -1 and
+   throws an exception.  If it doesn't abort entirely. */
+static gpointer
+getThreadIDFromThread(JNIEnv *env, jobject thread)
+{
+  g_assert((*env)->IsInstanceOf(env, thread, thread_class));
+  HIDE_OLD_TROUBLE;
+
+/*   if ( ! (*env)->IsInstanceOf(env, this_thread, runner_class) )  */
+/*     { */
+/*       fprintf(stderr, "g_thread_self() was called on a thread that was not 
started with g_thread_create(); Steve Augart needs to rethink his implementation of 
getThreadIDFromThread"); */
+ +/* } */
+  jint threadNum = (*env)->CallStaticIntMethod
+    (env, runner_class, runner_threadToThreadID_mth, thread);
+
+  if (MAYBE_RETHROW(env, "cannot get ThreadID for a Thread "))
+    return (gpointer) -1;
+
+  SHOW_OLD_TROUBLE();
+
+  return (gpointer) threadNum;
+}
+
+
+/************************************************************************/
+/* The Actual JNI functions that we pass to the function vector.       */
+/************************************************************************/
+
 /* Create a mutex, which is a java.lang.Object for us */
-static GMutex *g_mutex_new_jni_impl (void) {
-  return (GMutex*) allocatePlainObject();
+static GMutex *
+g_mutex_new_jni_impl (void) +{
+  jobject mutexObj;
+#if VERBOSE
+  fprintf(stderr, "g_mutex_new_jni_impl()");
+#endif
+
+  mutexObj = allocateMutexObject();
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> %d \n", mutexObj);
+#endif
+
+  return (GMutex*) mutexObj;
+ } /* Lock a mutex. */
-static void g_mutex_lock_jni_impl (GMutex *mutex __attribute__((unused))) {
-  JNIEnv *gdk_env;
+static void g_mutex_lock_jni_impl (GMutex *mutex) +{
+  jobject mutexObj = (jobject) mutex;
+  jobject lockForPotentialLockersObj, lockObj;
+  jint potentialLockers;
+
+#if VERBOSE
+  fprintf(stderr, "g_mutex_lock_jni_impl( mutexObj = %d )", mutexObj);
+#endif
+  JNIEnv *env;
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+  /* We'll need to record that we have the lock. */
+ lockForPotentialLockersObj = (*env)->GetObjectField + (env, mutexObj, mutex_lockForPotentialLockers_fld);
+  if (MAYBE_RETHROW(env, "cannot read GThreadMutex.lockForPotentialLockers field's 
value"))
+    goto done;
+
+  /* Get the lock object itself now. */
+ lockObj = (*env)->GetObjectField + (env, mutexObj, mutex_lock_fld);
+  if (MAYBE_RETHROW(env, "cannot read GThreadMutex.lock field's value"))
+    g_assert_not_reached();
+
+  ENTER_MONITOR(lockForPotentialLockersObj);
+
+ potentialLockers = (*env)->GetIntField + (env, mutexObj, mutex_potentialLockers_fld);
+  if (MAYBE_RETHROW(env, "cannot read GThreadMutex.potentialLockers field's 
value"))
+    g_assert_not_reached();
+
+  ++potentialLockers;
+
+  (*env)->SetIntField
+    (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+  if (MAYBE_RETHROW(env, "cannot write GThreadMutex.potentialLockers field's 
value"))
+    g_assert_not_reached();
+
+  EXIT_MONITOR(lockForPotentialLockersObj);
+ + ENTER_MONITOR(lockObj); - (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  SHOW_OLD_TROUBLE();
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> VOID \n");
+#endif
- takeLock(gdk_env, mutex);
 }
-/* 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. */
+static gboolean +g_mutex_trylock_jni_impl (GMutex *gmutex)
 {
-  /* XXX Shall we implement this in a VM-specific way under a flag? */
-  return FALSE;
+  jobject lockForPotentialLockersObj, lockObj;
+  jobject mutexObj = (jobject) gmutex;
+  jint potentialLockers;
+  gboolean ret;
+
+  JNIEnv *env;
+#if VERBOSE
+  fprintf(stderr, "g_mutex_trylock_jni_impl(mutexObj=%d)", mutexObj);
+#endif
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+  /* Check the value of mutex.potentialLockers */
+ lockForPotentialLockersObj = (*env)->GetObjectField + (env, mutexObj, mutex_lockForPotentialLockers_fld);
+  if (MAYBE_RETHROW(env, "cannot read GThreadMutex.lockForPotentialLockers field's 
value"))
+    g_assert_not_reached();
+
+  ENTER_MONITOR(lockForPotentialLockersObj);
+
+ potentialLockers = (*env)->GetIntField + (env, mutexObj, mutex_potentialLockers_fld);
+  if (MAYBE_RETHROW(env, "cannot read GThreadMutex.potentialLockers field's 
value"))
+    g_assert_not_reached();
+
+  g_assert(potentialLockers >= 0);
+ + if (potentialLockers) {
+    /* Already locked.  Clean up and leave. */
+    EXIT_MONITOR(lockForPotentialLockersObj);
+    ret = FALSE;                    /* false */
+  }
+ + /* So, get the lock itself now. */ + lockObj = (*env)->GetObjectField + (env, mutexObj, mutex_lock_fld);
+  if (MAYBE_RETHROW(env, "cannot read GThreadMutex.lock field's value"))
+    g_assert_not_reached();
+
+  g_assert(potentialLockers == 0);
+  ENTER_MONITOR(lockObj); /* this is guaranteed not to block. */
+
+  /* We have the monitor.  Record that fact. */
+  potentialLockers = 1;         /* must be zero. */
+ (*env)->SetIntField + (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+  if (MAYBE_RETHROW(env, "cannot write GThreadMutex.potentialLockers field's 
value"))
+    g_assert_not_reached();
+ + /* Clean up. */
+  EXIT_MONITOR(lockForPotentialLockersObj);
+  SHOW_OLD_TROUBLE();
+
+  ret = TRUE;                     /* We have it! */
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> %s\n", ret ? "TRUE" : "FALSE");
+#endif
+  return ret;
 }
/* Unlock a mutex. */
-static void g_mutex_unlock_jni_impl (GMutex *mutex) {
-  JNIEnv *gdk_env;
+static void +g_mutex_unlock_jni_impl (GMutex *gmutex) +{
+  jobject mutexObj = gmutex;
+  jobject lockObj, lockForPotentialLockersObj;
+  jint potentialLockers;
+
+  JNIEnv *env;
+
+#if VERBOSE
+  fprintf(stderr, "g_mutex_unlock_jni_impl(mutexObj=%d)", mutexObj);
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+  HIDE_OLD_TROUBLE;
+
+  /* So, get the lock itself now. */
+  if (! (lockObj = (*env)->GetObjectField (env, mutexObj, mutex_lock_fld))) {
+    RETHROW(env, "cannot read GThreadMutex.lock field's value");
+    g_assert_not_reached();
+  }
+
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  EXIT_MONITOR(lockObj); /* this is guaranteed not to block. */
- releaseLock(gdk_env, mutex);
+  /* Kick down potentialLockers by one.  We do this after we free the lock, so
+     that someone waiting for the lock can get it as soon as possible. */
+ lockForPotentialLockersObj + = (*env)->GetObjectField + (env, mutexObj, mutex_lockForPotentialLockers_fld);
+  if (MAYBE_RETHROW(env, "cannot read GThreadMutex.lockForPotentialLockers field's 
value"))
+      g_assert_not_reached();
+
+  ENTER_MONITOR(lockForPotentialLockersObj);
+ potentialLockers = (*env)->GetIntField + (env, mutexObj, mutex_potentialLockers_fld);
+  if (MAYBE_RETHROW(env, "cannot read GThreadMutex.potentialLockers field's 
value"))
+    g_assert_not_reached();
+ + g_assert(potentialLockers >= 1);
+  --potentialLockers;
+ (*env)->SetIntField + (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+  if (MAYBE_RETHROW(env, "cannot write GThreadMutex.potentialLockers field's 
value"))
+    g_assert_not_reached();
+ + /* Clean up. */
+  EXIT_MONITOR(lockForPotentialLockersObj);
+  SHOW_OLD_TROUBLE();
+#if VERBOSE
+  fprintf(stderr, " ==> VOID\n");
+#endif
 }
/* Free a mutex (isn't C fun?) */
 static void g_mutex_free_jni_impl (GMutex *mutex)
 {
-  freePlainObject( (jobject*)mutex );
+#if VERBOSE
+  fprintf(stderr, "g_mutex_free_jni_impl()");
+#endif
+  jobject mutexObj = (jobject) mutex;
+  freeObject( mutexObj);
+#if VERBOSE
+  fprintf(stderr, " ==> VOID\n");
+#endif
+ } +
+
 /************************************************************************/
 /* 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 *g_cond_new_jni_impl (void) +{
+  jobject condObj;
+ +#if VERBOSE
+  fprintf(stderr, "g_mutex_free_jni_impl()");
+#endif
+  condObj = allocatePlainObject();
+#if VERBOSE
+  fprintf(stderr, " ==> %d\n", condObj);
+#endif
+ 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;
-
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+static void g_cond_signal_jni_impl (GCond *gcond)
+{
+  JNIEnv *env;
+  jobject condObj = (jobject) gcond;
- 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>");
+#if VERBOSE
+  fprintf(stderr, "g_cond_signal_jni_impl(condObj = %d)", condObj);
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+ HIDE_OLD_TROUBLE;
   /* Must have locked an object to call notify */
-  takeLock(gdk_env, cond);
+  ENTER_MONITOR(condObj);
- (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, signal_mth);
-  MAYBE_RETHROW(gdk_env, "cannot signal mutex");
+  (*env)->CallVoidMethod (env, condObj, obj_notify_mth);
+  if (MAYBE_RETHROW(env, "cannot signal mutex with Object.notify()")) {
+    EXIT_MONITOR(condObj);
+    goto done;
+  }
+
+  EXIT_MONITOR(condObj);
+  SHOW_OLD_TROUBLE();
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> VOID\n");
+#endif
- releaseLock(gdk_env, cond);
 }
/* 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 g_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 VERBOSE
+  fprintf(stderr, "g_cond_broadcast_jni_impl(condObj=%d)", condObj);
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
- lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
-  MAYBE_RETHROW(gdk_env, "cannot find Object");
+  HIDE_OLD_TROUBLE;
+  /* Must have locked an object to call notifyAll */
+  ENTER_MONITOR(condObj);
+
+  (*env)->CallVoidMethod(env, condObj, obj_notifyall_mth);
+  if (MAYBE_RETHROW(env, "cannot broadcast to mutex with Object.notify()")) {
+    EXIT_MONITOR(condObj);
+    return;
+  }
- bcast_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notifyAll", "()V");
-  MAYBE_RETHROW(gdk_env, "cannot find Object.<notifyAll>");
- /* Must have locked an object to call notifyAll */
-  takeLock(gdk_env, cond);
+  EXIT_MONITOR(condObj);
- (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, bcast_mth);
-  MAYBE_RETHROW(gdk_env, "cannot broadcast to mutex");
+  SHOW_OLD_TROUBLE();
- releaseLock(gdk_env, cond);
+#if VERBOSE
+  fprintf(stderr, " ==> VOID\n");
+#endif
 }
/* Wait on a condition variable. For us, this simply means call
  * Object.wait.
  */
-static void g_cond_wait_jni_impl
-  (GCond *cond, GMutex *mutex __attribute__((unused)))
+static void g_cond_wait_jni_impl (GCond *gcond, GMutex *gmutex)
 {
-  jclass lcl_class;
-  jmethodID wait_mth;
-  JNIEnv *gdk_env;
+  jobject condObj = (jobject) gcond;
+  JNIEnv *env;
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  (*gdk_vm)->GetEnv(gdk_vm, (void **) &env, JNI_VERSION_1_1);
+  setup_cache(env);
- 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>");
+  HIDE_OLD_TROUBLE;
- /* Must have locked an object to call wait */
-  takeLock(gdk_env, cond);
+  /* Must have locked a Java object to call wait on it */
+  ENTER_MONITOR(condObj);
- (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth);
-  MAYBE_RETHROW(gdk_env, "cannot wait on mutex");
+  /* Our atomicity is now guaranteed.  Unlock the GMutex. */
+  g_mutex_unlock_jni_impl (gmutex);
+ + (*env)->CallVoidMethod(env, condObj, obj_wait_mth);
+  if (MAYBE_RETHROW(env, "cannot wait on condObj")) {
+    EXIT_MONITOR(condObj);
+    return;
+  }
- releaseLock(gdk_env, cond);
+  /* Re-acquire the lock. */
+  g_mutex_lock_jni_impl (gmutex);
+ + + EXIT_MONITOR(condObj);
+
+  SHOW_OLD_TROUBLE();
+ } -/* 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.
  */
 static gboolean
-g_cond_timed_wait_jni_impl
-  (GCond *cond, GMutex *mutex __attribute__((unused)),
-   GTimeVal *end_time)
-{
-  jclass lcl_class;
-  jmethodID wait_mth;
-  JNIEnv *gdk_env;
-  jlong time;
+g_cond_timed_wait_jni_impl(GCond *gcond, GMutex *gmutex, GTimeVal *end_time)
+{
+  JNIEnv *env;
+  jlong time_millisec;
+  jint time_nanosec;
   jthrowable cause;
+  jobject condObj = (jobject) gcond;
+  jboolean ret;
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+#if VERBOSE
+  jobject mutexObj = (jobject) gmutex;
+  fprintf(stderr, "g_cond_timed_wait_jni_impl(cond=%d, mutex=%d, end_time=< sec=%u, usec=%u 
>)", condObj, mutexObj, end_time->tv_sec, end_time->tv_usec);
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
- 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;
+  HIDE_OLD_TROUBLE;
- /* Must have locked an object to call wait */
-  takeLock(gdk_env, cond);
+  time_millisec = end_time->tv_sec*1000 + end_time->tv_usec/1000;
+  time_nanosec = 1000 * (end_time->tv_usec % 1000) ;
- (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth, time);
+  /* Must have locked an object to call wait */
+  ENTER_MONITOR(condObj);
+  g_mutex_unlock_jni_impl(gmutex);
- if ((cause = (*gdk_env)->ExceptionOccurred(gdk_env)) != NULL) {
-    jclass intr = (*gdk_env)->FindClass (gdk_env, 
"java.lang.InterruptedException");
-    if ( (*gdk_env)->IsInstanceOf(gdk_env, cause, intr) ) {
-      releaseLock(gdk_env, cond);
-  return FALSE;
-    } else {
-      MAYBE_RETHROW(gdk_env, "error in timed wait");
+  (*env)->CallVoidMethod(env, condObj, obj_wait_nanotime_mth, time_millisec, 
time_nanosec);
+ if ((cause = (*env)->ExceptionOccurred(env))) + {
+      (*env)->ExceptionClear(env);
+ if ( (*env)->IsInstanceOf(env, cause, interrupted_exception_class) ) + {
+          g_mutex_lock_jni_impl(gmutex);
+          EXIT_MONITOR(condObj);
+
+          SHOW_OLD_TROUBLE();
+          ret = FALSE;
+          goto done;
+        } else {
+          if (MAYBE_RETHROW(env, "error in timed wait"))
+            g_assert_not_reached();
+        }
     }
-  }
- releaseLock(gdk_env, cond);
-
-  return TRUE;
+  g_mutex_lock_jni_impl(gmutex);
+  EXIT_MONITOR(condObj);
+ + SHOW_OLD_TROUBLE();
+  ret = TRUE;
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> %s\n", ret ? "TRUE" : "FALSE");
+#endif
+  return ret;
 }
/* Free a condition variable. (isn't C fun?) */
-static void g_cond_free_jni_impl (GCond *cond) {
-  freePlainObject( (jobject*)cond );
+static void g_cond_free_jni_impl (GCond *cond) +{
+  jobject condObj = (jobject) cond;
+  freeObject( condObj );
 }
@@ -386,95 +1061,460 @@
 static GPrivate *g_private_new_jni_impl
   (GDestroyNotify notify __attribute__((unused)))
 {
-  jclass lcl_class;
-  jobject *local;
-  JNIEnv *gdk_env;
-  jmethodID ctor;
+  JNIEnv *env;
+  jobject lcl_key;
+  jobject global_key;
+  GPrivate *g_key;
+
+#if VERBOSE
+  fprintf(stderr, "g_private_new_jni_impl()");
+#endif
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+  lcl_key = (*env)->NewObject(env, threadlocal_class, threadlocal_ctor);
+  if (!lcl_key) {
+    RETHROW(env, "cannot allocate a ThreadLocal");
+    g_key = NULL;
+    goto done;
+  }
+ + global_key = ((*env)->NewGlobalRef (env, lcl_key));
+  if (MAYBE_RETHROW(env, "cannot create a GlobalRef")) {
+    g_key = NULL;
+    goto done;
+  }
+
+  g_key = (GPrivate*) global_key;
+  SHOW_OLD_TROUBLE();
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> %p\n", g_key);
+#endif /* VERBOSE */
+  return g_key;
+}
+
+/*  Get this thread's value for a thread-local key.  This is simply
+ * 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 *g_key) +{
+  JNIEnv *env;
+  jobject val_wrapper;
+  jobject java_key = (jobject) g_key;
+  gpointer thread_specific_data;
+
+  jlong val;
+
+#if VERBOSE
+  fprintf(stderr, "g_private_get_jni_impl(key=%p)", g_key);
+#endif
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+  val_wrapper = (*env)->CallObjectMethod (env, java_key, threadlocal_get_mth);
+  if (MAYBE_RETHROW(env, "cannot find thread-local object"))
+    return NULL;
+
+  if (! val_wrapper ) {
+    /* It's Java's  "null" object.  No ref found. */
+    thread_specific_data = NULL;
+    goto done;
+  }
+ + val = (*env)->CallLongMethod (env, val_wrapper, long_longValue_mth); + if (MAYBE_RETHROW(env, "cannot get thread local value")) + {
+      thread_specific_data = NULL;
+      goto done;
+    }
+  thread_specific_data = (gpointer) (intptr_t) val;
+ +
+  /* Only re-raise the old pending exception if a new one hasn't come along to
+     supersede it.  */
+  SHOW_OLD_TROUBLE();
+
+ done:
+
+#if VERBOSE
+  fprintf(stderr, " ==> %p\n", thread_specific_data);
+#endif
+  return thread_specific_data;
+}
+
+/* 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 *g_key, gpointer thread_specific_data ) +{
+  JNIEnv *env;
+  jobject val_wrapper;
+  jobject java_key = (jobject) g_key;
+
+
+#if VERBOSE
+  fprintf(stderr, "g_private_set_jni_impl(g_key=%p, thread_specific_data=%p)",
+          g_key, thread_specific_data);
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+  /* 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. + */ + +#if 0 && DEBUG
+  fprintf(stderr, "Trying to make a new Long: <long_class=%u, long_ctor = %u, 
tsd=%p>\n", long_class, long_ctor, thread_specific_data);
+#endif
+ val_wrapper = (*env)->NewObject(env, long_class, long_ctor, + (jlong) (intptr_t) thread_specific_data);
+  if (!val_wrapper) {
+#if 0 && DEBUG
+    fprintf(stderr, "TROUBLE: Failed to create java.lang.Long: <long_class=%u, 
long_ctor = %u, tsd=%p>\n", long_class, long_ctor, thread_specific_data);
+#endif
+    int r = RETHROW(env, "cannot create a java.lang.Long");
+    assert(r);
+    goto done;
+  }
+  g_assert(val_wrapper);
+ + + /* 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, java_key, threadlocal_set_mth, val_wrapper);
+  if (MAYBE_RETHROW(env, "cannot set thread local value"))
+    goto done;
+  SHOW_OLD_TROUBLE();
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> VOID\n");
+#endif
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+}
- lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
- ctor = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "<init>", "()V");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<init>");
+/** Create an object of type gnu.java.awt.peer.gtk.GThreadNativeMethodRunner.
+    Run it.
- local = (jobject *) g_malloc (sizeof (jobject));
-  *local = (*gdk_env)->NewObject(gdk_env, lcl_class, ctor);
-  MAYBE_RETHROW(gdk_env, "cannot allocate a ThreadLocal");
+    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.  */
+static void
+g_thread_create_jni_impl (GThreadFunc      func,
+                          gpointer            data,
+                          gulong              stack_size 
__attribute__((unused)),
+                          gboolean            joinable,
+                          gboolean            bound __attribute__((unused)),
+                          /*  TODO: Implement priority, when we do
+                              g_thread_set_priority_jni_impl()  */
+                          GThreadPriority     priority __attribute__((unused)),
+                          /* this is horrible. this is actually  a
+                             gpointer to the thread's thread-ID.  Which is, of
+ course, a gpointer. */ + gpointer threadIDp, + /* do not touch the GError stuff if you do not have
+                             trouble. */
+ GError **errorp) +{
+  JNIEnv *env;
+  jboolean jjoinable = joinable;
+  jobject jnewThread;
+  gpointer threadID;            /* to be filled in */
+ +#if VERBOSE
+  fprintf(stderr, "g_thread_create_jni_impl(func=%p, data=%p, joinable=%d, 
threadIDp=%p, *(int *) threadIDp = %d)", (void *) func, data, joinable, threadIDp, 
*(int *) threadIDp);
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+  /* 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. */
+  jnewThread = (*env)->NewObject
+    (env, runner_class, runner_ctor, (jlong) (intptr_t) func, (jlong) 
(intptr_t) data, jjoinable);
+  if (MAYBE_RETHROW(env, "creating a new thread failed in the constructor")) {
+    *(gpointer *) threadIDp = NULL;
+    /*    g_set_error(errorp, G_THREAD_ERROR, G_THREAD_ERROR_FAILED, "creating
+          a new thread failed in the constructor"); */
+    g_set_error(errorp, 0xffff, 0xffff, "creating a new thread failed in the 
constructor");
+    goto done;
+  }
- *local = ((*gdk_env)->NewGlobalRef (gdk_env, *local));
-  MAYBE_RETHROW(gdk_env, "cannot create a GlobalRef");
+  (*env)->CallVoidMethod(env, runner_class, runner_start_mth);
+  if (MAYBE_RETHROW(env, "starting a new thread failed")) {
+    *(gpointer *) threadIDp = NULL;
+    g_set_error(errorp, 0xffff, 0xffff, "starting the new thread failed");
+    /*  g_set_error(errorp, G_THREAD_ERROR, G_THREAD_ERROR_FAILED, "starting
+        the new thread failed"); */
+    goto done;
+  }
+ + threadID = getThreadIDFromThread(env, jnewThread); - return (GPrivate*) local;
+#if 0                           /* TODO: Uncomment this when we actually get
+                                   around to setting priorities.  */
+  g_thread_set_priority_jni_impl(threadID, priority);
+#endif +
+  *(gpointer *)threadIDp = threadID;
+  SHOW_OLD_TROUBLE();
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> (threadID = %p) \n", threadID);
+#endif
 }
-/* Get this thread's value for a thread-local key. This is simply
- * ThreadLocal.get for us.
- */
-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);
+/* Wraps a call to g_thread_yield. */
+static void +g_thread_yield_jni_impl (void) +{
+  JNIEnv *env;
- lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
+#if VERBOSE
+  fprintf(stderr, "g_thread_yield_jni_impl()");
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+ + HIDE_OLD_TROUBLE; - get_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "get", "()Ljava/lang/Object;");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<get>");
+  (*env)->CallStaticVoidMethod(env, thread_class, thread_yield_mth);
+  if (MAYBE_RETHROW(env, "Thread.yield() failed")) {
+    goto done;
+  }
+ + SHOW_OLD_TROUBLE();
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> VOID\n");
+#endif
+}
- lcl_obj = (*gdk_env)->CallObjectMethod(gdk_env, *(jobject*)private, get_mth);
-  MAYBE_RETHROW(gdk_env, "cannot find thread-local object");
- int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer");
+static void +g_thread_join_jni_impl (gpointer threadID) +{
+  JNIEnv *env;
+  jobject threadObj;
- val_mth = (*gdk_env)->GetMethodID(gdk_env, int_class, "intValue", "()I");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer.<intValue>");
+#if VERBOSE
+  fprintf(stderr, "g_thread_join_jni_impl(threadID=%p) ", threadID);
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+  threadObj = getThreadFromThreadID(env, threadID);
+
+  (*env)->CallVoidMethod(env, threadObj, thread_join_mth);
+  if (MAYBE_RETHROW(env, "Thread.join() failed")) {
+    goto done;
+  }
+ - int_val = (*gdk_env)->CallIntMethod(gdk_env, lcl_obj, val_mth);
-  MAYBE_RETHROW(gdk_env, "cannot get thread local value");
+  (*env)->CallStaticVoidMethod(env, runner_class, 
runner_deRegisterJoinable_mth, threadObj);
+  if (MAYBE_RETHROW(env, "Thread.deRegisterJoinableThread() failed"))
+    goto done;
+
+  SHOW_OLD_TROUBLE();
+
+done:
+#if VERBOSE
+  fprintf(stderr, " ==> VOID \n");
+#endif
- return (gpointer) int_val;
 }
-/* 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;
+/* 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
+   normally deprecated.  That's so that we can unlock monitors on the way out
+   -- Thread.stop() throws a ThreadDeath exception, which is usually
+ unchecked. */ +static void g_thread_exit_jni_impl (void) +{
+  JNIEnv *env;
+  jobject this_thread;
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+#if VERBOSE
+  fprintf(stderr, "g_thread_exit_jni_impl() ");
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+
+  this_thread = (*env)->
+    CallStaticObjectMethod (env, thread_class, thread_current_mth);
+
+  if (MAYBE_RETHROW(env, "cannot get current thread")) {
+    g_assert( ! this_thread );
+    goto done;
+  }
+  g_assert(this_thread);
+ + (*env)->CallVoidMethod (env, this_thread, thread_stop_mth);
+  if (MAYBE_RETHROW(env, "cannot call Thread.stop() on current thread"))
+    goto done;
+  SHOW_OLD_TROUBLE();
+
+ done:
+#if VERBOSE
+  fprintf(stderr, " ==> VOID \n");
+#endif
+}
- int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer");
- new_int = (*gdk_env)->GetMethodID(gdk_env, int_class, "<init>", "(I)V");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer.<init>");
+/* To be implemented. */
+#if 0
+static int javaPriorityLevel(GThreadPriority priority) +{
+  switch (priority ) {
+  G_THREAD_PRIORITY_LOW:
+#error TODO return MIN_PRIORITY
+    break;
+ + G_THREAD_PRIORITY_NORMAL:
+#error TODO return NORM_PRIORITY
+    break;
+ + G_THREAD_PRIORITY_HIGH:
+#error TODO return (NORM_PRIORITY + MAX_PRIORITY / 2 )
+    break;
+ + G_THREAD_PRIORITY_URGENT:
+    #error TODO return MAX_PRIORITY;
+    break;
+  default:
+    /* Invalid priority setting!
+       g_assert_not_reached(); ?? */
+    return NORM_PRIORITY;
+ + }
+}
+#endif
- 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 void g_thread_set_priority_jni_impl (gpointer thread, + GThreadPriority priority) +{
+#if VERBOSE
+  fprintf(stderr, "g_thread_set_priority_jni_impl(thread=%p, priority = %u) ", 
thread, priority);
+#endif
+#if 0                          /* It is safe to replace this with a no-op for
+                                  now.  Not all platforms do thread priorities.
+ TODO: Implement Them. */ + /* We have these fields in java.lang.Thread to play with:
+
+     static int MAX_PRIORITY     The maximum priority that a thread can have.
+     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.
+  */
+#error Set priority to JavaPriorityLevel(priority);
+#endif /* 0 */
+#if VERBOSE
+  fprintf(stderr, " ==> VOID\n");
+#endif
+}
- set_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "set", "(Ljava/lang/Object;)V");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<set>");
+/** Return the result of Thread.currentThread(), a static method. */
+static void
+g_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 VERBOSE
+  fprintf(stderr, "g_thread_self_jni_impl(my_thread_IDp=%p)", my_thread_IDp);
+#endif
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+  this_thread = (*env)->
+    CallStaticObjectMethod (env, thread_class, thread_current_mth);
+  if (MAYBE_RETHROW(env, "cannot get current thread")) {
+    g_assert(! this_thread);
+    my_threadID = NULL;
+    goto done;
+  }
+ + my_threadID = getThreadIDFromThread(env, this_thread);
+  SHOW_OLD_TROUBLE();
+done:
+#if VERBOSE
+  fprintf(stderr, " ==> (my_threadID = %p) \n", my_threadID);
+#endif
+  * (gpointer *) my_thread_IDp = my_threadID;
+}
+
+
+static gboolean +g_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 VERBOSE + fprintf(stderr, "g_thread_equal_jni_impl(threadID1=%p, threadID2=%p)", + threadID1, threadID2);
+#endif
+
+  (*gdk_vm)->GetEnv (gdk_vm, (void **)&env, JNI_VERSION_1_1);
+  setup_cache(env);
+
+  HIDE_OLD_TROUBLE;
+  thread1_obj = getThreadFromThreadID(env, threadID1);
+  thread2_obj = getThreadFromThreadID(env, threadID2);
+
+  ret = (*env)->CallBooleanMethod (env, thread1_obj, thread_equals_mth, 
thread2_obj);
+
+  if (MAYBE_RETHROW(env, "Thread.equals() failed")) {
+    g_assert_not_reached();
+    goto done;
+  }
+
+  SHOW_OLD_TROUBLE();
- (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)private, set_mth, lcl_obj);
-  MAYBE_RETHROW(gdk_env, "cannot set thread local value");
+done:
+#if VERBOSE
+  fprintf(stderr, " ==> %d\n", ret);
+#endif
+
+  return ret;
 }
+
+
 /************************************************************************/
 /* GLIB interface                                                      */
 /************************************************************************/
@@ -482,26 +1522,30 @@
 /* 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
+  g_mutex_new_jni_impl,                /* mutex_new */
+  g_mutex_lock_jni_impl,       /* mutex_lock */
+  g_mutex_trylock_jni_impl,    /* mutex_trylock */
+  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 */
+  g_thread_create_jni_impl,    /* thread_create */
+  g_thread_yield_jni_impl,     /* thread_yield */
+  g_thread_join_jni_impl,      /* thread_join */
+  g_thread_exit_jni_impl,      /* thread_exit */
+  g_thread_set_priority_jni_impl, /* thread_set_priority */
+  g_thread_self_jni_impl,      /* thread_self */
+  g_thread_equal_jni_impl,     /* thread_equal */
 };
+
+/* Local Variables: */
+/* c-file-style: "gnu" */
+/* End:  */
Index: native/jni/gtk-peer/gthread-jni.h
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/gtk-peer/gthread-jni.h,v
retrieving revision 1.5
diff -I*.class -u -r1.5 gthread-jni.h
--- native/jni/gtk-peer/gthread-jni.h   15 Feb 2003 15:08:08 -0000      1.5
+++ native/jni/gtk-peer/gthread-jni.h   10 May 2004 05:40:58 -0000
@@ -45,4 +45,6 @@
 extern GThreadFunctions g_thread_jni_functions;
 extern JavaVM *gdk_vm;
+extern void disposeThreadID(gpointer threadID);
+
 #endif /* __GTHREADJNI_H__ */
--- ChangeLog.umlaut-1-fixed    2004-05-10 05:27:34.000000000 +0000
+++ ChangeLog   2004-05-10 05:39:45.000000000 +0000
@@ -1,3 +1,9 @@
+2004-05-10  Steven Augart  <address@hidden>
+
+       * --portable-native-sync fixed for GTK2.  Code cleanup still to be
+       done (including a better changelog entry); do not apply this patch
+       to the permanent CVS sources.
+
 2004-05-08  Steven Augart  <address@hidden>
* ChangeLog: Restore corrupted umlauts.


------------------------------------------------------------------------

_______________________________________________
Classpath mailing list
address@hidden
http://mail.gnu.org/mailman/listinfo/classpath

--
Steven Augart

Jikes RVM, a free, open source, Virtual Machine:
http://oss.software.ibm.com/jikesrvm




reply via email to

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