classpath
[Top][All Lists]
Advanced

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

Re: --portable-native-sync; own threading system and AWT


From: Steven Augart
Subject: Re: --portable-native-sync; own threading system and AWT
Date: Sat, 08 May 2004 15:44:31 -0400
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8a) Gecko/20040504

Steven Augart wrote:
Dear Classpath Folks:

I spent some time this weekend working on fixing the --portable-native-sync support in Classpath; it's been broken since Classpath started using GTK 2. Sorry I didn't get it done in time for 0.09 to go out. I expect to have something that works out by Wednesday (Thursday for Jim Huang in Taiwan). This should make Claspath's AWT work again on Jikes RVM and on Kaffe, as well as on any other VM that uses its own threading system rather than the underlying Linux pthread system.

If anybody else has been working on this (Jim Huang (jserv) has already shared his partial implementation with me and we've merged code bases please let me know so that we don't duplicate effort; ideally, you'll share your half-written code with me too :).

I am still debugging this. Sorry that it took longer than I expected. It took about 700 lines of new code to handle all of the new methods and the lingering problems with the previous implementation (thread_trylock never succeeded; method ID and class ID were never cached.).

I do not plan to put more work into this over the rest of the weekend, but will take it up again early next week.

I have attached a patch that gives the (not yet fully debugged) changes I've made so far, in case someone else (Jim Huang?) wants to work on this. If you do anything with it, please write back to me or to the list so that we don't duplicate effort.

--
Steven Augart

Jikes RVM, a free, open source, Virtual Machine:
http://oss.software.ibm.com/jikesrvm
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   8 May 2004 19:38:40 -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,106 +80,348 @@
 /* Global data                                                         */
 /************************************************************************/
 
+#include <stdint.h>             /* provides intptr_t */
 #include "gthread-jni.h"
+#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 *gdk_lclenv) 
+{
+  static int exception_cache_initialized = 0;
+  
+  if (exception_cache_initialized)
+    return;
+  runtimeException_class = (*gdk_lclenv)->FindClass (gdk_lclenv,
+                                        "java/lang/RuntimeException");
+  if ((*gdk_lclenv)->ExceptionOccurred(gdk_lclenv)) {
+    /* XXX TODO Improve error handling here. */
+    fprintf(stderr, "Serious trouble: classpath couldn't find 
java/lang/RuntimeException\n");
+    g_assert(0);
+  }
+  
+
+  runtimeException_ctor 
+    = (*gdk_lclenv)->GetMethodID(gdk_lclenv, runtimeException_class, "<init>",
+                              "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+  if ((*gdk_lclenv)->ExceptionOccurred(gdk_lclenv)) {
+    /* XXX TODO Improve error handling here. */
+    fprintf(stderr, "Serious trouble: classpath couldn't find a two-arg 
constructor for java/lang/RuntimeException\n");
+    g_assert(0);
+  }
+  
+  ++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
+
+/* 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.
  */
-static void maybe_rethrow(JNIEnv *gdk_env, char *message, char *file, int 
line) {
+static void 
+maybe_rethrow(JNIEnv *gdk_lclenv, const char *message, const char *file, int 
line) 
+{
   jthrowable cause;
 
-    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;
+  jstring jmessage;
+  jobject obj;
+  /* rethrow if an exception happened */
+  if ((cause = (*gdk_lclenv)->ExceptionOccurred(gdk_lclenv)))
+    {
+
+      /* allocate local message in Java */
+      size_t len = strlen(message) + strlen(file) + 25;
+      char *buf = (char *) malloc(len);
+      if (buf)
+       {
+         bzero(buf, len);
+         sprintf(buf, "%s (at %s:%d)", message, file, line);
+         jmessage = (*gdk_lclenv)->NewStringUTF(gdk_lclenv, 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);
-      }
+      setup_exception_cache(gdk_lclenv);
+
+      /* Create the RuntimeException wrapper object */
+      obj = (*gdk_lclenv)->NewObject (gdk_lclenv, runtimeException_class, 
+                                   runtimeException_ctor, jmessage, cause);
+
+      if ((*gdk_lclenv)->ExceptionOccurred(gdk_lclenv)) 
+        {
+          /* I think this should never happen.  Unless there are bugs in my
+             JNI code. */
+          fprintf(stderr, "GNU Classpath: Failure of JNI NewObject() while 
creating a java.lang.RuntimeException object.  We were trying to warn about the 
following failure:\n");
+          fprintf(stderr, "%s:%d: %s\n", file, line, message);
+          g_assert_not_reached();
+          abort();
+        }
+      
+
+      /* throw it */
+      (*gdk_lclenv)->Throw(gdk_lclenv, (jthrowable) obj);
+      fprintf(stderr, "GNU Classpath: Failure of JNI Throw.  We were trying to 
warn about the following failure:\n");
+      fprintf(stderr, "%s:%d: %s\n", file, line, message);
+      g_assert_not_reached();
+      abort();                  /* for good measure. */
+    }
 }
 
 /* This macro is used to include a source location in the exception message */
 #define MAYBE_RETHROW(_class, _message) \
-maybe_rethrow(_class, _message, __FILE__, __LINE__)
+       maybe_rethrow(_class, _message, __FILE__, __LINE__)
+
+
+/**********************************************************/
+/***** 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 *gdk_lclenv)
+{
+  static int initialized = 0;
+  
+  if (initialized)
+    return;
+  setup_exception_cache(gdk_lclenv); /* make sure we can report on trouble */
+
+  /* java.lang.Object and its methods */
+  obj_class = (*gdk_lclenv)->FindClass (gdk_lclenv, "java/lang/Object");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find java.lang.Object");
+
+  obj_ctor = (*gdk_lclenv)->GetMethodID(gdk_lclenv, obj_class, "<init>", 
"()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find constructor for java.lang.Object");
+
+  obj_notify_mth = (*gdk_lclenv)->GetMethodID(gdk_lclenv, obj_class, "notify", 
"()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find java.lang.Object.notify()V");
+
+  obj_notifyall_mth = (*gdk_lclenv)->GetMethodID(gdk_lclenv, obj_class, 
"notifyAll", "()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find java.lang.Object.notifyAll()V");
+
+  obj_wait_mth = (*gdk_lclenv)->GetMethodID(gdk_lclenv, obj_class, "wait", 
"()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find Object.<wait()V>");
+
+  obj_wait_nanotime_mth = (*gdk_lclenv)->GetMethodID(gdk_lclenv, obj_class, 
"wait", "(JI)V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find Object.<wait(JI)V>");
+  
+
+  /* GThreadMutex and its methods */
+  mutex_class = (*gdk_lclenv)->FindClass (gdk_lclenv, 
"gnu/java/awt/peer/gtk/GThreadMutex");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find gnu.java.awt.peer.gtk.GThreadMutex");
+
+  mutex_ctor = (*gdk_lclenv)->GetMethodID(gdk_lclenv, mutex_class, "<init>", 
"()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find zero-arg constructor for 
GThreadMutex");
+
+  mutex_potentialLockers_fld = (*gdk_lclenv)->GetFieldID
+    (gdk_lclenv, mutex_class, "potentialLockers", "I");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find GThreadMutex.potentialLockers");
+
+  mutex_lockForPotentialLockers_fld = (*gdk_lclenv)->GetFieldID
+    (gdk_lclenv, mutex_class, "lockForPotentialLockers", "Ljava/lang/Object;");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find 
GThreadMutex.lockForPotentialLockers");
+
+  mutex_lock_fld = (*gdk_lclenv)->GetFieldID(gdk_lclenv, mutex_class,
+                                             "lock", "Ljava/lang/Object;");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find GThreadMutex.lock");
+ 
+  /* java.lang.Thread */
+  thread_class = (*gdk_lclenv)->FindClass (gdk_lclenv, "java/lang/Thread");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find java.lang.Thread");
+
+  thread_current_mth
+    = (*gdk_lclenv)->GetStaticMethodID(gdk_lclenv, thread_class, 
"currentThread", "()Ljava/lang/Thread;");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find Thread.currentThread() method");
+
+  thread_equals_mth
+    = (*gdk_lclenv)->GetMethodID(gdk_lclenv, thread_class, "equals", 
"(Ljava/lang/Object;)Z");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find Thread.equals() method");
+
+  thread_join_mth
+    = (*gdk_lclenv)->GetMethodID(gdk_lclenv, thread_class, "join", "()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find Thread.join() method");
+
+  thread_stop_mth
+    = (*gdk_lclenv)->GetMethodID(gdk_lclenv, thread_class, "stop", "()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find Thread.stop() method");
+
+  thread_yield_mth
+    = (*gdk_lclenv)->GetStaticMethodID(gdk_lclenv, thread_class, "yield", 
"()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find Thread.yield() method");
+
+
+  /* java.lang.ThreadLocal */
+  threadlocal_class = (*gdk_lclenv)->FindClass (gdk_lclenv, 
"java/lang/ThreadLocal");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find ThreadLocal");
+
+  threadlocal_ctor = (*gdk_lclenv)->GetMethodID(gdk_lclenv, threadlocal_class, 
"<init>", "()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find ThreadLocal.<init>()V");
+  
+  threadlocal_set_mth = (*gdk_lclenv)->GetMethodID(gdk_lclenv, 
threadlocal_class, "set", "(Ljava/lang/Object;)V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find ThreadLocal.set(Object)V");
+
+  threadlocal_get_mth = (*gdk_lclenv)->GetMethodID(gdk_lclenv, 
threadlocal_class, "get", "()Ljava/lang/Object;");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find java.lang.ThreadLocal.get()Object");
+
+
+  /* java.lang.Long */
+  long_class = (*gdk_lclenv)->FindClass (gdk_lclenv, "java/lang/Long");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find class java.lang.Long");
+      
+  long_ctor = (*gdk_lclenv)->GetMethodID(gdk_lclenv, long_class, "<init>", 
"(J)V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find method java.lang.Long.<init>(J)V");
+      
+  long_longValue_mth = (*gdk_lclenv)->GetMethodID(gdk_lclenv, long_class, 
"longValue", "()J");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find method java.lang.Long.longValue()J");
+
+      
+  /* GThreadNativeMethodRunner */
+  runner_class = (*gdk_lclenv)->FindClass (gdk_lclenv, 
"gnu/java/awt/peer/gtk/GThreadNativeMethodRunner");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find 
gnu.java.awt.peer.gtk.GThreadNativeMethodRunner");
+
+  runner_ctor = (*gdk_lclenv)->GetMethodID(gdk_lclenv, runner_class, "<init>", 
"(JJZ)V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find method 
GThreadNativeMethodRunner.<init>(JJZ)");
+      
+  runner_start_mth = (*gdk_lclenv)->GetMethodID(gdk_lclenv, runner_class, 
"start", "()V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find method 
GThreadNativeMethodRunner.start()V");
+
+  runner_threadToThreadID_mth = (*gdk_lclenv)->GetStaticMethodID(gdk_lclenv, 
runner_class, "threadToThreadID", "(Ljava/lang/Thread;)I");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find method 
GThreadNativeMethodRunner.threadToThreadID(java.lang.Thread)I");
+
+  runner_threadIDToThread_mth = (*gdk_lclenv)->GetStaticMethodID(gdk_lclenv, 
runner_class, "threadIDToThread", "(I)Ljava/lang/Thread;");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find method 
GThreadNativeMethodRunner.threadIDToThread(I)java.lang.Thread");
+
+  runner_deRegisterJoinable_mth = (*gdk_lclenv)->GetStaticMethodID(gdk_lclenv, 
runner_class, "deRegisterJoinable", "(Ljava/lang/Thread;)V");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find method 
GThreadNativeMethodRunner.deRegisterJoinable(java.lang.Thread)V");
+
+
+
+  /* java.lang.InterruptedException */
+  interrupted_exception_class 
+    = (*gdk_lclenv)->FindClass (gdk_lclenv, "java/lang/InterruptedException");
+  MAYBE_RETHROW(gdk_lclenv, "cannot find class 
java.lang.InterruptedException");
+
+  ++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");
+static jobject *
+allocatePlainObject(void) 
+{
+  jobject *objp;
+  JNIEnv *gdk_lclenv;
 
-  ctor = (*gdk_env)->GetMethodID(gdk_env, obj_class, "<init>", "()V");
-  MAYBE_RETHROW(gdk_env, "cannot find constructor");
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
-  obj = (jobject *) g_malloc (sizeof (jobject));
-  *obj = (*gdk_env)->NewObject (gdk_env, obj_class, ctor);
-  MAYBE_RETHROW(gdk_env, "cannot allocate object");
+  objp = (jobject *) g_malloc (sizeof (jobject));
+  *objp = (*gdk_lclenv)->NewObject (gdk_lclenv, obj_class, obj_ctor);
+  MAYBE_RETHROW(gdk_lclenv, "cannot allocate object");
   
-  *obj = (*gdk_env)->NewGlobalRef (gdk_env, *obj);
-  MAYBE_RETHROW(gdk_env, "cannot make global ref");
+  *objp = (*gdk_lclenv)->NewGlobalRef (gdk_lclenv, *objp);
+  MAYBE_RETHROW(gdk_lclenv, "cannot make global ref");
 
-  return obj;
+  return objp;
 }
 
-/*  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 *gdk_lclenv;
+    (*gdk_vm)->GetEnv (gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
 
-    (*gdk_env)->DeleteGlobalRef (gdk_env, *obj);
-    MAYBE_RETHROW(gdk_env, "cannot delete global ref");
+    (*gdk_lclenv)->DeleteGlobalRef (gdk_lclenv, *obj);
+    MAYBE_RETHROW(gdk_lclenv, "cannot delete global ref");
   
     g_free (obj);
   }
@@ -180,119 +429,285 @@
 
 
 /************************************************************************/
+/* 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 *obj;
+  JNIEnv *gdk_lclenv;
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
+
+  obj = (jobject *) g_malloc (sizeof (jobject));
+  *obj = (*gdk_lclenv)->NewObject (gdk_lclenv, mutex_class, mutex_ctor);
+  MAYBE_RETHROW(gdk_lclenv, "cannot allocate a GThreadMutex");
+  
+  *obj = (*gdk_lclenv)->NewGlobalRef (gdk_lclenv, *obj);
+  MAYBE_RETHROW(gdk_lclenv, "cannot make global ref");
+
+  return obj;
+}
+
+
+/************************************************************************/
 /* Locking code                                                                
*/
 /************************************************************************/
 
 /* 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");
+static void enterMonitor(JNIEnv *gdk_lclenv, jobject mutexObj) {
+  (*gdk_lclenv)->MonitorEnter (gdk_lclenv, mutexObj);
+  MAYBE_RETHROW(gdk_lclenv, "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");
+static void exitMonitor(JNIEnv *gdk_lclenv, jobject mutexObj) {
+    (*gdk_lclenv)->MonitorExit (gdk_lclenv, mutexObj);
+  MAYBE_RETHROW(gdk_lclenv, "cannot exit monitor ");
 }
 
+/************************************************************************/
+/* Miscellaneous utilities                                             */
+/************************************************************************/
+
+/* Get the Java Thread object that corresponds to a particular thread ID  */
+static jobject 
+getThreadFromThreadID(JNIEnv *gdk_lclenv, gpointer gThreadID)
+{
+  jint threadNum = (jint) gThreadID;
+
+  jobject thread = (*gdk_lclenv)->CallStaticObjectMethod
+    (gdk_lclenv, runner_class, runner_threadToThreadID_mth, threadNum);
+  MAYBE_RETHROW(gdk_lclenv, "cannot get Thread for threadID ");
+
+  return thread;
+}
+
+/* Return the unique threadID of THREAD.  */
+static gpointer
+getThreadIDFromThread(JNIEnv *gdk_lclenv, jobject thread)
+{
+/*   if ( ! (*gdk_lclenv)->IsInstanceOf(gdk_lclenv, this_thread, thread_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 = (*gdk_lclenv)->CallStaticIntMethod
+    (gdk_lclenv, runner_class, runner_threadToThreadID_mth, thread);
+  MAYBE_RETHROW(gdk_lclenv, "cannot get ThreadID for a Thread ");
+
+  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();
+  return (GMutex*) allocateMutexObject();
 }
 
 /* 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;
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  JNIEnv *gdk_lclenv;
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
-  takeLock(gdk_env, mutex);
+  /* We'll need to record that we have the lock. */
+  lockForPotentialLockersObj = (*gdk_lclenv)->GetObjectField 
+    (gdk_lclenv, mutexObj, mutex_lockForPotentialLockers_fld);
+  MAYBE_RETHROW(gdk_lclenv, "cannot read GThreadMutex.lockForPotentialLockers 
field's value");
+
+  /* Get the lock object itself now. */
+  lockObj = (*gdk_lclenv)->GetObjectField 
+    (gdk_lclenv, mutexObj, mutex_lock_fld);
+  MAYBE_RETHROW(gdk_lclenv, "cannot read GThreadMutex.lock field's value");
+
+  enterMonitor(gdk_lclenv, lockForPotentialLockersObj);
+
+  potentialLockers =   (*gdk_lclenv)->GetIntField 
+    (gdk_lclenv, mutexObj, mutex_potentialLockers_fld);
+  MAYBE_RETHROW(gdk_lclenv, "cannot read GThreadMutex.potentialLockers field's 
value");
+
+  ++potentialLockers;
+
+  (*gdk_lclenv)->SetIntField
+    (gdk_lclenv, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+  MAYBE_RETHROW(gdk_lclenv, "cannot write GThreadMutex.potentialLockers 
field's value");
+
+  exitMonitor(gdk_lclenv, lockForPotentialLockersObj);
+  
+  enterMonitor(gdk_lclenv, lockObj);
 }
 
-/*  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 *mutex)
 {
-  /* XXX Shall we implement this in a VM-specific way under a flag? */
-  return FALSE;
+  jobject lockForPotentialLockersObj, lockObj;
+  jobject mutexObj = *(jobject *)mutex;
+  jint potentialLockers;
+
+  JNIEnv *gdk_lclenv;
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
+
+  /* Check the value of mutex.potentialLockers */
+  lockForPotentialLockersObj = (*gdk_lclenv)->GetObjectField 
+    (gdk_lclenv, mutexObj, mutex_lockForPotentialLockers_fld);
+  MAYBE_RETHROW(gdk_lclenv, "cannot read GThreadMutex.lockForPotentialLockers 
field's value");
+
+  enterMonitor(gdk_lclenv, lockForPotentialLockersObj);
+
+  potentialLockers =   (*gdk_lclenv)->GetIntField 
+    (gdk_lclenv, mutexObj, mutex_potentialLockers_fld);
+  MAYBE_RETHROW(gdk_lclenv, "cannot read GThreadMutex.potentialLockers field's 
value");
+
+  g_assert(potentialLockers >= 0);
+  
+  if (potentialLockers) {
+    /* Already locked.  Clean up and leave. */
+    exitMonitor(gdk_lclenv, lockForPotentialLockersObj);
+    return 0;                   /* false */
+  }
+  
+  /* So, get the lock itself now. */
+  lockObj = (*gdk_lclenv)->GetObjectField 
+    (gdk_lclenv, mutexObj, mutex_lock_fld);
+  MAYBE_RETHROW(gdk_lclenv, "cannot read GThreadMutex.lock field's value");
+
+  g_assert(potentialLockers == 0);
+  enterMonitor(gdk_lclenv, lockObj); /* this is guaranteed not to block. */
+
+  /* We have the monitor.  Record that fact. */
+  potentialLockers = 1;         /* must be zero. */
+  (*gdk_lclenv)->SetIntField 
+    (gdk_lclenv, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+  MAYBE_RETHROW(gdk_lclenv, "cannot write GThreadMutex.potentialLockers 
field's value");
+  
+  /* Clean up. */
+  exitMonitor(gdk_lclenv, lockForPotentialLockersObj);
+
+  return 1;                     /* We have it! */
 }
 
 /* Unlock a mutex. */
-static void g_mutex_unlock_jni_impl (GMutex *mutex) {
-  JNIEnv *gdk_env;
+static void 
+g_mutex_unlock_jni_impl (GMutex *mutex) 
+{
+  jobject mutexObj = *(jobject *) mutex;
+  jobject lockObj, lockForPotentialLockersObj;
+  jint potentialLockers;
+  
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  JNIEnv *gdk_lclenv;
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
+
+#if 0                           /* old implementation */
+  exitMonitor(gdk_lclenv, mutex);
+#endif
+  
+    /* So, get the lock itself now. */
+  lockObj = (*gdk_lclenv)->GetObjectField 
+    (gdk_lclenv, mutexObj, mutex_lock_fld);
+  MAYBE_RETHROW(gdk_lclenv, "cannot read GThreadMutex.lock field's value");
+
+  exitMonitor(gdk_lclenv, lockObj); /* this is guaranteed not to block. */
+
+  /* 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 = (*gdk_lclenv)->GetObjectField 
+    (gdk_lclenv, mutexObj, mutex_lockForPotentialLockers_fld);
+  MAYBE_RETHROW(gdk_lclenv, "cannot read GThreadMutex.lockForPotentialLockers 
field's value");
+
+  enterMonitor(gdk_lclenv, lockForPotentialLockersObj);
+    potentialLockers =   (*gdk_lclenv)->GetIntField 
+    (gdk_lclenv, mutexObj, mutex_potentialLockers_fld);
+  MAYBE_RETHROW(gdk_lclenv, "cannot read GThreadMutex.potentialLockers field's 
value");
+
+  g_assert(potentialLockers >= 1);
+  --potentialLockers;
+    (*gdk_lclenv)->SetIntField 
+    (gdk_lclenv, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+  MAYBE_RETHROW(gdk_lclenv, "cannot write GThreadMutex.potentialLockers 
field's value");
 
-  releaseLock(gdk_env, mutex);
+  /* Clean up. */
+  exitMonitor(gdk_lclenv, lockForPotentialLockersObj);
 }
 
 /* Free a mutex (isn't C fun?) */
 static void g_mutex_free_jni_impl (GMutex *mutex)
 {
-  freePlainObject( (jobject*)mutex );
+  freeObject( (jobject*)mutex );
 }
 
 
+
+
 /************************************************************************/
 /* Condition variable code                                             */
 /************************************************************************/
 
 /* Create a new condition variable.  This is a java.lang.Object for us. */
-static GCond *g_cond_new_jni_impl () {
+static GCond *g_cond_new_jni_impl (void) 
+{
   return (GCond*)allocatePlainObject();
 }
 
 /*  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 *cond)
+{
+  JNIEnv *gdk_lclenv;
+  jobject condObj = *(jobject *) cond;
 
-  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>");
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
   /* Must have locked an object to call notify */
-  takeLock(gdk_env, cond);
+  enterMonitor(gdk_lclenv, condObj);
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, signal_mth);
-  MAYBE_RETHROW(gdk_env, "cannot signal mutex");
+  (*gdk_lclenv)->CallVoidMethod (gdk_lclenv, condObj, obj_notify_mth);
+  MAYBE_RETHROW(gdk_lclenv, "cannot signal mutex with Object.notify()");
 
-  releaseLock(gdk_env, cond);
+  exitMonitor(gdk_lclenv, condObj);
 }
 
 /*  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;
-
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+static void g_cond_broadcast_jni_impl (GCond *cond) 
+{
+  jobject condObj = *(jobject *) cond;
+  JNIEnv *gdk_lclenv;
 
-  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>");
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
   /* Must have locked an object to call notifyAll */
-  takeLock(gdk_env, cond);
+  enterMonitor(gdk_lclenv, condObj);
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, bcast_mth);
-  MAYBE_RETHROW(gdk_env, "cannot broadcast to mutex");
+  (*gdk_lclenv)->CallVoidMethod(gdk_lclenv, condObj, obj_notifyall_mth);
+  MAYBE_RETHROW(gdk_lclenv, "cannot broadcast to mutex with Object.notify()");
 
-  releaseLock(gdk_env, cond);
+  exitMonitor(gdk_lclenv, condObj);
 }
 
 
@@ -302,28 +717,22 @@
 static void g_cond_wait_jni_impl
   (GCond *cond, GMutex *mutex __attribute__((unused)))
 {
-  jclass lcl_class;
-  jmethodID wait_mth;
-  JNIEnv *gdk_env;
+  jobject condObj = *(jobject *) cond;
+  JNIEnv *gdk_lclenv;
 
-  (*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>");
+  (*gdk_vm)->GetEnv(gdk_vm, (void **) &gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
   /* Must have locked an object to call wait */
-  takeLock(gdk_env, cond);
+  enterMonitor(gdk_lclenv, condObj);
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth);
-  MAYBE_RETHROW(gdk_env, "cannot wait on mutex");
+  (*gdk_lclenv)->CallVoidMethod(gdk_lclenv, condObj, obj_wait_mth);
+  MAYBE_RETHROW(gdk_lclenv, "cannot wait on mutex");
 
-  releaseLock(gdk_env, cond);
+  exitMonitor(gdk_lclenv, condObj);
 }
 
-/*  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.
@@ -333,46 +742,39 @@
   (GCond *cond, GMutex *mutex __attribute__((unused)),
    GTimeVal *end_time)
 {
-  jclass lcl_class;
-  jmethodID wait_mth;
-  JNIEnv *gdk_env;
-  jlong time;
+  JNIEnv *gdk_lclenv;
+  jlong time_millisec;
+  jint time_nanosec;
   jthrowable cause;
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
-  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;
+  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);
-
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth, time);
+  enterMonitor (gdk_lclenv, cond);
 
-  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;
+  (*gdk_lclenv)->CallVoidMethod(gdk_lclenv, *(jobject*)cond, 
obj_wait_nanotime_mth, time_millisec, time_nanosec);
+  if ((cause = (*gdk_lclenv)->ExceptionOccurred(gdk_lclenv))) {
+    if ( (*gdk_lclenv)->IsInstanceOf(gdk_lclenv, cause, 
interrupted_exception_class) ) {
+      exitMonitor(gdk_lclenv, cond);
+      return FALSE;
     } else {
-      MAYBE_RETHROW(gdk_env, "error in timed wait");
+      MAYBE_RETHROW(gdk_lclenv, "error in timed wait");
     }
   }
 
-  releaseLock(gdk_env, cond);
+  exitMonitor(gdk_lclenv, cond);
 
   return TRUE;
 }
 
 /* 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) 
+{
+  freeObject( (jobject*)cond );
 }
 
 
@@ -386,25 +788,18 @@
 static GPrivate *g_private_new_jni_impl
   (GDestroyNotify notify __attribute__((unused)))
 {
-  jclass lcl_class;
+  JNIEnv *gdk_lclenv;
   jobject *local;
-  JNIEnv *gdk_env;
-  jmethodID ctor;
 
-  (*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>");
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
   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_lclenv)->NewObject(gdk_lclenv, threadlocal_class, 
threadlocal_ctor);
+  MAYBE_RETHROW(gdk_lclenv, "cannot allocate a ThreadLocal");
   
-  *local = ((*gdk_env)->NewGlobalRef (gdk_env, *local));
-  MAYBE_RETHROW(gdk_env, "cannot create a GlobalRef");
+  *local = ((*gdk_lclenv)->NewGlobalRef (gdk_lclenv, *local));
+  MAYBE_RETHROW(gdk_lclenv, "cannot create a GlobalRef");
 
   return (GPrivate*) local;
 }
@@ -412,69 +807,262 @@
 /*  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;
+static gpointer 
+g_private_get_jni_impl (GPrivate *g_key) 
+{
+  JNIEnv *gdk_lclenv;
   jobject lcl_obj;
-  JNIEnv *gdk_env;
-  jmethodID get_mth;
-  jclass int_class;
-  jmethodID val_mth;
-  jint int_val;
+  jobject *java_keyp = (jobject *)g_key;
+  jlong long_val;
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
+
+  lcl_obj = (*gdk_lclenv)->CallObjectMethod (gdk_lclenv, *java_keyp, 
+                                         threadlocal_get_mth);
+  MAYBE_RETHROW(gdk_lclenv, "cannot find thread-local object");
+
+  if (! lcl_obj ) {
+    /* It's Java's  "null" object.  No ref found. */
+    return NULL;
+  }
+  
+  long_val = (*gdk_lclenv)->CallLongMethod (gdk_lclenv, lcl_obj, 
long_longValue_mth);
+  MAYBE_RETHROW(gdk_lclenv, "cannot get thread local value");
+
+  return (gpointer) (intptr_t) long_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 
thread_specific_data )  
+{
+  JNIEnv *gdk_lclenv;
+  jobject lcl_obj;
+
 
-  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
+  /* 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. 
+  */
+  lcl_obj = (*gdk_lclenv)->NewObject(gdk_lclenv, long_class, long_ctor, 
+                                     (jlong) (intptr_t) thread_specific_data);
+  MAYBE_RETHROW(gdk_lclenv, "cannot create a java.lang.Long");
+  
+  /* At this point, we now have set lcl_obj as a numeric class that wraps
+     around the thread-specific data we were given. */
+  (*gdk_lclenv)->CallVoidMethod(gdk_lclenv, *(jobject*)private, 
threadlocal_set_mth, lcl_obj);
+  MAYBE_RETHROW(gdk_lclenv, "cannot set thread local value");
+}
 
-  get_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "get", 
"()Ljava/lang/Object;");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<get>");
 
-  lcl_obj = (*gdk_env)->CallObjectMethod(gdk_env, *(jobject*)private, get_mth);
-  MAYBE_RETHROW(gdk_env, "cannot find thread-local object");
+/** Create an object of type gnu.java.awt.peer.gtk.GThreadNativeMethodRunner.
+    Run it.
 
-  int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer");
+    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, 
+                          GError             **error) 
+{
+  JNIEnv *gdk_lclenv;
+  jboolean jjoinable = joinable;
+  jobject jnewThread;
+  gpointer threadID;            /* to be filled in */
+  
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
-  val_mth = (*gdk_env)->GetMethodID(gdk_env, int_class, "intValue", "()I");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer.<intValue>");
+  /* 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 = (*gdk_lclenv)->NewObject
+    (gdk_lclenv, runner_class, runner_ctor, (jlong) (intptr_t) func, (jlong) 
(intptr_t) data, jjoinable);
+  MAYBE_RETHROW(gdk_lclenv, "creating a new thread failed in the constructor");
+  
+  (*gdk_lclenv)->CallVoidMethod(gdk_lclenv, runner_class, runner_start_mth);
+  MAYBE_RETHROW(gdk_lclenv, "starting a new thread failed");
+  
+  *error = 0;
+  threadID = getThreadIDFromThread(gdk_lclenv, jnewThread);
 
-  int_val = (*gdk_env)->CallIntMethod(gdk_env, lcl_obj, val_mth);
-  MAYBE_RETHROW(gdk_env, "cannot get thread local value");
+#if 0                           /* TODO: Uncomment this when we actually get
+                                   around to setting priorities.  */
+  g_thread_set_priority_jni_impl(threadID, priority);
+#endif  
 
-  return (gpointer) int_val;
+  *(gpointer *)threadIDp = threadID;
 }
 
-/*  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;
 
-  (*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 *gdk_lclenv;
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
+  
+  (*gdk_lclenv)->CallStaticVoidMethod(gdk_lclenv, thread_class, 
thread_yield_mth);
+  MAYBE_RETHROW(gdk_lclenv, "Thread.yield() failed");
+
+}
+
+
+static void 
+g_thread_join_jni_impl (gpointer threadID) 
+{
+  JNIEnv *gdk_lclenv;
+  jobject threadObj;
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
+
+  threadObj = getThreadFromThreadID(gdk_lclenv, threadID);
+
+  (*gdk_lclenv)->CallVoidMethod(gdk_lclenv, threadObj, thread_join_mth);
+  MAYBE_RETHROW(gdk_lclenv, "Thread.join() failed");
+
+  (*gdk_lclenv)->CallStaticVoidMethod(gdk_lclenv, runner_class, 
runner_deRegisterJoinable_mth, threadObj);
+  MAYBE_RETHROW(gdk_lclenv, "Thread.deRegisterJoinableThread() failed");
+}
+
+/* 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 *gdk_lclenv;
+  jobject this_thread;
+
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
 
-  int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer");
+  this_thread = (*gdk_lclenv)->
+    CallStaticObjectMethod (gdk_lclenv, thread_class, thread_current_mth);
+  MAYBE_RETHROW(gdk_lclenv, "cannot get current thread");
+                                                    
+  (*gdk_lclenv)->CallVoidMethod (gdk_lclenv, this_thread, thread_stop_mth);
+  MAYBE_RETHROW(gdk_lclenv, "cannot call Thread.stop() on current thread");
+}
 
-  new_int = (*gdk_env)->GetMethodID(gdk_env, int_class, "<init>", "(I)V");
-  MAYBE_RETHROW(gdk_env, "cannot find Integer.<init>");
 
-  lcl_obj = (*gdk_env)->NewObject(gdk_env, int_class, new_int, (jint)data);
-  MAYBE_RETHROW(gdk_env, "cannot create an Integer");
+/* 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!
+       abort(); ?? */
+    return NORM_PRIORITY;
+ 
+  }
+}
+#endif
 
-  lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
 
-  set_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "set", 
"(Ljava/lang/Object;)V");
-  MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<set>");
+static void g_thread_set_priority_jni_impl (gpointer thread, 
+                                          GThreadPriority priority) 
+{
+#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 */
+}
 
-  (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)private, set_mth, lcl_obj);
-  MAYBE_RETHROW(gdk_env, "cannot set thread local value");
+/** 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 *gdk_lclenv;
+  jobject this_thread;
+  
+  (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
+
+  this_thread = (*gdk_lclenv)->
+    CallStaticObjectMethod (gdk_lclenv, thread_class, thread_current_mth);
+  MAYBE_RETHROW(gdk_lclenv, "cannot get current thread");
+                                                    
+  *(gpointer *)my_thread_IDp = getThreadIDFromThread(gdk_lclenv, this_thread);
 }
 
 
+static gboolean 
+g_thread_equal_jni_impl(gpointer thread1, gpointer thread2) 
+{
+  JNIEnv *gdk_lclenv;
+  jobject thread1_obj, thread2_obj;
+  gboolean ret;
+  
+  (*gdk_vm)->GetEnv (gdk_vm, (void **)&gdk_lclenv, JNI_VERSION_1_1);
+  setup_cache(gdk_lclenv);
+
+  thread1_obj = getThreadFromThreadID(gdk_lclenv, thread1);
+  thread2_obj = getThreadFromThreadID(gdk_lclenv, thread2);
+
+  ret = (*gdk_lclenv)->CallBooleanMethod (gdk_lclenv, thread1_obj, 
thread_equals_mth, thread2_obj);
+  MAYBE_RETHROW(gdk_lclenv, "Thread.equals() failed");
+
+  return ret;
+}
+
+
+
+
 /************************************************************************/
 /* GLIB interface                                                      */
 /************************************************************************/
@@ -482,26 +1070,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   8 May 2004 19:38:40 -0000
@@ -45,4 +45,6 @@
 extern GThreadFunctions g_thread_jni_functions;
 extern JavaVM *gdk_vm;
 
+extern void disposeThreadID(gpointer threadID);
+
 #endif /* __GTHREADJNI_H__ */
--- /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

reply via email to

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