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