mirror of
https://github.com/2003scape/deep-c-rsc.git
synced 2024-03-22 05:49:51 -04:00
2593 lines
76 KiB
C
2593 lines
76 KiB
C
![]() |
/* 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 *cp_gtk_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 */
|
|||
|
|
|||
|
static jclass runtimeException_class; /* java.lang.RuntimeException */
|
|||
|
static 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 = GPOINTER_TO_INT(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 GINT_TO_POINTER(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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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;
|
|||
|
(*cp_gtk_the_vm)->GetEnv (cp_gtk_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 cp_gtk_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: */
|