[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
--portable-native-sync working again
From: |
Steven Augart |
Subject: |
--portable-native-sync working again |
Date: |
Mon, 10 May 2004 01:45:01 -0400 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8a) Gecko/20040504 |
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.
--
Steven Augart
Jikes RVM, a free, open source, Virtual Machine:
http://oss.software.ibm.com/jikesrvm
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.
- --portable-native-sync working again,
Steven Augart <=