diff options
author | Tom Tromey <tromey@redhat.com> | 2005-07-16 01:27:14 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2005-07-16 01:27:14 +0000 |
commit | b0fa81eea9a270f23d6ad67ca7a6d25c18d20da1 (patch) | |
tree | 8762d1f992e2f725a6bde1ff966ed6f1e5f4f823 /libjava/jni/gtk-peer/gthread-jni.c | |
parent | ea54b29342c8506acb4f858c68340c44b72e3532 (diff) | |
download | gcc-b0fa81eea9a270f23d6ad67ca7a6d25c18d20da1.zip gcc-b0fa81eea9a270f23d6ad67ca7a6d25c18d20da1.tar.gz gcc-b0fa81eea9a270f23d6ad67ca7a6d25c18d20da1.tar.bz2 |
Major merge with Classpath.
Removed many duplicate files.
* HACKING: Updated.x
* classpath: Imported new directory.
* standard.omit: New file.
* Makefile.in, aclocal.m4, configure: Rebuilt.
* sources.am: New file.
* configure.ac: Run Classpath configure script. Moved code around
to support. Disable xlib AWT peers (temporarily).
* Makefile.am (SUBDIRS): Added 'classpath'
(JAVAC): Removed.
(AM_CPPFLAGS): Added more -I options.
(BOOTCLASSPATH): Simplified.
Completely redid how sources are built.
Include sources.am.
* include/Makefile.am (tool_include__HEADERS): Removed jni.h.
* include/jni.h: Removed (in Classpath).
* scripts/classes.pl: Updated to look at built classes.
* scripts/makemake.tcl: New file.
* testsuite/libjava.jni/jni.exp (gcj_jni_compile_c_to_so): Added
-I options.
(gcj_jni_invocation_compile_c_to_binary): Likewise.
From-SVN: r102082
Diffstat (limited to 'libjava/jni/gtk-peer/gthread-jni.c')
-rw-r--r-- | libjava/jni/gtk-peer/gthread-jni.c | 2592 |
1 files changed, 0 insertions, 2592 deletions
diff --git a/libjava/jni/gtk-peer/gthread-jni.c b/libjava/jni/gtk-peer/gthread-jni.c deleted file mode 100644 index be822ce..0000000 --- a/libjava/jni/gtk-peer/gthread-jni.c +++ /dev/null @@ -1,2592 +0,0 @@ -/* gthread-jni.c -- JNI threading routines for GLIB - Copyright (C) 1998, 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., 51 Franklin Street, Fifth Floor, Boston, MA -02110-1301 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. */ - -/************************************************************************/ -/* Header */ -/************************************************************************/ - -/* - * @author Julian Dolby (dolby@us.ibm.com) - * @date February 7, 2003 implemented for GLIB v.1 - * - * - * @author Steven Augart - * <steve+classpath at augart dot com>, <augart at watson dot ibm dot com> - * @date April 30, 2004 -- May 10 2004: Support new functions for Glib v.2, - * fix cond_wait to free and re-acquire the mutex, - * replaced trylock stub implementation with a full one. - * - * This code implements the GThreadFunctions interface for GLIB using - * Java threading primitives. All of the locking and conditional variable - * functionality required by GThreadFunctions is implemented using the - * monitor and wait/notify functionality of Java objects. The thread- - * local functionality uses the java.lang.ThreadLocal class. - * - * Classpath's AWT support uses GTK+ peers. GTK+ uses GLIB. GLIB by default - * uses the platform's native threading model -- pthreads in most cases. If - * the Java runtime doesn't use the native threading model, then it needs this - * code in order to use Classpath's (GTK+-based) AWT routines. - * - * This code should be portable; I believe it makes no assumptions - * about the underlying VM beyond that it implements the JNI functionality - * that this code uses. - * - * Currently, use of this code is governed by the configuration option - * --enable-portable-native-sync. We will soon add a VM hook so the VM can - * select which threading model it wants to use at run time; at that point, - * the configuration option will go away. - * - * The code in this file uses only JNI 1.1, except for one JNI 1.2 function: - * GetEnv, in the JNI Invocation API. (There seems to be no way around using - * GetEnv). - * - * ACKNOWLEDGEMENT: - * - * I would like to thank Mark Wielaard for his kindness in spending at least - * six hours of his own time in reviewing this code and correcting my GNU - * coding and commenting style. --Steve Augart - * - * - * NOTES: - * - * This code has been tested with Jikes RVM and with Kaffe. - * - * This code should have proper automated unit tests. I manually tested it - * by running an application that uses AWT. --Steven Augart - * - * MINOR NIT: - * - * - Using a jboolean in the arglist to "throw()" and "rethrow()" - * triggers many warnings from GCC's -Wconversion operation, because that - * is not the same as the conversion (upcast to an int) that would occur in - * the absence of a prototype. - * - * It would be very slightly more efficient to just pass the jboolean, but - * is not worth the clutter of messages. The right solution would be to - * turn off the -Wconversion warning for just this file, *except* that - * -Wconversion also warns you against constructs such as: - * unsigned u = -1; - * and that is a useful warning. So I went from a "jboolean" to a - * "gboolean" (-Wconversion is not enabled by default for GNU Classpath, - * but it is in my own CFLAGS, which, for gcc 3.3.3, read: -pipe -ggdb3 -W - * -Wall -Wbad-function-cast -Wcast-align -Wpointer-arith -Wcast-qual - * -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations - * -fkeep-static-consts -fkeep-inline-functions -Wundef -Wwrite-strings - * -Wno-aggregate-return -Wmissing-noreturn -Wnested-externs -Wtrigraphs - * -Wconversion -Wsign-compare -Wno-float-equal -Wmissing-format-attribute - * -Wno-unreachable-code -Wdisabled-optimization ) - */ - -#include <config.h> - -/************************************************************************/ -/* Configuration */ -/************************************************************************/ - -/** Tracing and Reporting **/ -#define TRACE_API_CALLS 0 /* announce entry and exit into each method, - by printing to stderr. */ - -#define TRACE_MONITORS 0 /* Every enterMonitor() and exitMonitor() goes - to stderr. */ - -/** Trouble handling. There is a discussion below of this. **/ -#define EXPLAIN_TROUBLE 1 /* Describe any unexpected trouble that - happens. This is a superset - of EXPLAIN_BROKEN, and if set trumps an - unset EXPLAIN_BROKEN. It is not a strict - superset, since at the moment there is no - TROUBLE that is not also BROKEN. - - Use criticalMsg() to describe the problem. - */ - -#define EXPLAIN_BROKEN 1 /* Describe trouble that is serious enough to - be BROKEN. (Right now all trouble is at - least BROKEN.) */ - -/* There is no EXPLAIN_BADLY_BROKEN definition. We always explain - BADLY_BROKEN trouble, since there is no other way to report it. */ - - -/** Error Handling **/ -#define DIE_IF_BROKEN 1 /* Dies if serious trouble happens. There is - really no non-serious trouble, except - possibly problems that arise during - pthread_create, which are reported by a - GError. - - If you do not set DIE_IF_BROKEN, then - trouble will raise a Java RuntimeException. - We probably do want to die right away, - since anything that's BROKEN really - indicates a programming error or a - system-wide error, and that's what the glib - documentation says you should do in case of - that kind of error in a glib-style - function. But it does work to turn this - off. */ - -#if DIE_IF_BROKEN -#define DIE_IF_BADLY_BROKEN 1 /* DIE_IF_BROKEN implies DIE_IF_BADLY_BROKEN */ -#else -#define DIE_IF_BADLY_BROKEN 1 /* Die if the system is badly broken -- - that is, if we have further trouble while - attempting to throw an exception - upwards, or if we are unable to generate - one of the classes we'll need in order to - throw wrapped exceptions upward. - - If unset, we will print a warning message, - and limp along anyway. Not that the system - is likely to work. */ -#endif - -/** Performance tuning parameters **/ - -#define ENABLE_EXPENSIVE_ASSERTIONS 0 /* Enable expensive assertions? */ - -#define DELETE_LOCAL_REFS 1 /* Whether to delete local references. - - JNI only guarantees that there wil be 16 - available. (Jikes RVM provides an number - only limited by VM memory.) - - Jikes RVM will probably perform faster if - this is turned off, but other VMs may need - this to be turned on in order to perform at - all, or might need it if things change. - - Remember, we don't know how many of those - local refs might have already been used up - by higher layers of JNI code that end up - calling g_thread_self(), - g_thread_set_private(), and so on. - - We set this to 1 for GNU Classpath, since - one of our principles is "always go for the - most robust implementation" */ - -#define HAVE_JNI_VERSION_1_2 0 /* Assume we don't. We could - dynamically check for this. We will - assume JNI 1.2 in later versions of - Classpath. - - As it stands, the code in this file - already needs one JNI 1.2 function: - GetEnv, in the JNI Invocation API. - - TODO This code hasn't been tested yet. - And really hasn't been implemented yet. - */ - -/************************************************************************/ -/* Global data */ -/************************************************************************/ - -#if defined HAVE_STDINT_H -#include <stdint.h> /* provides intptr_t */ -#elif defined HAVE_INTTYPES_H -#include <inttypes.h> -#endif -#include <stdarg.h> /* va_list */ -#include <glib.h> -#include "gthread-jni.h" -#include <assert.h> /* assert() */ - -/* For Java thread priority constants. */ -#include <gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h> - -/* Since not all JNI header generators actually define constants we - define them here explicitly. */ -#ifndef gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MIN_PRIORITY -#define gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MIN_PRIORITY 1 -#endif -#ifndef gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_NORM_PRIORITY -#define gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_NORM_PRIORITY 5 -#endif -#ifndef gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MAX_PRIORITY -#define gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MAX_PRIORITY 10 -#endif - -/* The VM handle. This is set in - Java_gnu_java_awt_peer_gtk_GtkMainThread_gtkInit */ -JavaVM *the_vm; - -/* Unions used for type punning. */ -union env_union -{ - void **void_env; - JNIEnv **jni_env; -}; - -union func_union -{ - void *void_func; - GThreadFunc g_func; -}; - -/* 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)); - -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)); - -/************************************************************************/ -/* Trouble-handling, including utilities to reflect exceptions */ -/* back to the VM. Also some status reporting. */ -/************************************************************************/ - -/* How are we going to handle problems? - - There are several approaches: - - 1) Report them with the GError mechanism. - - (*thread_create)() is the only one of these functions that takes a - GError pointer. And the only G_THREAD error defined maps onto EAGAIN. - We don't have any errors in our (*thread_create)() implementation that - can be mapped to EAGAIN. So this idea is a non-starter. - - 2) Reflect the exception back to the VM, wrapped in a RuntimeException. - This will fail sometimes, if we're so broken (BADLY_BROKEN) that we - fail to throw the exception. - - 3) Abort execution. This is what the glib functions themselves do for - errors that they can't report via GError. - - Enable DIE_IF_BROKEN and/or DIE_IF_BADLY_BROKEN to - make this the default for BROKEN and/or BADLY_BROKEN trouble. - - 4) Display messages to stderr. We always do this for BADLY_BROKEN - trouble. The glib functions do that for errors they can't report via - GError. - - There are some complications. - - When I attempted to report a problem in g_thread_self() using g_critical (a - macro around g_log(), I found that g_log in turn looks for thread-private - data and calls g_thread_self() again. - - We got a segfault, probably due to stack overflow. So, this code doesn't - use the g_critical() and g_error() functions any more. Nor do we use - g_assert(); we use the C library's assert() instead. -*/ - - -#define WHERE __FILE__ ":" G_STRINGIFY(__LINE__) ": " - -/* This is portable to older compilers that lack variable-argument macros. - This used to be just g_critical(), but then we ran into the error reporting - problem discussed above. -*/ -static void -fatalMsg (const char fmt[], ...) -{ - va_list ap; - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); - fputs ("\nAborting execution\n", stderr); - abort (); -} - - -static void -criticalMsg (const char fmt[], ...) -{ - va_list ap; - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); - putc ('\n', stderr); -} - -/* Unlike the other two, this one does not append a newline. This is only - used if one of the TRACE_ macros is defined. */ -static void -tracing (const char fmt[], ...) -{ - va_list ap; - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); -} - -#define assert_not_reached() \ - do \ - { \ - fputs(WHERE "You should never get here. Aborting execution.\n", \ - stderr); \ - abort(); \ - } \ - while(0) - - -#if DIE_IF_BADLY_BROKEN -#define BADLY_BROKEN fatalMsg -#else -#define BADLY_BROKEN criticalMsg -/* So, the user may still attempt to recover, even though we do not advise - this. */ -#endif - -/* I find it so depressing to have to use C without varargs macros. */ -#define BADLY_BROKEN_MSG WHERE "Something fundamental" \ - " to GNU Classpath's AWT JNI broke while we were trying to pass up a Java error message" - -#define BADLY_BROKEN0() \ - BADLY_BROKEN(BADLY_BROKEN_MSG); -#define BADLY_BROKEN1(msg) \ - BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg) -#define BADLY_BROKEN2(msg, arg) \ - BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg) -#define BADLY_BROKEN3(msg, arg, arg2) \ - BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg1, arg2) -#define BADLY_BROKEN4(msg, arg, arg2, arg3) \ - BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg1, arg2, arg3) - -#define DELETE_LOCAL_REF(env, ref) \ - do \ - { \ - if ( DELETE_LOCAL_REFS ) \ - { \ - (*env)->DeleteLocalRef (env, ref); \ - (ref) = NULL; \ - } \ - } \ - while(0) - -/* Cached info for Exception-wrapping */ - -jclass runtimeException_class; /* java.lang.RuntimeException */ -jmethodID runtimeException_ctor; /* constructor for it */ - - -/* Throw a new RuntimeException. It may wrap around an existing exception. - 1 if we did rethrow, -1 if we had trouble while rethrowing. - isBroken is always true in this case. */ -static int -throw (JNIEnv * env, jthrowable cause, const char *message, - gboolean isBroken, const char *file, int line) -{ - jstring jmessage; - gboolean describedException = FALSE; /* Did we already describe the - exception to stderr or the - equivalent? */ - jthrowable wrapper; - - /* allocate local message in Java */ - const char fmt[] = "In AWT JNI, %s (at %s:%d)"; - size_t len = strlen (message) + strlen (file) + sizeof fmt + 25; - char *buf; - - if (EXPLAIN_TROUBLE || (isBroken && EXPLAIN_BROKEN)) - { - criticalMsg ("%s:%d: AWT JNI failure%s: %s\n", file, line, - isBroken ? " (BROKEN)" : "", message); - if (cause) - { - jthrowable currentException = (*env)->ExceptionOccurred (env); - - if (cause == currentException) - { - criticalMsg ("Description follows to System.err:"); - (*env)->ExceptionDescribe (env); - /* ExceptionDescribe has the side-effect of clearing the pending - exception; relaunch it. */ - describedException = TRUE; - - if ((*env)->Throw (env, cause)) - { - BADLY_BROKEN1 - ("Relaunching an exception with Throw failed."); - return -1; - } - } - else - { - DELETE_LOCAL_REF (env, currentException); - criticalMsg (WHERE - "currentException != cause; something else happened" - " while handling an exception."); - } - } - } /* if (EXPLAIN_TROUBLE) */ - - if (isBroken && DIE_IF_BROKEN) - fatalMsg ("%s:%d: Aborting execution; BROKEN: %s\n", file, line, message); - - if ((buf = malloc (len))) - { - memset (buf, 0, len); - g_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 int -maybe_rethrow (JNIEnv * env, const char *message, gboolean isBroken, - const char *file, int line) -{ - jthrowable cause = (*env)->ExceptionOccurred (env); - int ret = 0; - - /* rethrow if an exception happened */ - if (cause) - { - ret = rethrow (env, cause, message, isBroken, file, line); - DELETE_LOCAL_REF (env, cause); - } - - return 0; -} - -/* MAYBE_TROUBLE() is used to include a source location in the exception - message. Once we have run maybe_rethrow, if there WAS trouble, - return TRUE, else FALSE. - - MAYBE_TROUBLE() is actually never used; all problems that throw exceptions - are BROKEN, at least. Nothing is recoverable :(. See the discussion of - possible errors at thread_create_jni_impl(). */ -#define MAYBE_TROUBLE(_env, _message) \ - maybe_rethrow(_env, _message, FALSE, __FILE__, __LINE__) - -/* MAYBE_TROUBLE(), but something would be BROKEN if it were true. */ -#define MAYBE_BROKEN(_env, _message) \ - maybe_rethrow(_env, _message, TRUE, __FILE__, __LINE__) - -/* Like MAYBE_TROUBLE(), TROUBLE() is never used. */ -#define TROUBLE(_env, _message) \ - rethrow(_env, (*env)->ExceptionOccurred (env), _message, FALSE, \ - __FILE__, __LINE__) - -#define BROKEN(_env, _message) \ - rethrow (_env, (*env)->ExceptionOccurred (env), _message, TRUE, \ - __FILE__, __LINE__) - -/* Like MAYBE_TROUBLE(), NEW_TROUBLE() is never used. */ -#define NEW_TROUBLE(_env, _message) \ - throw (_env, NULL, _message, FALSE, __FILE__, __LINE__) - -#define NEW_BROKEN(_env, _message) \ - throw (_env, NULL, _message, TRUE, __FILE__, __LINE__) - -/* Like MAYBE_TROUBLE(), RETHROW_CAUSE() is never used. */ -#define RETHROW_CAUSE(_env, _cause, _message) \ - rethrow (_env, _cause, _message, FALSE, __FILE__, __LINE__) - -#define BROKEN_CAUSE(_env, _cause, _message) \ - rethrow (_env, _cause, _message, TRUE, __FILE__, __LINE__) - -/* Macros to handle the possibility that someone might have called one of the - GThreadFunctions API functions with a Java exception pending. It is - generally discouraged to continue to use JNI after a Java exception has - been raised. Sun's JNI book advises that one trap JNI errors immediately - and not continue with an exception pending. - - These are #if'd out for these reasons: - - 1) They do not work in the C '89 subset that Classpath is currently - (2004 May 10) sticking to; HIDE_OLD_TROUBLE() includes a declaration - that should be in scope for the rest of the function, so it needs a - language version that lets you mix declarations and statements. (This - could be worked around if it were important.) - - 2) They chew up more time and resources. - - 3) There does not ever seem to be old trouble -- the assertion in - HIDE_OLD_TROUBLE never goes off. - - You will want to re-enable them if this code needs to be used in a context - where old exceptions might be pending when the GThread functions are - called. - - The implementations in this file are responsible for skipping around calls - to SHOW_OLD_TROUBLE() if they've raised exceptions during the call. So, if - we reach SHOW_OLD_TROUBLE, we are guaranteed that there are no exceptions - pending. */ -#if 1 -#define HIDE_OLD_TROUBLE(env) \ - assert ( NULL == (*env)->ExceptionOccurred (env) ) - -#define SHOW_OLD_TROUBLE() \ - assert ( NULL == (*env)->ExceptionOccurred (env) ) -#else /* 0 */ -#define HIDE_OLD_TROUBLE(env) \ - jthrowable savedTrouble = (*env)->ExceptionOccurred (env); \ - (*env)->ExceptionClear (env); - -#define SHOW_OLD_TROUBLE() do \ -{ \ - assert ( NULL == (*env)->ExceptionOccurred (env) ) \ - if (savedTrouble) \ - { \ - if ((*env)->Throw (env, savedTrouble)) \ - BADLY_BROKEN ("ReThrowing the savedTrouble failed"); \ - } \ - DELETE_LOCAL_REF (env, savedTrouble); \ -} while(0) - -#endif /* 0 */ - -/* Set up the cache of jclass and jmethodID primitives we need - in order to throw new exceptions and rethrow exceptions. We do this - independently of the other caching. We need to have this cache set up - first, so that we can then report errors properly. - - If any errors while setting up the error cache, the world is BADLY_BROKEN. - - May be called more than once. - - Returns -1 if the cache was not initialized properly, 1 if it was. -*/ -static int -setup_exception_cache (JNIEnv * env) -{ - static int exception_cache_initialized = 0; /* -1 for trouble, 1 for proper - init. */ - - jclass lcl_class; /* a class used for local refs */ - - if (exception_cache_initialized) - return exception_cache_initialized; - lcl_class = (*env)->FindClass (env, "java/lang/RuntimeException"); - if ( ! lcl_class ) - { - BADLY_BROKEN1 ("Broken Class library or VM?" - " Couldn't find java/lang/RuntimeException"); - return exception_cache_initialized = -1; - } - /* Pin it down. */ - runtimeException_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); - DELETE_LOCAL_REF (env, lcl_class); - if (!runtimeException_class) - { - BADLY_BROKEN1 ("Serious trouble: could not turn" - " java.lang.RuntimeException into a global reference"); - return exception_cache_initialized = -1; - } - - runtimeException_ctor = - (*env)->GetMethodID (env, runtimeException_class, "<init>", - "(Ljava/lang/String;Ljava/lang/Throwable;)V"); - if ( ! runtimeException_ctor ) - { - BADLY_BROKEN1 ("Serious trouble: classpath couldn't find a" - " two-arg constructor for java/lang/RuntimeException"); - return exception_cache_initialized = -1; - } - - return exception_cache_initialized = 1; -} - - -/**********************************************************/ -/***** The main cache *************************************/ -/**********************************************************/ - -/** This is a cache of all classes, methods, and field IDs that we use during - the run. We maintain a permanent global reference to each of the classes - we cache, since otherwise the (local) jclass that refers to that class - would go out of scope and possibly be reused in further calls. - - The permanent global reference also achieves the secondary goal of - protecting the validity of the methods and field IDs in case the classes - were otherwise unloaded and then later loaded again. Obviously, this will - never happen to classes such as java.lang.Thread and java.lang.Object, but - the primary reason for maintaining permanent global refs is sitll valid. - - The code in jnilink.c has a similar objective. TODO: Consider using that - code instead. - - --Steven Augart -*/ - -/* All of these are cached classes and method IDs: */ -/* java.lang.Object */ -static jclass obj_class; /* java.lang.Object */ -static jmethodID obj_ctor; /* no-arg Constructor for java.lang.Object */ -static jmethodID obj_notify_mth; /* java.lang.Object.notify() */ -static jmethodID obj_notifyall_mth; /* java.lang.Object.notifyall() */ -static jmethodID obj_wait_mth; /* java.lang.Object.wait() */ -static jmethodID obj_wait_nanotime_mth; /* java.lang.Object.wait(JI) */ - -/* GThreadMutex and its methods */ -static jclass mutex_class; -static jmethodID mutex_ctor; -static jfieldID mutex_lockForPotentialLockers_fld; -static jfieldID mutex_potentialLockers_fld; - -/* java.lang.Thread and its methods*/ -static jclass thread_class; /* java.lang.Thread */ -static jmethodID thread_current_mth; /* Thread.currentThread() */ -static jmethodID thread_equals_mth; /* Thread.equals() */ -static jmethodID thread_join_mth; /* Thread.join() */ -static jmethodID thread_setPriority_mth; /* Thread.setPriority() */ -static jmethodID thread_stop_mth; /* Thread.stop() */ -static jmethodID thread_yield_mth; /* Thread.yield() */ - -/* java.lang.ThreadLocal and its methods */ -static jclass threadlocal_class; /* java.lang.ThreadLocal */ -static jmethodID threadlocal_ctor; /* Its constructor */ -static jmethodID threadlocal_set_mth; /* ThreadLocal.set() */ -static jmethodID threadlocal_get_mth; /* ThreadLocal.get() */ - -/* java.lang.Long and its methods */ -static jclass long_class; /* java.lang.Long */ -static jmethodID long_ctor; /* constructor for it: (J) */ -static jmethodID long_longValue_mth; /* longValue()J */ - - -/* GThreadNativeMethodRunner */ -static jclass runner_class; -static jmethodID runner_ctor; -static jmethodID runner_threadToThreadID_mth; -static jmethodID runner_threadIDToThread_mth; -static jmethodID runner_deRegisterJoinable_mth; -static jmethodID runner_start_mth; /* Inherited Thread.start() */ - - -/* java.lang.InterruptedException */ -static jclass interrupted_exception_class; - - - - -/* Returns a negative value if there was trouble during initialization. - Returns a positive value of the cache was initialized correctly. - Never returns zero. */ -static int -setup_cache (JNIEnv * env) -{ - jclass lcl_class; - static int initialized = 0; /* 1 means initialized, 0 means uninitialized, - -1 means mis-initialized */ - - if (initialized) - return initialized; - - /* make sure we can report on trouble */ - if (setup_exception_cache (env) < 0) - return initialized = -1; - -#ifdef JNI_VERSION_1_2 - if (HAVE_JNI_VERSION_1_2) - assert ( ! (*env)->ExceptionCheck (env)); - else -#endif - assert ( ! (*env)->ExceptionOccurred (env)); - - /* java.lang.Object and its methods */ - lcl_class = (*env)->FindClass (env, "java/lang/Object"); - if (!lcl_class) - { - BROKEN (env, "cannot find java.lang.Object"); - return initialized = -1; - } - - /* Pin it down. */ - obj_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); - DELETE_LOCAL_REF (env, lcl_class); - if (!obj_class) - { - BROKEN (env, "Cannot get a global reference to java.lang.Object"); - return initialized = -1; - } - - obj_ctor = (*env)->GetMethodID (env, obj_class, "<init>", "()V"); - if (!obj_ctor) - { - BROKEN (env, "cannot find constructor for java.lang.Object"); - return initialized = -1; - } - - obj_notify_mth = (*env)->GetMethodID (env, obj_class, "notify", "()V"); - if ( ! obj_notify_mth ) - { - BROKEN (env, "cannot find java.lang.Object.notify()V"); - return initialized = -1; - } - - obj_notifyall_mth = - (*env)->GetMethodID (env, obj_class, "notifyAll", "()V"); - if ( ! obj_notifyall_mth) - { - BROKEN (env, "cannot find java.lang.Object.notifyall()V"); - return initialized = -1; - } - - obj_wait_mth = (*env)->GetMethodID (env, obj_class, "wait", "()V"); - if ( ! obj_wait_mth ) - { - BROKEN (env, "cannot find Object.<wait()V>"); - return initialized = -1; - } - - obj_wait_nanotime_mth = - (*env)->GetMethodID (env, obj_class, "wait", "(JI)V"); - if ( ! obj_wait_nanotime_mth ) - { - BROKEN (env, "cannot find Object.<wait(JI)V>"); - return initialized = -1; - } - - /* GThreadMutex and its methods */ - lcl_class = (*env)->FindClass (env, "gnu/java/awt/peer/gtk/GThreadMutex"); - if ( ! lcl_class) - { - BROKEN (env, "cannot find gnu.java.awt.peer.gtk.GThreadMutex"); - return initialized = -1; - } - /* Pin it down. */ - mutex_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); - DELETE_LOCAL_REF (env, lcl_class); - if ( ! mutex_class) - { - BROKEN (env, "Cannot get a global reference to GThreadMutex"); - return initialized = -1; - } - - mutex_ctor = (*env)->GetMethodID (env, mutex_class, "<init>", "()V"); - if ( ! mutex_ctor) - { - BROKEN (env, "cannot find zero-arg constructor for GThreadMutex"); - return initialized = -1; - } - - mutex_potentialLockers_fld = (*env)->GetFieldID - (env, mutex_class, "potentialLockers", "I"); - if ( ! mutex_class ) - { - BROKEN (env, "cannot find GThreadMutex.potentialLockers"); - return initialized = -1; - } - - if (! (mutex_lockForPotentialLockers_fld = (*env)->GetFieldID - (env, mutex_class, "lockForPotentialLockers", "Ljava/lang/Object;"))) - { - BROKEN (env, "cannot find GThreadMutex.lockForPotentialLockers"); - return initialized = -1; - } - - - /* java.lang.Thread */ - if (! (lcl_class = (*env)->FindClass (env, "java/lang/Thread"))) - { - BROKEN (env, "cannot find java.lang.Thread"); - return initialized = -1; - } - - /* Pin it down. */ - thread_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); - DELETE_LOCAL_REF (env, lcl_class); - if (!thread_class) - { - BROKEN (env, "Cannot get a global reference to java.lang.Thread"); - return initialized = -1; - } - - thread_current_mth = - (*env)->GetStaticMethodID (env, thread_class, "currentThread", - "()Ljava/lang/Thread;"); - if (!thread_current_mth) - { - BROKEN (env, "cannot find Thread.currentThread() method"); - return initialized = -1; - } - - thread_equals_mth = - (*env)->GetMethodID (env, thread_class, "equals", "(Ljava/lang/Object;)Z"); - if (!thread_equals_mth) - { - BROKEN (env, "cannot find Thread.equals() method"); - return initialized = -1; - } - - thread_join_mth = (*env)->GetMethodID (env, thread_class, "join", "()V"); - if (!thread_join_mth) - { - BROKEN (env, "cannot find Thread.join() method"); - return initialized = -1; - } - - thread_stop_mth = (*env)->GetMethodID (env, thread_class, "stop", "()V"); - if ( ! thread_stop_mth ) - { - BROKEN (env, "cannot find Thread.stop() method"); - return initialized = -1; - } - - thread_setPriority_mth = - (*env)->GetMethodID (env, thread_class, "setPriority", "(I)V"); - if ( ! thread_setPriority_mth ) - { - BROKEN (env, "cannot find Thread.setPriority() method"); - return initialized = -1; - } - - thread_yield_mth = - (*env)->GetStaticMethodID (env, thread_class, "yield", "()V"); - if ( ! thread_yield_mth ) - { - BROKEN (env, "cannot find Thread.yield() method"); - return initialized = -1; - } - - /* java.lang.ThreadLocal */ - lcl_class = (*env)->FindClass (env, "java/lang/ThreadLocal"); - if ( ! lcl_class ) - { - BROKEN (env, "cannot find class java.lang.ThreadLocal"); - return initialized = -1; - } - - /* Pin it down. */ - threadlocal_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); - DELETE_LOCAL_REF (env, lcl_class); - if ( ! threadlocal_class ) - { - BROKEN (env, "Cannot get a global reference to java.lang.ThreadLocal"); - return initialized = -1; - } - - threadlocal_ctor = (*env)->GetMethodID (env, threadlocal_class, - "<init>", "()V"); - if ( ! threadlocal_ctor ) - { - BROKEN (env, "cannot find ThreadLocal.<init>()V"); - return initialized = -1; - } - - threadlocal_get_mth = (*env)->GetMethodID (env, threadlocal_class, - "get", "()Ljava/lang/Object;"); - if ( ! threadlocal_get_mth ) - { - BROKEN (env, "cannot find java.lang.ThreadLocal.get()Object"); - return initialized = -1; - } - - threadlocal_set_mth = (*env)->GetMethodID (env, threadlocal_class, - "set", "(Ljava/lang/Object;)V"); - if ( ! threadlocal_set_mth ) - { - BROKEN (env, "cannot find ThreadLocal.set(Object)V"); - return initialized = -1; - } - - /* java.lang.Long */ - lcl_class = (*env)->FindClass (env, "java/lang/Long"); - if ( ! lcl_class ) - { - BROKEN (env, "cannot find class java.lang.Long"); - return initialized = -1; - } - - /* Pin it down. */ - long_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); - DELETE_LOCAL_REF (env, lcl_class); - if (!long_class) - { - BROKEN (env, "Cannot get a global reference to java.lang.Long"); - return initialized = -1; - } - - long_ctor = (*env)->GetMethodID (env, long_class, "<init>", "(J)V"); - if (!long_ctor) - { - BROKEN (env, "cannot find method java.lang.Long.<init>(J)V"); - return initialized = -1; - } - - long_longValue_mth = - (*env)->GetMethodID (env, long_class, "longValue", "()J"); - if (!long_longValue_mth) - { - BROKEN (env, "cannot find method java.lang.Long.longValue()J"); - return initialized = -1; - } - - - /* GThreadNativeMethodRunner */ - lcl_class = - (*env)->FindClass (env, - "gnu/java/awt/peer/gtk/GThreadNativeMethodRunner"); - if ( ! lcl_class ) - { - BROKEN (env, - "cannot find gnu.java.awt.peer.gtk.GThreadNativeMethodRunner"); - return initialized = -1; - } - - /* Pin it down. */ - runner_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); - DELETE_LOCAL_REF (env, lcl_class); - if (!runner_class) - { - BROKEN (env, - "Cannot get a global reference to the class GThreadNativeMethodRunner"); - return initialized = -1; - } - - runner_ctor = (*env)->GetMethodID (env, runner_class, "<init>", "(JJZ)V"); - if ( ! runner_ctor ) - { - BROKEN (env, - "cannot find method GThreadNativeMethodRunner.<init>(JJZ)"); - return initialized = -1; - } - - runner_start_mth = (*env)->GetMethodID (env, runner_class, "start", "()V"); - if ( ! runner_start_mth ) - { - BROKEN (env, "cannot find method GThreadNativeMethodRunner.start()V"); - return initialized = -1; - } - - - runner_threadToThreadID_mth = - (*env)->GetStaticMethodID (env, runner_class, - "threadToThreadID", "(Ljava/lang/Thread;)I"); - if ( ! runner_threadToThreadID_mth ) - { - BROKEN (env, - "cannot find method GThreadNativeMethodRunner.threadToThreadID(java.lang.Thread)I"); - return initialized = -1; - } - - - runner_threadIDToThread_mth = - (*env)->GetStaticMethodID (env, runner_class, - "threadIDToThread", "(I)Ljava/lang/Thread;"); - if ( ! runner_threadIDToThread_mth ) - { - BROKEN (env, - "cannot find method GThreadNativeMethodRunner.threadIDToThread(I)java.lang.Thread"); - return initialized = -1; - } - - - runner_deRegisterJoinable_mth = - (*env)->GetStaticMethodID (env, runner_class, "deRegisterJoinable", - "(Ljava/lang/Thread;)V"); - if (!runner_deRegisterJoinable_mth) - { - BROKEN (env, - "cannot find method GThreadNativeMethodRunner.deRegisterJoinable(java.lang.Thread)V"); - return initialized = -1; - } - - - /* java.lang.InterruptedException */ - lcl_class = (*env)->FindClass (env, "java/lang/InterruptedException"); - if ( ! lcl_class ) - { - BROKEN (env, "cannot find class java.lang.InterruptedException"); - return initialized = -1; - } - - /* Pin it down. */ - interrupted_exception_class = (jclass) (*env)->NewGlobalRef (env, lcl_class); - DELETE_LOCAL_REF (env, lcl_class); - if (!interrupted_exception_class) - { - BROKEN (env, "Cannot make a global reference" - " to java.lang.InterruptedException"); - return initialized = -1; - } - -#ifdef JNI_VERSION_1_2 - if (HAVE_JNI_VERSION_1_2) - assert ( ! (*env)->ExceptionCheck (env)); - else -#endif - assert ( ! (*env)->ExceptionOccurred (env)); - - - return initialized = 1; -} - - - - - -/************************************************************************/ -/* Utilities to allocate and free java.lang.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 (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 global_obj; -} - -/* 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 */ - } -} - - -/************************************************************************/ -/* 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; -} - - -/************************************************************************/ -/* Locking code */ -/************************************************************************/ - -/* Lock a Java object */ -#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 */ -#define EXIT_MONITOR(env, m) \ - exitMonitor(env, m, G_STRINGIFY(m)) - -static int -exitMonitor (JNIEnv * env, jobject mutexObj, const char monName[]) -{ - if (TRACE_MONITORS) - tracing (" <MonitorExit(%s)>", monName); - assert (mutexObj); - if ((*env)->MonitorExit (env, mutexObj) < 0) - { - BROKEN (env, "cannot exit monitor "); - return -1; - } - return 0; -} - - -/************************************************************************/ -/* Miscellaneous utilities */ -/************************************************************************/ - -/* Get the Java Thread object that corresponds to a particular thread ID. - A negative thread Id gives us a null object. - - Returns a local reference. -*/ -static jobject -getThreadFromThreadID (JNIEnv * env, gpointer gThreadID) -{ - jint threadNum = (jint) gThreadID; - jobject thread; - - if (threadNum < 0) - { - NEW_BROKEN (env, "getThreadFromThreadID asked to look up" - " a negative thread index"); - return NULL; - } - - thread = (*env)->CallStaticObjectMethod - (env, runner_class, runner_threadIDToThread_mth, threadNum); - - if (MAYBE_BROKEN (env, "cannot get Thread for threadID ")) - return NULL; - - return thread; -} - -/** Return the unique threadID of THREAD. - - Error handling: Return (gpointer) -1 on all failures, - and propagate an exception. -*/ -static gpointer -getThreadIDFromThread (JNIEnv * env, jobject thread) -{ - jint threadNum; - - if (ENABLE_EXPENSIVE_ASSERTIONS) - assert ((*env)->IsInstanceOf (env, thread, thread_class)); - - HIDE_OLD_TROUBLE (env); - - threadNum = (*env)->CallStaticIntMethod - (env, runner_class, runner_threadToThreadID_mth, thread); - - if (MAYBE_BROKEN (env, "cannot get ThreadID for a Thread ")) - { - threadNum = -1; - goto done; - } - - - SHOW_OLD_TROUBLE (); - -done: - return (gpointer) threadNum; -} - - -/************************************************************************/ -/* The Actual JNI functions that we pass to the function vector. */ -/************************************************************************/ - - -/************************************************************************/ -/* Mutex Functions */ -/************************************************************************/ - -/*** Mutex Utilities ****/ -struct mutexObj_cache -{ - jobject lockForPotentialLockersObj; /* Lock for the potentialLockers - field. Local reference. */ - jobject lockObj; /* The real lock we use. This is a GLOBAL - reference and must not be freed. */ -}; - -/* Initialize the cache of sub-locks for a particular mutex object. - - -1 on error, 0 on success. The caller is not responsible for freeing the - partially-populated cache in case of failure (but in practice does anyway) - (This actually never fails, though, since GetObjectField allegedly never - fails.) - - Guaranteed to leave all fields of the cache initialized, even if only to - zero. -*/ -static int -populate_mutexObj_cache (JNIEnv * env, jobject mutexObj, - struct mutexObj_cache *mcache) -{ - mcache->lockObj = mutexObj; /* the mutexObj is its own lock. */ - assert (mcache->lockObj); - - mcache->lockForPotentialLockersObj = (*env)->GetObjectField - (env, mutexObj, mutex_lockForPotentialLockers_fld); - /* GetObjectField can never fail. */ - - /* Retrieving a NULL object could only happen if we somehow got a - a mutex object that was not properly intialized. */ - assert (mcache->lockForPotentialLockersObj); - - return 0; -} - - -/* Clean out the mutexObj_cache, even if it was never populated. */ -static void -clean_mutexObj_cache (JNIEnv * env, struct mutexObj_cache *mcache) -{ - /* OK to pass NULL refs to DELETE_LOCAL_REF */ - DELETE_LOCAL_REF (env, mcache->lockForPotentialLockersObj); - /* mcache->lockObj is a GLOBAL reference. */ - mcache->lockObj = NULL; -} - -/* -1 on failure, 0 on success. - The mutexObj_cache is already populated for this particular object. */ -static int -mutexObj_lock (JNIEnv * env, jobject mutexObj, struct mutexObj_cache *mcache) -{ - jint potentialLockers; - - if (ENTER_MONITOR (env, mcache->lockForPotentialLockersObj)) - return -1; - - assert(mutexObj); - potentialLockers = - (*env)->GetIntField (env, mutexObj, mutex_potentialLockers_fld); - /* GetIntField() never fails. */ - - ++potentialLockers; - - (*env)->SetIntField - (env, mutexObj, mutex_potentialLockers_fld, potentialLockers); - - if (EXIT_MONITOR (env, mcache->lockForPotentialLockersObj)) - return -1; - - if (ENTER_MONITOR (env, mcache->lockObj)) - return -1; - - SHOW_OLD_TROUBLE (); - - return 0; -} - -/* Unlock a GMutex, once we're already in JNI and have already gotten the - mutexObj for it. This skips the messages that TRACE_API_CALLS would - print. - - Returns -1 on error, 0 on success. */ -static int -mutexObj_unlock (JNIEnv * env, jobject mutexObj, - struct mutexObj_cache *mcache) -{ - jint potentialLockers; - int ret = -1; /* assume failure until we suceed. */ - - /* Free the lock first, so that someone waiting for the lock can get it - ASAP. */ - /* This is guaranteed not to block. */ - if (EXIT_MONITOR (env, mcache->lockObj) < 0) - goto done; - - /* Kick down potentialLockers by one. We do this AFTER we free the lock, so - that we hold it no longer than necessary. */ - if (ENTER_MONITOR (env, mcache->lockForPotentialLockersObj) < 0) - goto done; - - potentialLockers = (*env)->GetIntField - (env, mutexObj, mutex_potentialLockers_fld); - /* GetIntField never fails */ - - assert (potentialLockers >= 1); - --potentialLockers; - - (*env)->SetIntField - (env, mutexObj, mutex_potentialLockers_fld, potentialLockers); - /* Never fails, so the JNI book says. */ - - /* Clean up. */ - if (EXIT_MONITOR (env, mcache->lockForPotentialLockersObj) < 0) - goto done; - ret = 0; - -done: - return ret; -} - -/*** 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; - union env_union e; - - if (TRACE_API_CALLS) - tracing ("mutex_new_jni_impl()"); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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 -mutex_lock_jni_impl (GMutex * mutex) -{ - struct mutexObj_cache mcache; - jobject mutexObj = (jobject) mutex; - JNIEnv *env; - union env_union e; - - if (TRACE_API_CALLS) - tracing ("mutex_lock_jni_impl( mutexObj = %p )", mutexObj); - - assert (mutexObj); - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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. Return TRUE if we succeed, FALSE if we fail. - FALSE on error. */ -static gboolean -mutex_trylock_jni_impl (GMutex * gmutex) -{ - jobject mutexObj = (jobject) gmutex; - jint potentialLockers; - gboolean ret = FALSE; - JNIEnv *env; - union env_union e; - struct mutexObj_cache mcache; - - if (TRACE_API_CALLS) - tracing ("mutex_trylock_jni_impl(mutexObj=%p)", mutexObj); - - assert (mutexObj); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); - if (setup_cache (env) < 0) - goto done; - HIDE_OLD_TROUBLE (env); - - if (populate_mutexObj_cache (env, mutexObj, &mcache) < 0) - goto done; - - if (ENTER_MONITOR (env, mcache.lockForPotentialLockersObj)) - goto done; - - potentialLockers = (*env)->GetIntField - (env, mutexObj, mutex_potentialLockers_fld); - - assert (potentialLockers >= 0); - - if (potentialLockers) - { - /* Already locked. Clean up and leave. */ - EXIT_MONITOR (env, mcache.lockForPotentialLockersObj); - /* Ignore any error code from EXIT_MONITOR; there's nothing we could do - at this level, in any case. */ - goto done; - } - - /* Guaranteed not to block. */ - if (ENTER_MONITOR (env, mcache.lockObj)) - { - /* Clean up the existing lock. */ - EXIT_MONITOR (env, mcache.lockForPotentialLockersObj); - /* Ignore any error code from EXIT_MONITOR; there's nothing we could do - at this level, in any case. */ - goto done; - } - - - /* We have the monitor. Record that fact. */ - potentialLockers = 1; - (*env)->SetIntField - (env, mutexObj, mutex_potentialLockers_fld, potentialLockers); - /* Set*Field() never fails */ - - ret = TRUE; /* We have the lock. */ - - /* Clean up. */ - if (EXIT_MONITOR (env, mcache.lockForPotentialLockersObj)) - goto done; /* If we fail at this point, still keep the - main lock. */ - - SHOW_OLD_TROUBLE (); -done: - clean_mutexObj_cache (env, &mcache); - if (TRACE_API_CALLS) - tracing (" ==> %s\n", ret ? "TRUE" : "FALSE"); - return ret; -} - - -/* Unlock a mutex. */ -static void -mutex_unlock_jni_impl (GMutex * gmutex) -{ - jobject mutexObj = (jobject) gmutex; - struct mutexObj_cache mcache; - JNIEnv *env; - union env_union e; - - if (TRACE_API_CALLS) - tracing ("mutex_unlock_jni_impl(mutexObj=%p)", mutexObj); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); - if (setup_cache (env) < 0) - goto done; - HIDE_OLD_TROUBLE (env); - - assert (mutexObj); - - 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?). OK this time for it to be NULL. - No failure conditions, for a change. */ -static void -mutex_free_jni_impl (GMutex * mutex) -{ - jobject mutexObj = (jobject) mutex; - JNIEnv *env; - union env_union e; - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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 * -cond_new_jni_impl (void) -{ - jobject condObj; - JNIEnv *env; - union env_union e; - - if (TRACE_API_CALLS) - tracing ("mutex_free_jni_impl()"); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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 -cond_signal_jni_impl (GCond * gcond) -{ - JNIEnv *env; - union env_union e; - jobject condObj = (jobject) gcond; - - if (TRACE_API_CALLS) - tracing ("cond_signal_jni_impl(condObj = %p)", condObj); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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 */ - if (ENTER_MONITOR (env, condObj)) - goto done; - - (*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); - - 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 -cond_broadcast_jni_impl (GCond * gcond) -{ - jobject condObj = (jobject) gcond; - JNIEnv *env; - union env_union e; - - if (TRACE_API_CALLS) - tracing ("cond_broadcast_jni_impl(condObj=%p)", condObj); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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 */ - if (ENTER_MONITOR (env, condObj)) - goto done; - - (*env)->CallVoidMethod (env, condObj, obj_notifyall_mth); - if (MAYBE_BROKEN (env, "cannot broadcast to mutex with Object.notify()")) - { - EXIT_MONITOR (env, condObj); - goto done; - } - - 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 calling - * Object.wait. - * - * Throws a Java exception on trouble; may leave the mutexes set arbitrarily. - * XXX TODO: Further improve error recovery. - */ -static void -cond_wait_jni_impl (GCond * gcond, GMutex * gmutex) -{ - struct mutexObj_cache cache; - jobject condObj = (jobject) gcond; - jobject mutexObj = (jobject) gmutex; - JNIEnv *env; - union env_union e; - - if (TRACE_API_CALLS) - tracing ("cond_wait_jni_impl(condObj=%p, mutexObj=%p)", - condObj, mutexObj); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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; - } - - /* 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 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. - * - * 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 -cond_timed_wait_jni_impl (GCond * gcond, GMutex * gmutex, GTimeVal * end_time) -{ - JNIEnv *env; - union env_union e; - 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); - } - - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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 */ - if (ENTER_MONITOR (env, condObj) < 0) - goto done; - - if (mutexObj_unlock (env, mutexObj, &cache) < 0) - { - if (EXIT_MONITOR (env, condObj) < 0) - criticalMsg - ("Unable to unlock an existing lock on a condition; your proram may deadlock"); - goto done; - } - - - (*env)->CallVoidMethod (env, condObj, obj_wait_nanotime_mth, - time_millisec, time_nanosec); - - /* If there was trouble, save that fact, and the reason for the trouble. We - want to respond to this condition as fast as possible. */ - cause = (*env)->ExceptionOccurred (env); - - if ( ! cause ) - { - condRaised = TRUE; /* condition was signalled */ - } - else if ((*env)->IsInstanceOf (env, cause, interrupted_exception_class)) - { - condRaised = FALSE; /* Condition was not raised before timeout. - (This is redundant with the initialization - of condRaised above) */ - (*env)->ExceptionClear (env); /* Clear the InterruptedException. */ - cause = NULL; /* no pending cause now. */ - } - else - { - interrupted = FALSE; /* Trouble, but not because of - InterruptedException. Assume the condition - was not raised. */ - /* Leave condRaised set to FALSE */ - } - - /* 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); - } - - 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?). Can not fail. */ -static void -cond_free_jni_impl (GCond * cond) -{ - jobject condObj = (jobject) cond; - JNIEnv *env; - union env_union e; - - if (TRACE_API_CALLS) - tracing ("cond_free_jni_impl(condObj = %p)", condObj); - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); - - freeObject (env, condObj); - - if (TRACE_API_CALLS) - tracing (" ==> VOID\n"); -} - - -/************************************************************************/ -/* Thread-local data code */ -/************************************************************************/ - -/* 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 * -private_new_jni_impl (GDestroyNotify notify __attribute__ ((unused))) -{ - JNIEnv *env; - union env_union e; - jobject lcl_key; - jobject global_key; - GPrivate *gkey = NULL; /* Error return code */ - - if (TRACE_API_CALLS) - tracing ("private_new_jni_impl()"); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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; - } - - 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; - } - - gkey = (GPrivate *) global_key; - SHOW_OLD_TROUBLE (); - -done: - if (TRACE_API_CALLS) - tracing (" ==> %p\n", (void *) gkey); - - return gkey; -} - -/* Get this thread's value for a thread-local key. This is simply - * ThreadLocal.get for us. Return NULL if no value. (I can't think of - * anything else to do.) - */ -static gpointer -private_get_jni_impl (GPrivate * gkey) -{ - JNIEnv *env; - union env_union e; - 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); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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; - } - - val = (*env)->CallLongMethod (env, val_wrapper, long_longValue_mth); - - if (MAYBE_BROKEN (env, "cannot get thread local value")) - goto done; - - thread_specific_data = (gpointer) (intptr_t) val; - - /* Only re-raise the old pending exception if a new one hasn't come along to - supersede it. */ - SHOW_OLD_TROUBLE (); - -done: - - if (TRACE_API_CALLS) - tracing (" ==> %p\n", thread_specific_data); - - return thread_specific_data; -} - -/* Set this thread's value for a thread-local key. This is simply - * ThreadLocal.set() for us. - */ -static void -private_set_jni_impl (GPrivate * gkey, gpointer thread_specific_data) -{ - JNIEnv *env; - union env_union e; - 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); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); - if (setup_cache (env) < 0) - goto done; - HIDE_OLD_TROUBLE (env); - - /* We are just going to always use a Java long to represent a C pointer. - Otherwise all of the code would end up being conditionalized for various - pointer sizes, and that seems like too much of a hassle, in order to save - a paltry few bytes, especially given the horrendous overhead of JNI in - any case. - */ - - val_wrapper = (*env)->NewObject (env, long_class, long_ctor, - (jlong) (intptr_t) thread_specific_data); - if ( ! val_wrapper ) - { - BROKEN (env, "cannot create a java.lang.Long"); - goto done; - } - - /* At this point, we now have set lcl_obj as a numeric class that wraps - around the thread-specific data we were given. */ - (*env)->CallVoidMethod (env, keyObj, threadlocal_set_mth, val_wrapper); - if (MAYBE_BROKEN (env, "cannot set thread local value")) - goto done; - - SHOW_OLD_TROUBLE (); -done: - if (TRACE_API_CALLS) - tracing (" ==> VOID\n"); -} - - -/** Create an object of type gnu.java.awt.peer.gtk.GThreadNativeMethodRunner. - Run it. - - We need to create joinable threads. We handle the notion of a joinable - thread by determining whether or not we are going to maintain a permanent - hard reference to it until it croaks. - - Posix does not appear to have a Java-like concept of daemon threads, where - the JVM will exit when there are only daemon threads running. - - Error handling: - - To quote from the glib guide: - "GError should only be used to report recoverable runtime errors, never - to report programming errors." - - So how do we consider the failure to create a thread? Well, each of the - failure cases in this function are discussed, and none of them are really - recoverable. - - The glib library is really designed so that you should fail - catastrophically in case of "programming errors". The only error defined - for the GThread functions is G_THREAD_ERROR_AGAIN, and that for - thread_create. - - Most of these GThread functions could fail if we run out of memory, for - example, but the only one capable of reporting that fact is - thread_create. */ -static void -thread_create_jni_impl (GThreadFunc func, - gpointer data, - gulong stack_size __attribute__((unused)), - gboolean joinable, - gboolean bound __attribute__((unused)), - GThreadPriority gpriority, - /* This prototype is horrible. threadIDp is actually - a gpointer to the thread's thread-ID. Which is, - of course, itself a gpointer-typed value. Ouch. */ - gpointer threadIDp, - /* Do not touch the GError stuff unless you have - RECOVERABLE trouble. There is no recoverable - trouble in this implementation. */ - GError **errorp __attribute__((unused))) -{ - JNIEnv *env; - union env_union e; - union func_union f; - jboolean jjoinable = joinable; - jobject newThreadObj; - gpointer threadID; /* to be filled in */ - - if (TRACE_API_CALLS) - { - f.g_func = func; - tracing ("thread_create_jni_impl(func=%p, data=%p, joinable=%s," - " threadIDp=%p, *(int *) threadIDp = %d)", - f.void_func, data, joinable ? "TRUE" : "FALSE", - threadIDp, *(int *) threadIDp); - } - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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; - union env_union e; - - if (TRACE_API_CALLS) - tracing ("thread_yield_jni_impl()"); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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; - union env_union e; - jobject threadObj = NULL; - - if ( TRACE_API_CALLS ) - tracing ("thread_join_jni_impl(threadID=%p) ", threadID); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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; - - SHOW_OLD_TROUBLE (); - -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; - union env_union e; - jobject this_thread; - - if (TRACE_API_CALLS) - tracing ("thread_exit_jni_impl() "); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); - if (setup_cache (env) < 0) - goto done; - - HIDE_OLD_TROUBLE (env); - - this_thread = (*env)-> - CallStaticObjectMethod (env, thread_class, thread_current_mth); - - if ( ! this_thread ) - { - BROKEN (env, "cannot get current thread"); - goto done; - } - - (*env)->CallVoidMethod (env, this_thread, thread_stop_mth); - if (MAYBE_BROKEN (env, "cannot call Thread.stop() on current thread")) - goto done; - - SHOW_OLD_TROUBLE (); - -done: - if (TRACE_API_CALLS) - tracing (" ==> VOID \n"); -} - - -/* Translate a GThreadPriority to a Java priority level. */ -static jint -javaPriorityLevel (GThreadPriority priority) -{ - /* We have these fields in java.lang.Thread to play with: - - static int MIN_PRIORITY The minimum priority that a thread can have. - static int NORM_PRIORITY The default priority that is assigned to a - thread. - static int MAX_PRIORITY The maximum priority that a thread can have. - - We get these from the header file generated by javah, even though they're - documented as being 1, 5, and 10. - */ - static const jint minJPri = - gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MIN_PRIORITY; - static const jint normJPri = - gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_NORM_PRIORITY; - static const jint maxJPri = - gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MAX_PRIORITY; - - switch (priority) - { - case G_THREAD_PRIORITY_LOW: - return minJPri; - break; - - default: - assert_not_reached (); - /* Deliberate fall-through if assertions are turned off; also shuts up - GCC warnings if they're turned on. */ - case G_THREAD_PRIORITY_NORMAL: - return normJPri; - break; - - case G_THREAD_PRIORITY_HIGH: - return (normJPri + maxJPri) / 2; - break; - - case G_THREAD_PRIORITY_URGENT: - return maxJPri; - break; - } -} - - -/** It would be safe not to implement this, according to the JNI docs, since - not all platforms do thread priorities. However, we might as well - provide the hint for those who want it. -*/ -static void -thread_set_priority_jni_impl (gpointer gThreadID, GThreadPriority gpriority) -{ - jobject threadObj = NULL; - JNIEnv *env; - union env_union e; - - if (TRACE_API_CALLS) - tracing ("thread_set_priority_jni_impl(gThreadID=%p, gpriority = %u) ", - gThreadID, gpriority); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.void_env, JNI_VERSION_1_1); - - if (setup_cache (env) < 0) - goto done; - - HIDE_OLD_TROUBLE (env); - - - threadObj = getThreadFromThreadID (env, gThreadID); - if ( ! threadObj) /* Reported with BROKEN already. */ - goto done; - - if (threadObj_set_priority (env, threadObj, gpriority)) - goto done; - - SHOW_OLD_TROUBLE (); - -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; - union env_union e; - jobject this_thread; - gpointer my_threadID; - - if (TRACE_API_CALLS) - tracing ("thread_self_jni_impl(my_thread_IDp=%p)", my_thread_IDp); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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; - union env_union e; - - 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); - - e.jni_env = &env; - (*the_vm)->GetEnv (the_vm, e.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 portable_native_sync_jni_functions = { - mutex_new_jni_impl, /* mutex_new */ - mutex_lock_jni_impl, /* mutex_lock */ - mutex_trylock_jni_impl, /* mutex_trylock */ - mutex_unlock_jni_impl, /* mutex_unlock */ - mutex_free_jni_impl, /* mutex_free */ - cond_new_jni_impl, /* cond_new */ - cond_signal_jni_impl, /* cond_signal */ - cond_broadcast_jni_impl, /* cond_broadcast */ - cond_wait_jni_impl, /* cond_wait */ - cond_timed_wait_jni_impl, /* cond_timed_wait */ - cond_free_jni_impl, /* cond_free */ - private_new_jni_impl, /* private_new */ - private_get_jni_impl, /* private_get */ - private_set_jni_impl, /* private_set */ - thread_create_jni_impl, /* thread_create */ - thread_yield_jni_impl, /* thread_yield */ - thread_join_jni_impl, /* thread_join */ - thread_exit_jni_impl, /* thread_exit */ - thread_set_priority_jni_impl, /* thread_set_priority */ - thread_self_jni_impl, /* thread_self */ - thread_equal_jni_impl, /* thread_equal */ -}; - - -/* Keep c-font-lock-extra-types in alphabetical order. */ -/* Local Variables: */ -/* c-file-style: "gnu" */ -/* c-font-lock-extra-types: ("\\sw+_t" "gboolean" "GError" "gpointer" - "GPrivate" "GThreadFunc" "GThreadFunctions" "GThreadPriority" - "gulong" - "JNIEnv" - "jboolean" "jclass" "jfieldID" "jint" "jlong" "jmethodID" "jobject" "jstring" "jthrowable" ) */ -/* End: */ |