[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
portable-native-sync-7.patch
From: |
Steven Augart |
Subject: |
portable-native-sync-7.patch |
Date: |
Wed, 26 May 2004 17:06:50 -0400 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7b) Gecko/20040421 |
--
Steven Augart
Jikes RVM, a free, open source, Virtual Machine:
http://oss.software.ibm.com/jikesrvm
Minor changes to portable-native-sync-6.patch, so that one can
use kaffeh to regenerate the *GThreadNativeMethodRunner.h file
without undue pain. Thanks to Dalibor for suggesting this.
ChangeLog entry:
2004-05-26 Steven Augart <address@hidden>
--portable-native-sync implemented for GTK2.
* 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.
--- /dev/null 2003-01-30 10:24:37.000000000 +0000
+++ gnu/java/awt/peer/gtk/GThreadMutex.java 2004-05-26 14:58:19.000000000
+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:
--- /dev/null 2003-01-30 10:24:37.000000000 +0000
+++ gnu/java/awt/peer/gtk/GThreadNativeMethodRunner.java 2004-05-26
17:26:27.000000000 +0000
@@ -0,0 +1,309 @@
+/* GThreadNativeMethodRunner.java -- Implements pthread_create(), under
+ glib's gthread abstraction, for use with GNU Classpath's
+ --portable-native-sync option.
+ This is used by gthread-jni.c
+
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package gnu.java.awt.peer.gtk;
+import java.lang.ref.WeakReference;
+import java.util.Set;
+import java.util.Collections;
+import java.util.HashSet;
+
+/** Implements pthread_create(), under glib's gthread abstraction, for use
+ with GNU Classpath's --portable-native-sync option. This is used in
+ gthread-jni.c
+
+ Also implements a registry for threads, mapping Thread objects to small
+ integers. We use weak references for threads that aren't joinable, so
+ that they will be garbage collected.
+
+ There are a number of possible alternative implementations. To see one of
+ them, search for the (commented-out) variable myThreadID.
+
+ 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 sticks around (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 around 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;
+
+// /** A numeric code. We could use this to help along the system by
deleting
+// weak references as soon as we will no longer need them. We don't
+// actually use this at this time, though, since we would need to store
+// additional information (whether a thread was joinable or not) in order
+// to make this work. And there is not a clear efficiency gain. */
+// private final int myThreadID;
+
+ GThreadNativeMethodRunner(long funcPtr, long funcArg, boolean joinable)
+ {
+ this.funcPtr = funcPtr;
+ this.funcArg = funcArg;
+// this.myThreadID = registerThread(self); // register self numerically
+ if (joinable)
+ registerSelfJoinable();
+ }
+
+ public void run()
+ {
+ nativeRun(funcPtr, funcArg);
+ }
+
+ private native void nativeRun(long funcPtr, long funcArg);
+
+ /** THREADS is an array of threads, indexed by thread ID codes. Not sure
+ whether this is the "best" approach but it does make it O(1) to look up a
+ thread by its ID.
+
+ Zero is a valid thread ID code. Any negative number is invalid.
+
+ 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: Joinable set. Hmmmm. 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:
--- /dev/null 2003-01-30 10:24:37.000000000 +0000
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.c
2004-05-08 06:12:47.000000000 +0000
@@ -0,0 +1,68 @@
+/* Native implementation of functions in GThreadNativeMethodRunner
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+#include "gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h"
+#include "gthread-jni.h"
+
+/*
+ * Class: GThreadNativeMethodRunner
+ * Method: nativeRun
+ * Signature: (J)V
+ *
+ * Purpose: Run the C function whose function pointer is
+ *
+ */
+JNIEXPORT void JNICALL
+Java_gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_nativeRun(JNIEnv
*gdk_env, jobject lcl_obj,
+ jlong funcAddr, jlong funcArg)
+{
+ /* Convert the function's address back into a pointer to a C function. */
+ void *(*funcPtr)(void *) = (void *(*)(void *)) funcAddr;
+
+ /* We do not need to worry about the return value from funcPtr(); it's
+ just thrown away. That is part of the g_threads spec, so no reason
+ to worry about returning it. */
+ (void) funcPtr((void *) funcArg);
+ /* Fall off the end and terminate the thread of control. */
+}
+
+/* Local Variables: */
+/* c-file-style: "gnu" */
+/* End: */
+
+
--- /dev/null 2003-01-30 10:24:37.000000000 +0000
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h
2004-05-26 20:25:09.000000000 +0000
@@ -0,0 +1,30 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class gnu_java_awt_peer_gtk_GThreadNativeMethodRunner */
+
+#ifndef _Included_gnu_java_awt_peer_gtk_GThreadNativeMethodRunner
+#define _Included_gnu_java_awt_peer_gtk_GThreadNativeMethodRunner
+#ifdef __cplusplus
+extern "C" {
+#endif
+#undef gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MIN_PRIORITY
+#define gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MIN_PRIORITY 1L
+#undef gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_NORM_PRIORITY
+#define gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_NORM_PRIORITY 5L
+#undef gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MAX_PRIORITY
+#define gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MAX_PRIORITY 10L
+/* Inaccessible static: numAnonymousThreadsCreated */
+/* Inaccessible static: threads */
+/* Inaccessible static: joinable */
+/*
+ * Class: gnu_java_awt_peer_gtk_GThreadNativeMethodRunner
+ * Method: nativeRun
+ * Signature: (JJ)V
+ */
+JNIEXPORT void JNICALL
Java_gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_nativeRun
+ (JNIEnv *, jobject, jlong, jlong);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c
===================================================================
RCS file:
/cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c,v
retrieving revision 1.17
diff -I*.class -u -r1.17 gnu_java_awt_peer_gtk_GtkMainThread.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c 29 Mar 2004
07:07:27 -0000 1.17
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkMainThread.c 26 May 2004
21:05:00 -0000
@@ -63,10 +63,6 @@
JNIEnv *gdk_env;
-#ifdef PORTABLE_NATIVE_SYNC
-JavaVM *gdk_vm;
-#endif
-
GtkWindowGroup *global_gtk_window_group;
/*
@@ -87,18 +83,25 @@
NSA_INIT (env, clazz);
/* GTK requires a program's argc and argv variables, and requires that they
- be valid. */
-
- argv = (char **) malloc (sizeof (char *) * 2);
- argv[0] = "";
+ be valid. Set it up. */
+ argv = (char **) g_malloc (sizeof (char *) * 2);
+ argv[0] = (char *) g_malloc(1);
+#if 1
+ strcpy(argv[0], "");
+#else /* The following is a more efficient alternative, but less intuitively
+ * expresses what we are trying to do. This code is only run once, so
+ * I'm going for intuitive. */
+ argv[0][0] = '\0';
+#endif
argv[1] = NULL;
/* until we have JDK 1.2 JNI, assume we have a VM with threads that
match what GLIB was compiled for */
#ifdef PORTABLE_NATIVE_SYNC
- (*env)->GetJavaVM( env, &gdk_vm );
- g_thread_init ( &g_thread_jni_functions );
- printf("called gthread init\n");
+ (*env)->GetJavaVM( env, &the_vm );
+ g_thread_init ( &portable_native_sync_jni_functions );
+ /* Debugging progress message; uncomment if needed: */
+ /* printf("called gthread init\n"); */
#else
g_thread_init ( NULL );
#endif
@@ -121,16 +124,15 @@
if ((homedir = getenv ("HOME")))
{
- rcpath = (char *) malloc (strlen (homedir) + strlen (RC_FILE) + 2);
+ rcpath = (char *) g_malloc (strlen (homedir) + strlen (RC_FILE) + 2);
sprintf (rcpath, "%s/%s", homedir, RC_FILE);
}
gtk_rc_parse ((rcpath) ? rcpath : RC_FILE);
- if (rcpath)
- free (rcpath);
-
- free (argv);
+ g_free (rcpath);
+ g_free (argv[0]);
+ g_free (argv);
/* setup cached IDs for posting GTK events to Java */
/* gtkgenericpeer = (*env)->FindClass (env, */
Index: native/jni/gtk-peer/gthread-jni.c
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/gtk-peer/gthread-jni.c,v
retrieving revision 1.11
diff -I*.class -u -r1.11 gthread-jni.c
--- native/jni/gtk-peer/gthread-jni.c 30 Apr 2004 11:05:17 -0000 1.11
+++ native/jni/gtk-peer/gthread-jni.c 26 May 2004 21:05:00 -0000
@@ -40,142 +40,1112 @@
/************************************************************************/
/*
- * Julian Dolby (address@hidden)
- * February 7, 2003
+ * @author Julian Dolby (address@hidden)
+ * @date February 7, 2003
+ *
+ * @author Steven Augart
+ * <steve+classpath at augart dot com>, <augart at watson dot ibm dot com>
+ * @date April 30, 2004 -- May 10 2004: Support new functions for Glib 2.0,
+ * fix cond_wait to free and re-acquire the mutex,
+ * replaced trylock stub implementation with a full one.
*
* This code implements the GThreadFunctions interface for GLIB using
* Java threading primitives. All of the locking and conditional variable
* functionality required by GThreadFunctions is implemented using the
* monitor and wait/notify functionality of Java objects. The thread-
- * local fucntionality uses the java.lang.ThreadLocal class.
+ * local functionality uses the java.lang.ThreadLocal class.
*
- * This code is designed to be portable in that it makes no assumptions
+ * This code should be portable; I believe it makes no assumptions
* about the underlying VM beyond that it implements the JNI functionality
* that this code uses.
*
- * The one piece that does not really work is trylock for mutexes. The
- * Java locking model does not include such functionality, and I do not
- * see how to implement it without knowing something about how the VM
- * implements locking.
+ * Currently, use of this code is governed by the configuration option
+ * --enable-portable-native-sync. 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.
*
* NOTES:
*
- * I have tested it only on JikesRVM---the CVS head as of early February
- * 2003.
+ * We have tested this only on Jikes RVM---the CVS head as of
+ * early February 2003 and the CVS head as of May, 2004.
*
- * Currently, use of this code is governed by the configuration option
- * --enable-portable-native-sync
*
+ * MINOR NITS:
+ *
+ * - This file is only needed for --portable-native-sync. We
+ * conditionally compile it against the PORTABLE_NATIVE_SYNC macro. Should
+ * we manipulate Makefile.am instead? Or should we go ahead and compile
+ * it so that something which breaks this code will not be used?
+ *
+ * - Using a jboolean in the arglist to "throw()" and "rethrow()"
+ * triggers many warnings from GCC's -Wconversion operation, because that
+ * is not the same as the conversion (upcast to an int) that would occur in
+ * the absence of a prototype.
+ *
+ * It would be very slightly more efficient to just pass the jboolean, but
+ * is not worth the clutter of messages. The right solution would be to
+ * turn off the -Wconversion warning for just this file, *except* that
+ * -Wconversion also warns you against constructs such as:
+ * unsigned u = -1;
+ * and that is a useful warning. So I went from a "jboolean" to a
+ * "gboolean" (-Wconversion is not enabled by default for GNU Classpath,
+ * but it is in my own CFLAGS, which, for gcc 3.3.3, read: -pipe -ggdb3 -W
+ * -Wall -Wbad-function-cast -Wcast-align -Wpointer-arith -Wcast-qual
+ * -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
+ * -fkeep-static-consts -fkeep-inline-functions -Wundef -Wwrite-strings
+ * -Wno-aggregate-return -Wmissing-noreturn -Wnested-externs -Wtrigraphs
+ * -Wconversion -Wsign-compare -Wno-float-equal -Wmissing-format-attribute
+ * -Wno-unreachable-code -Wdisabled-optimization )
*/
+#include <config.h>
+
+/************************************************************************/
+/* Configuration */
+/************************************************************************/
+
+/** Tracing and Reporting **/
+#define TRACE_API_CALLS 0 /* announce entry and exit into each
method,
+ by printing to stderr. */
+
+#define TRACE_MONITORS 0 /* Every enterMonitor() and exitMonitor() goes
+ to stderr. */
+
+/** Trouble handling. There is a discussion below of this. **/
+#define EXPLAIN_TROUBLE 1 /* Describe any unexpected trouble that
+ happens. This is a superset
+ of EXPLAIN_BROKEN, and if set trumps an
+ unset EXPLAIN_BROKEN. It is not a strict
+ superset, since at the moment there is no
+ TROUBLE that is not also BROKEN.
+
+ Use criticalMsg() to describe the problem.
+ */
+
+#define EXPLAIN_BROKEN 1 /* Describe trouble that is serious enough to
+ be BROKEN. Right now all trouble is at
+ least BROKEN. EXPLAIN_TROUBLE, if set,
+ overrides this. */
+
+/* There is no EXPLAIN_BADLY_BROKEN definition. We always explain
+ BADLY_BROKEN trouble, since there is no other way to report it. */
+
+
+/** Error Handling **/
+#define DIE_IF_BROKEN 1 /* Dies if serious trouble happens. There is
+ really no non-serious trouble, except
+ possibly problems that arise during
+ pthread_create, which are reported by a
+ GError.
+
+ If you do not set DIE_IF_BROKEN, then
+ trouble will raise a Java RuntimeException.
+ We probably do want to die right away,
+ since anything that's BROKEN really
+ indicates a programming error or a
+ system-wide error, and that's what the glib
+ documentation says you should do in case of
+ that kind of error in a glib-style
+ function. But it does work to turn this
+ off. */
+
+#if DIE_IF_BROKEN
+#define DIE_IF_BADLY_BROKEN 1 /* DIE_IF_BROKEN implies DIE_IF_BADLY_BROKEN */
+#else
+#define DIE_IF_BADLY_BROKEN 1 /* Die if the system is badly broken --
+ that is, if we have further trouble while
+ attempting to throw an exception
+ upwards, or if we are unable to generate
+ one of the classes we'll need in order to
+ throw wrapped exceptions upward.
+
+ If unset, we will print a warning message,
+ and limp along anyway. Not that the system
+ is likely to work. */
+#endif
+
+/** Performance tuning parameters **/
+
+#define ENABLE_EXPENSIVE_ASSERTIONS 0 /* Enable expensive assertions? */
+
+#define DELETE_LOCAL_REFS 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.
+
+ TODO This code hasn't been tested yet.
+ And really hasn't been implemented yet.
+ */
/************************************************************************/
/* Global data */
/************************************************************************/
+#include <stdint.h> /* provides intptr_t */
+#include <stdarg.h> /* va_list */
#include "gthread-jni.h"
+#include <assert.h> /* assert() */
+
+/* For Java thread priority constants. */
+#include "gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h"
+
+/* The VM handle. This is set in
+ Java_gnu_java_awt_peer_gtk_GtkMainThread_gtkInit */
+JavaVM *the_vm;
+
+/* Forward Declarations for Functions */
+static int threadObj_set_priority (JNIEnv * env, jobject threadObj,
+ GThreadPriority gpriority);
+static void fatalMsg (const char fmt[], ...)
+ __attribute__ ((format (printf, 1, 2)))
+ __attribute__ ((noreturn));
-/* The VM handle. This is set in GtkToolkitMain.gtkInit */
-JavaVM *gdk_vm;
+static void criticalMsg (const char fmt[], ...)
+ __attribute__ ((format (printf, 1, 2)));
+static void tracing (const char fmt[], ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+static jint javaPriorityLevel (GThreadPriority priority)
+ __attribute__ ((const));
/************************************************************************/
-/* Utilities to reflect exceptions back to the VM */
+/* Trouble-handling, including utilities to reflect exceptions */
+/* back to the VM. Also some status reporting.
*/
/************************************************************************/
-/* This function checks for a pending exception, and rethrows it with
+/* How are we going to handle problems?
+
+ There are several approaches:
+
+ 1) Report them with the GError mechanism (this only works for
+ thread_create(), and that only for something that maps onto a POSIX
+ EAGAIN. Nothing maps onto EAGAIN. So this idea is a non-starter.)
+
+ 2) Reflect the exception back to the VM, wrapped in a RuntimeException.
+ This will fail sometimes -- but you can enable DIE_IF_BADLY_BROKEN to
+ croak in such a situation.
+
+ 3) Abort execution. Enable DIE_IF_BROKEN and/or DIE_IF_BADLY_BROKEN to
+ make this the default for BROKEN or BADLY_BROKEN trouble. This is
+ what the glib functions do if it's not appropriate to report via
+ GError.
+
+ 4) Display messages to stderr. We always do this for BADLY_BROKEN trouble.
+
+ There are some complications.
+
+ When I attempted to report a problem in g_thread_self() using g_critical (a
+ macro around g_log(), I found that g_log in turn looks for thread-private
+ data and calls g_thread_self() again.
+
+ We got a segfault -- exactly the sort of error this attempted to avoid.
+ So I don't use the g_critical() and g_error() functions any more from this
+ code. Nor do I use g_assert(); I use old-fashioned assert instead.
+
+
+
+*/
+
+
+#define WHERE __FILE__ ":" G_STRINGIFY(__LINE__) ": "
+
+/* This is portable to older compilers that lack variable-argument macros.
+ This used to be just g_critical(), but then we ran into the error reporting
+ problem discussed above.
+*/
+static void
+fatalMsg (const char fmt[], ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ fputs ("\nAborting execution\n", stderr);
+ abort ();
+}
+
+
+static void
+criticalMsg (const char fmt[], ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+ putc ('\n', stderr);
+}
+
+/* Unlike the other two, this one does not append a newline. This is only
+ used if one of the TRACE_ macros is defined. */
+static void
+tracing (const char fmt[], ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vfprintf (stderr, fmt, ap);
+ va_end (ap);
+}
+
+#define assert_not_reached() \
+ do \
+ { \
+ fputs(WHERE "You should never get here. Aborting execution.\n",
\
+ stderr); \
+ abort(); \
+ } \
+ while(0)
+
+
+#if DIE_IF_BADLY_BROKEN
+#define BADLY_BROKEN fatalMsg
+#else
+#define BADLY_BROKEN criticalMsg
+/* So, the user may still attempt to recover, even though we do not advise
+ this. */
+#endif
+
+/* I find it so depressing to have to use C without varargs macros. */
+#define BADLY_BROKEN_MSG WHERE "Something fundamental" \
+ " to GNU Classpath's AWT JNI broke while we were trying to pass up a
Java error message"
+
+#define BADLY_BROKEN0() \
+ BADLY_BROKEN(BADLY_BROKEN_MSG);
+#define BADLY_BROKEN1(msg) \
+ BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg)
+#define BADLY_BROKEN2(msg, arg) \
+ BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg)
+#define BADLY_BROKEN3(msg, arg, arg2) \
+ BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg1, arg2)
+#define BADLY_BROKEN4(msg, arg, arg2, arg3) \
+ BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg1, arg2, arg3)
+
+#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;
+
+ assert ( ! (*env)->ExceptionCheck (env));
+
+ /* java.lang.Object and its methods */
+ lcl_class = (*env)->FindClass (env, "java/lang/Object");
+ if (!lcl_class)
+ {
+ BROKEN (env, "cannot find java.lang.Object");
+ return initialized = -1;
+ }
+
+ /* Pin it down. */
+ obj_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+ DELETE_LOCAL_REF (env, lcl_class);
+ if (!obj_class)
+ {
+ BROKEN (env, "Cannot get a global reference to java.lang.Object");
+ return initialized = -1;
+ }
+
+ obj_ctor = (*env)->GetMethodID (env, obj_class, "<init>", "()V");
+ if (!obj_ctor)
+ {
+ BROKEN (env, "cannot find constructor for java.lang.Object");
+ return initialized = -1;
+ }
+
+ obj_notify_mth = (*env)->GetMethodID (env, obj_class, "notify", "()V");
+ if ( ! obj_notify_mth )
+ {
+ BROKEN (env, "cannot find java.lang.Object.notify()V");
+ return initialized = -1;
+ }
+
+ obj_notifyall_mth =
+ (*env)->GetMethodID (env, obj_class, "notifyAll", "()V");
+ if ( ! obj_notifyall_mth)
+ {
+ BROKEN (env, "cannot find java.lang.Object.notifyall()V");
+ return initialized = -1;
+ }
+
+ obj_wait_mth = (*env)->GetMethodID (env, obj_class, "wait", "()V");
+ if ( ! obj_wait_mth )
+ {
+ BROKEN (env, "cannot find Object.<wait()V>");
+ return initialized = -1;
+ }
+
+ obj_wait_nanotime_mth =
+ (*env)->GetMethodID (env, obj_class, "wait", "(JI)V");
+ if ( ! obj_wait_nanotime_mth )
+ {
+ BROKEN (env, "cannot find Object.<wait(JI)V>");
+ return initialized = -1;
+ }
+
+ /* GThreadMutex and its methods */
+ lcl_class = (*env)->FindClass (env, "gnu/java/awt/peer/gtk/GThreadMutex");
+ if ( ! lcl_class)
+ {
+ BROKEN (env, "cannot find gnu.java.awt.peer.gtk.GThreadMutex");
+ return initialized = -1;
+ }
+ /* Pin it down. */
+ mutex_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+ DELETE_LOCAL_REF (env, lcl_class);
+ if ( ! mutex_class)
+ {
+ BROKEN (env, "Cannot get a global reference to GThreadMutex");
+ return initialized = -1;
+ }
+
+ mutex_ctor = (*env)->GetMethodID (env, mutex_class, "<init>", "()V");
+ if ( ! mutex_ctor)
+ {
+ BROKEN (env, "cannot find zero-arg constructor for GThreadMutex");
+ return initialized = -1;
+ }
+
+ mutex_potentialLockers_fld = (*env)->GetFieldID
+ (env, mutex_class, "potentialLockers", "I");
+ if ( ! mutex_class )
+ {
+ BROKEN (env, "cannot find GThreadMutex.potentialLockers");
+ return initialized = -1;
+ }
+
+ if (! (mutex_lockForPotentialLockers_fld = (*env)->GetFieldID
+ (env, mutex_class, "lockForPotentialLockers", "Ljava/lang/Object;")))
+ {
+ BROKEN (env, "cannot find GThreadMutex.lockForPotentialLockers");
+ return initialized = -1;
+ }
+
+
+ /* java.lang.Thread */
+ if (! (lcl_class = (*env)->FindClass (env, "java/lang/Thread")))
+ {
+ BROKEN (env, "cannot find java.lang.Thread");
+ return initialized = -1;
+ }
+
+ /* Pin it down. */
+ thread_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
+ DELETE_LOCAL_REF (env, lcl_class);
+ if (!thread_class)
+ {
+ BROKEN (env, "Cannot get a global reference to java.lang.Thread");
+ return initialized = -1;
+ }
+
+ 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 +1154,721 @@
/************************************************************************/
/* 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. Or so the JNI book says.
+ I personally fail to see how it can never fail -- one might give it a
+ NULL object, for instance. That would throw a NullPointerException in
+ Java, so why not here?
+
+ In any case, retrieving a NULL object is cause for an internal error
+ -- there would then be a mutex object that was not properly intialized. */
+ assert (mcache->lockForPotentialLockersObj);
+
+ return 0;
+}
+
+
+/* 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;
+
+ potentialLockers = (*env)->GetIntField
+ (env, mutexObj, mutex_potentialLockers_fld);
+ if (MAYBE_BROKEN
+ (env, "cannot read GThreadMutex.potentialLockers field's value"))
+ return -1;
+
+ ++potentialLockers;
+
+ (*env)->SetIntField
+ (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+
+ if (EXIT_MONITOR (env, mcache->lockForPotentialLockersObj))
+ return -1;
+
+ if (ENTER_MONITOR (env, mcache->lockObj))
+ return -1;
+
+ SHOW_OLD_TROUBLE ();
+
+ return 0;
+}
+
+/* Unlock a GMutex, once we're already in JNI and have already gotten the
+ mutexObj for it. This skips the messages that TRACE_API_CALLS would
+ print.
+
+ Returns -1 on error, 0 on success. */
+static int
+mutexObj_unlock (JNIEnv * env, jobject mutexObj,
+ struct mutexObj_cache *mcache)
+{
+ jint potentialLockers;
+ int ret = -1; /* assume failure until we suceed. */
+
+ /* Free the lock first, so that someone waiting for the lock can get it
+ ASAP. */
+ /* This is guaranteed not to block. */
+ if (EXIT_MONITOR (env, mcache->lockObj) < 0)
+ goto done;
+
+ /* Kick down potentialLockers by one. We do this AFTER we free the lock, so
+ that we hold it no longer than necessary. */
+ if (ENTER_MONITOR (env, mcache->lockForPotentialLockersObj) < 0)
+ goto done;
+
+ potentialLockers = (*env)->GetIntField
+ (env, mutexObj, mutex_potentialLockers_fld);
+ /* Never fails, so the JNI book says. */
+
+ assert (potentialLockers >= 1);
+ --potentialLockers;
+
+ (*env)->SetIntField
+ (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+ /* Never fails, so the JNI book says. */
+
+ /* Clean up. */
+ if (EXIT_MONITOR (env, mcache->lockForPotentialLockersObj) < 0)
+ goto done;
+ ret = 0;
+
+done:
+ return ret;
}
-/* 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))
+ goto done;
+
+
+ /* We have the monitor. Record that fact. */
+ potentialLockers = 1;
+ (*env)->SetIntField
+ (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
+ /* Set*Field() never fails */
+
+ /* Clean up. */
+ if (EXIT_MONITOR (env, mcache.lockForPotentialLockersObj))
+ goto done;
+
+ SHOW_OLD_TROUBLE ();
+
+ ret = TRUE; /* We have the lock. */
+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);
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+ assert (mutexObj);
- releaseLock(gdk_env, mutex);
+ if ( populate_mutexObj_cache (env, mutexObj, &mcache) < 0)
+ goto done;
+
+ (void) mutexObj_unlock (env, mutexObj, &mcache);
+
+ SHOW_OLD_TROUBLE ();
+
+done:
+ clean_mutexObj_cache (env, &mcache);
+ if (TRACE_API_CALLS)
+ tracing (" ==> VOID\n");
}
-/* Free a mutex (isn't C fun?) */
-static void g_mutex_free_jni_impl (GMutex *mutex)
+
+
+/* Free a mutex (isn't C fun?). OK this time for it to be NULL.
+ No failure conditions, for a change. */
+static void
+mutex_free_jni_impl (GMutex * mutex)
{
- freePlainObject( (jobject*)mutex );
+ jobject mutexObj = (jobject) mutex;
+ JNIEnv *env;
+
+ (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+
+ if (TRACE_API_CALLS)
+ tracing ("mutex_free_jni_impl(%p)", mutexObj);
+
+ freeObject (env, mutexObj);
+
+ if (TRACE_API_CALLS)
+ tracing (" ==> VOID\n");
}
+
+
/************************************************************************/
/* Condition variable code */
/************************************************************************/
/* Create a new condition variable. This is a java.lang.Object for us. */
-static GCond *g_cond_new_jni_impl () {
- return (GCond*)allocatePlainObject();
+static GCond *
+cond_new_jni_impl (void)
+{
+ jobject condObj;
+ JNIEnv *env;
+
+ if (TRACE_API_CALLS)
+ tracing ("mutex_free_jni_impl()");
+
+ (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+
+ condObj = allocatePlainObject (env);
+
+ if (TRACE_API_CALLS)
+ tracing (" ==> %p\n", condObj);
+
+ return (GCond *) condObj;
}
/* Signal on a condition variable. This is simply calling Object.notify
* for us.
*/
-static void g_cond_signal_jni_impl (GCond *cond) {
- jclass lcl_class;
- jmethodID signal_mth;
- JNIEnv *gdk_env;
+static void
+cond_signal_jni_impl (GCond * gcond)
+{
+ JNIEnv *env;
+ jobject condObj = (jobject) gcond;
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+ if (TRACE_API_CALLS)
+ tracing ("cond_signal_jni_impl(condObj = %p)", condObj);
- lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
- MAYBE_RETHROW(gdk_env, "cannot find Object");
-
- signal_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notify", "()V");
- MAYBE_RETHROW(gdk_env, "cannot find Object.<notify>");
+ (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+ if (setup_cache (env) < 0)
+ goto done;
+ HIDE_OLD_TROUBLE (env);
+
+ assert (condObj);
/* Must have locked an object to call notify */
- takeLock(gdk_env, cond);
+ if (ENTER_MONITOR (env, condObj))
+ goto done;
- (*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;
+ }
+
+ EXIT_MONITOR (env, condObj);
- releaseLock(gdk_env, cond);
+ 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;
- (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, bcast_mth);
- MAYBE_RETHROW(gdk_env, "cannot broadcast to mutex");
+ (*env)->CallVoidMethod (env, condObj, obj_notifyall_mth);
+ if (MAYBE_BROKEN (env, "cannot broadcast to mutex with Object.notify()"))
+ {
+ EXIT_MONITOR (env, condObj);
+ goto done;
+ }
- releaseLock(gdk_env, cond);
+ EXIT_MONITOR (env, condObj);
+
+ SHOW_OLD_TROUBLE ();
+
+done:
+ if (TRACE_API_CALLS)
+ tracing (" ==> VOID\n");
}
-/* Wait on a condition variable. For us, this simply means call
+/* Wait on a condition variable. For us, this simply means calling
* Object.wait.
+ *
+ * Throws a Java exception on trouble; may leave the mutexes set arbitrarily.
+ * XXX TODO: Further improve error recovery.
*/
-static void g_cond_wait_jni_impl
- (GCond *cond, GMutex *mutex __attribute__((unused)))
+static void
+cond_wait_jni_impl (GCond * gcond, GMutex * gmutex)
{
- jclass lcl_class;
- jmethodID wait_mth;
- JNIEnv *gdk_env;
-
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
-
- lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
- MAYBE_RETHROW(gdk_env, "cannot find Object");
-
- wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "()V");
- MAYBE_RETHROW(gdk_env, "cannot find Object.<wait>");
-
- /* Must have locked an object to call wait */
- takeLock(gdk_env, cond);
-
- (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth);
- MAYBE_RETHROW(gdk_env, "cannot wait on mutex");
+ struct mutexObj_cache cache;
+ jobject condObj = (jobject) gcond;
+ jobject mutexObj = (jobject) gmutex;
+ JNIEnv *env;
+
+ if (TRACE_API_CALLS)
+ tracing ("cond_wait_jni_impl(condObj=%p, mutexObj=%p)",
+ condObj, mutexObj);
+
+ (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+ if (setup_cache (env) < 0)
+ goto done;
+ HIDE_OLD_TROUBLE (env);
+
+ assert (condObj);
+ assert (mutexObj);
+ /* Must have locked a Java object to call wait on it */
+ if (ENTER_MONITOR (env, condObj) < 0)
+ goto done;
+
+ /* Our atomicity is now guaranteed; we're protected by the Java monitor on
+ condObj. Unlock the GMutex. */
+ if (mutexObj_unlock (env, mutexObj, &cache))
+ goto done;
+
+ (*env)->CallVoidMethod (env, condObj, obj_wait_mth);
+ if (MAYBE_BROKEN (env, "cannot wait on condObj"))
+ {
+ EXIT_MONITOR (env, condObj); /* ignore err checking */
+ goto done;
+ }
- releaseLock(gdk_env, cond);
+ /* Re-acquire the lock on the GMutex. Do this while we're protected by the
+ Java monitor on condObj. */
+ if (mutexObj_lock (env, mutexObj, &cache))
+ goto done;
+
+ EXIT_MONITOR (env, condObj);
+
+ SHOW_OLD_TROUBLE ();
+
+done:
+ if (TRACE_API_CALLS)
+ tracing (" ==> VOID\n");
}
-/* Wait on a condition vairable until a timeout. This is a little tricky
+
+/** Wait on a condition variable until a timeout. This is a little tricky
* for us. We first call Object.wait(J) giving it the appropriate timeout
* value. On return, we check whether an InterruptedException happened. If
- * so, that is Java-speak for wait timing out.
+ * so, that is Java-speak for wait timing out.
+ *
+ * We return FALSE if we timed out. Return TRUE if the condition was
+ * signalled first, before we timed out.
+ *
+ * In case of trouble we throw a Java exception. Whether we return FALSE or
+ * TRUE depends upon whether the condition was raised before the trouble
+ * happened.
+ *
+ * I believe that this function goes to the proper lengths to try to unlock
+ * all of the locked mutexes and monitors, as appropriate, and that it further
+ * tries to make sure that the thrown exception is the current one, not any
+ * future cascaded one from something like a failure to unlock the monitors.
*/
static gboolean
-g_cond_timed_wait_jni_impl
- (GCond *cond, GMutex *mutex __attribute__((unused)),
- GTimeVal *end_time)
+cond_timed_wait_jni_impl (GCond * gcond, GMutex * gmutex, GTimeVal * end_time)
{
- jclass lcl_class;
- jmethodID wait_mth;
- JNIEnv *gdk_env;
- jlong time;
+ JNIEnv *env;
+ jlong time_millisec;
+ jint time_nanosec;
jthrowable cause;
+ jobject condObj = (jobject) gcond;
+ jobject mutexObj = (jobject) gmutex;
+ gboolean condRaised = FALSE; /* Condition has not been raised yet. */
+ struct mutexObj_cache cache;
+ gboolean interrupted;
+
+ if (TRACE_API_CALLS)
+ {
+ tracing ("cond_timed_wait_jni_impl(cond=%p, mutex=%p,"
+ " end_time=< sec=%lu, usec=%lu >)", condObj, mutexObj,
+ (unsigned long) end_time->tv_sec,
+ (unsigned long) end_time->tv_usec);
+ }
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
- lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
- MAYBE_RETHROW(gdk_env, "cannot find Object");
-
- wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "(J)V");
- MAYBE_RETHROW(gdk_env, "cannot find Object.<wait(J)>");
-
- time = end_time->tv_sec*1000;
- time += end_time->tv_usec/1000;
+ (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+ if (setup_cache (env) < 0)
+ goto done;
+ HIDE_OLD_TROUBLE (env);
+
+ time_millisec = end_time->tv_sec * 1000 + end_time->tv_usec / 1000;
+ time_nanosec = 1000 * (end_time->tv_usec % 1000);
/* Must have locked an object to call wait */
- takeLock(gdk_env, cond);
+ if (ENTER_MONITOR (env, condObj) < 0)
+ goto done;
+
+ if (mutexObj_unlock (env, mutexObj, &cache) < 0)
+ {
+ if (EXIT_MONITOR (env, condObj) < 0)
+ criticalMsg
+ ("Unable to unlock an existing lock on a condition; your proram may
deadlock");
+ goto done;
+ }
- (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth, time);
- if ((cause = (*gdk_env)->ExceptionOccurred(gdk_env)) != NULL) {
- jclass intr = (*gdk_env)->FindClass (gdk_env,
"java.lang.InterruptedException");
- if ( (*gdk_env)->IsInstanceOf(gdk_env, cause, intr) ) {
- releaseLock(gdk_env, cond);
- return FALSE;
- } else {
- MAYBE_RETHROW(gdk_env, "error in timed wait");
+ (*env)->CallVoidMethod (env, condObj, obj_wait_nanotime_mth,
+ time_millisec, time_nanosec);
+
+ /* If there was trouble, save that fact, and the reason for the trouble. We
+ want to respond to this condition as fast as possible. */
+ cause = (*env)->ExceptionOccurred (env);
+
+ if ( ! cause )
+ {
+ condRaised = TRUE; /* condition was signalled */
+ }
+ else if ((*env)->IsInstanceOf (env, cause, interrupted_exception_class))
+ {
+ condRaised = FALSE; /* Condition was not raised before timeout.
+ (This is redundant with the initialization
+ of condRaised above) */
+ (*env)->ExceptionClear (env); /* Clear the InterruptedException. */
+ cause = NULL; /* no pending cause now. */
+ }
+ else
+ {
+ interrupted = FALSE; /* Trouble, but not because of
+ InterruptedException. Assume the condition
+ was not raised. */
+ /* Leave condRaised set to FALSE */
}
- }
- releaseLock(gdk_env, cond);
+ /* Irrespective of whether there is a pending problem to report, go ahead
+ and try to clean up. This may end up throwing an exception that is
+ different from the one that was thrown by the call to Object.wait().
+ So we will override it with the first exception (don't want to have
+ cascading problems). */
+ if (mutexObj_lock (env, mutexObj, &cache) && !cause)
+ {
+ cause = (*env)->ExceptionOccurred (env);
+ assert (cause);
+ }
+
+ if (EXIT_MONITOR (env, condObj) && !cause)
+ {
+ cause = (*env)->ExceptionOccurred (env);
+ assert (cause);
+ }
- return TRUE;
+ if (cause) /* Raise the first cause. */
+ {
+ BROKEN_CAUSE (env, cause, "error in timed wait or during its cleanup");
+ goto done;
+ }
+
+ SHOW_OLD_TROUBLE ();
+
+done:
+ if (TRACE_API_CALLS)
+ tracing (" ==> condRaised = %s\n", condRaised ? "TRUE" : "FALSE");
+ return condRaised;
}
-/* Free a condition variable. (isn't C fun?) */
-static void g_cond_free_jni_impl (GCond *cond) {
- freePlainObject( (jobject*)cond );
+
+/* Free a condition variable. (isn't C fun?). Can not fail. */
+static void
+cond_free_jni_impl (GCond * cond)
+{
+ jobject condObj = (jobject) cond;
+ JNIEnv *env;
+
+ if (TRACE_API_CALLS)
+ tracing ("cond_free_jni_impl(condObj = %p)", condObj);
+ (*the_vm)->GetEnv (the_vm, (void **) &env, JNI_VERSION_1_1);
+
+ freeObject (env, condObj);
+
+ if (TRACE_API_CALLS)
+ tracing (" ==> VOID\n");
}
@@ -380,128 +1876,616 @@
/* 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". But they don't seem to
+ have thoroughly considered cases like resource exhaustion, at least not
+ with respect to these functions. The only error defined for the 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. And if we run out of Java
+ memory, we are almost certainly not going to get any more. Never the
+ less, it does not seem appropriate for
+*/
+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;
+
+
+ (*env)->CallStaticVoidMethod
+ (env, runner_class, runner_deRegisterJoinable_mth, threadObj);
+ if (MAYBE_BROKEN (env, "Thread.deRegisterJoinableThread() failed"))
+ goto done;
- (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
+ SHOW_OLD_TROUBLE ();
- int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
- MAYBE_RETHROW(gdk_env, "cannot find Integer");
+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:
- new_int = (*gdk_env)->GetMethodID(gdk_env, int_class, "<init>", "(I)V");
- MAYBE_RETHROW(gdk_env, "cannot find Integer.<init>");
+ 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; shut up GCC warnings. Apparently glib (at
+ least the installation on my machine) doesn't tell GNU C that
+ assert_not_reached() will never return. */
+ case G_THREAD_PRIORITY_NORMAL:
+ return normJPri;
+ break;
+
+ case G_THREAD_PRIORITY_HIGH:
+ return (normJPri + maxJPri) / 2;
+ break;
+
+ case G_THREAD_PRIORITY_URGENT:
+ return maxJPri;
+ break;
+ }
+}
+
+
+/** It would be safe not to implement this, according to the JNI docs, since
+ not all platforms do thread priorities. However, we might as well
+ provide the hint for those who want it.
+*/
+static void
+thread_set_priority_jni_impl (gpointer gThreadID, GThreadPriority gpriority)
+{
+ jobject threadObj = 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);
- lcl_obj = (*gdk_env)->NewObject(gdk_env, int_class, new_int, (jint)data);
- MAYBE_RETHROW(gdk_env, "cannot create an Integer");
+ if (setup_cache (env) < 0)
+ goto done;
- lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
- MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
+ HIDE_OLD_TROUBLE (env);
- set_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "set",
"(Ljava/lang/Object;)V");
- MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<set>");
- (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)private, set_mth, lcl_obj);
- MAYBE_RETHROW(gdk_env, "cannot set thread local value");
+ threadObj = getThreadFromThreadID (env, gThreadID);
+ if ( ! threadObj) /* Reported with BROKEN already. */
+ goto done;
+
+ if (threadObj_set_priority (env, threadObj, gpriority))
+ goto done;
+
+ SHOW_OLD_TROUBLE ();
+
+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;
+}
+
+
+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;
+ }
+
+ 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" "jint" "jlong" "jobject" "jstring" "jthrowable" ) */
+/* End: */
Index: NEWS
===================================================================
RCS file: /cvsroot/classpath/classpath/NEWS,v
retrieving revision 1.39
diff -I*.class -u -r1.39 NEWS
--- NEWS 2 May 2004 13:47:32 -0000 1.39
+++ NEWS 26 May 2004 21:05:00 -0000
@@ -1,3 +1,13 @@
+New in release 0.10 (in preparation)
+
+* Java primitives can once again be used to support AWT native
+ threading; this had been broken since Classpath release 0.06, when
+ we upgraded to GTK+2. This is enabled with the
+ --enable-portable-native-sync configure option.
+* We intend that the next release of GNU Classpath will require the VM
+ to provide JNI 1.2. If there are serious problems with this, please
+ raise them now.
+
New in release 0.09 (2004/02/02)
* Includes updated GNU JAXP version from 2004-02-01.
- portable-native-sync-7.patch,
Steven Augart <=