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: */
|