1 /* gthread-jni.c -- JNI threading routines for GLIB
2 Copyright (C) 1998, 2004 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 /************************************************************************/
39 /* Header */
40 /************************************************************************/
41
42 /*
43 * @author Julian Dolby (dolby@us.ibm.com)
44 * @date February 7, 2003 implemented for GLIB v.1
45 *
46 *
47 * @author Steven Augart
48 * <steve+classpath at augart dot com>, <augart at watson dot ibm dot com>
49 * @date April 30, 2004 -- May 10 2004: Support new functions for Glib v.2,
50 * fix cond_wait to free and re-acquire the mutex,
51 * replaced trylock stub implementation with a full one.
52 *
53 * This code implements the GThreadFunctions interface for GLIB using
54 * Java threading primitives. All of the locking and conditional variable
55 * functionality required by GThreadFunctions is implemented using the
56 * monitor and wait/notify functionality of Java objects. The thread-
57 * local functionality uses the java.lang.ThreadLocal class.
58 *
59 * Classpath's AWT support uses GTK+ peers. GTK+ uses GLIB. GLIB by default
60 * uses the platform's native threading model -- pthreads in most cases. If
61 * the Java runtime doesn't use the native threading model, then it needs this
62 * code in order to use Classpath's (GTK+-based) AWT routines.
63 *
64 * This code should be portable; I believe it makes no assumptions
65 * about the underlying VM beyond that it implements the JNI functionality
66 * that this code uses.
67 *
68 * Currently, use of this code is governed by the configuration option
69 * --enable-portable-native-sync. We will soon add a VM hook so the VM can
70 * select which threading model it wants to use at run time; at that point,
71 * the configuration option will go away.
72 *
73 * The code in this file uses only JNI 1.1, except for one JNI 1.2 function:
74 * GetEnv, in the JNI Invocation API. (There seems to be no way around using
75 * GetEnv).
76 *
77 * ACKNOWLEDGEMENT:
78 *
79 * I would like to thank Mark Wielaard for his kindness in spending at least
80 * six hours of his own time in reviewing this code and correcting my GNU
81 * coding and commenting style. --Steve Augart
82 *
83 *
84 * NOTES:
85 *
86 * This code has been tested with Jikes RVM and with Kaffe.
87 *
88 * This code should have proper automated unit tests. I manually tested it
89 * by running an application that uses AWT. --Steven Augart
90 *
91 * MINOR NIT:
92 *
93 * - Using a jboolean in the arglist to "throw()" and "rethrow()"
94 * triggers many warnings from GCC's -Wconversion operation, because that
95 * is not the same as the conversion (upcast to an int) that would occur in
96 * the absence of a prototype.
97 *
98 * It would be very slightly more efficient to just pass the jboolean, but
99 * is not worth the clutter of messages. The right solution would be to
100 * turn off the -Wconversion warning for just this file, *except* that
101 * -Wconversion also warns you against constructs such as:
102 * unsigned u = -1;
103 * and that is a useful warning. So I went from a "jboolean" to a
104 * "gboolean" (-Wconversion is not enabled by default for GNU Classpath,
105 * but it is in my own CFLAGS, which, for gcc 3.3.3, read: -pipe -ggdb3 -W
106 * -Wall -Wbad-function-cast -Wcast-align -Wpointer-arith -Wcast-qual
107 * -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
108 * -fkeep-static-consts -fkeep-inline-functions -Wundef -Wwrite-strings
109 * -Wno-aggregate-return -Wmissing-noreturn -Wnested-externs -Wtrigraphs
110 * -Wconversion -Wsign-compare -Wno-float-equal -Wmissing-format-attribute
111 * -Wno-unreachable-code -Wdisabled-optimization )
112 */
113
114 #include <config.h>
115
116 /************************************************************************/
117 /* Configuration */
118 /************************************************************************/
119
120 /** Tracing and Reporting **/
121 #define TRACE_API_CALLS 0 /* announce entry and exit into each method,
122 by printing to stderr. */
123
124 #define TRACE_MONITORS 0 /* Every enterMonitor() and exitMonitor() goes
125 to stderr. */
126
127 /** Trouble handling. There is a discussion below of this. **/
128 #define EXPLAIN_TROUBLE 1 /* Describe any unexpected trouble that
129 happens. This is a superset
130 of EXPLAIN_BROKEN, and if set trumps an
131 unset EXPLAIN_BROKEN. It is not a strict
132 superset, since at the moment there is no
133 TROUBLE that is not also BROKEN.
134
135 Use criticalMsg() to describe the problem.
136 */
137
138 #define EXPLAIN_BROKEN 1 /* Describe trouble that is serious enough to
139 be BROKEN. (Right now all trouble is at
140 least BROKEN.) */
141
142 /* There is no EXPLAIN_BADLY_BROKEN definition. We always explain
143 BADLY_BROKEN trouble, since there is no other way to report it. */
144
145
146 /** Error Handling **/
147 #define DIE_IF_BROKEN 1 /* Dies if serious trouble happens. There is
148 really no non-serious trouble, except
149 possibly problems that arise during
150 pthread_create, which are reported by a
151 GError.
152
153 If you do not set DIE_IF_BROKEN, then
154 trouble will raise a Java RuntimeException.
155 We probably do want to die right away,
156 since anything that's BROKEN really
157 indicates a programming error or a
158 system-wide error, and that's what the glib
159 documentation says you should do in case of
160 that kind of error in a glib-style
161 function. But it does work to turn this
162 off. */
163
164 #if DIE_IF_BROKEN
165 #define DIE_IF_BADLY_BROKEN 1 /* DIE_IF_BROKEN implies DIE_IF_BADLY_BROKEN */
166 #else
167 #define DIE_IF_BADLY_BROKEN 1 /* Die if the system is badly broken --
168 that is, if we have further trouble while
169 attempting to throw an exception
170 upwards, or if we are unable to generate
171 one of the classes we'll need in order to
172 throw wrapped exceptions upward.
173
174 If unset, we will print a warning message,
175 and limp along anyway. Not that the system
176 is likely to work. */
177 #endif
178
179 /** Performance tuning parameters **/
180
181 #define ENABLE_EXPENSIVE_ASSERTIONS 0 /* Enable expensive assertions? */
182
183 #define DELETE_LOCAL_REFS 1 /* Whether to delete local references.
184
185 JNI only guarantees that there wil be 16
186 available. (Jikes RVM provides an number
187 only limited by VM memory.)
188
189 Jikes RVM will probably perform faster if
190 this is turned off, but other VMs may need
191 this to be turned on in order to perform at
192 all, or might need it if things change.
193
194 Remember, we don't know how many of those
195 local refs might have already been used up
196 by higher layers of JNI code that end up
197 calling g_thread_self(),
198 g_thread_set_private(), and so on.
199
200 We set this to 1 for GNU Classpath, since
201 one of our principles is "always go for the
202 most robust implementation" */
203
204 #define HAVE_JNI_VERSION_1_2 0 /* Assume we don't. We could
205 dynamically check for this. We will
206 assume JNI 1.2 in later versions of
207 Classpath.
208
209 As it stands, the code in this file
210 already needs one JNI 1.2 function:
211 GetEnv, in the JNI Invocation API.
212
213 TODO This code hasn't been tested yet.
214 And really hasn't been implemented yet.
215 */
216
217 /************************************************************************/
218 /* Global data */
219 /************************************************************************/
220
221 #if defined HAVE_STDINT_H
222 #include <stdint.h> /* provides intptr_t */
223 #elif defined HAVE_INTTYPES_H
224 #include <inttypes.h>
225 #endif
226 #include <stdarg.h> /* va_list */
227 #include <glib.h>
228 #include "gthread-jni.h"
229 #include <assert.h> /* assert() */
230
231 /* For Java thread priority constants. */
232 #include <gnu_java_awt_peer_gtk_GThreadNativeMethodRunner.h>
233
234 /* Since not all JNI header generators actually define constants we
235 define them here explicitly. */
236 #ifndef gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MIN_PRIORITY
237 #define gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MIN_PRIORITY 1
238 #endif
239 #ifndef gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_NORM_PRIORITY
240 #define gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_NORM_PRIORITY 5
241 #endif
242 #ifndef gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MAX_PRIORITY
243 #define gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MAX_PRIORITY 10
244 #endif
245
246 /* The VM handle. This is set in
247 Java_gnu_java_awt_peer_gtk_GtkMainThread_gtkInit */
248 JavaVM *cp_gtk_the_vm;
249
250 /* Unions used for type punning. */
251 union env_union
252 {
253 void **void_env;
254 JNIEnv **jni_env;
255 };
256
257 union func_union
258 {
259 void *void_func;
260 GThreadFunc g_func;
261 };
262
263 /* Forward Declarations for Functions */
264 static int threadObj_set_priority (JNIEnv * env, jobject threadObj,
265 GThreadPriority gpriority);
266 static void fatalMsg (const char fmt[], ...)
267 __attribute__ ((format (printf, 1, 2)))
268 __attribute__ ((noreturn));
269
270 static void criticalMsg (const char fmt[], ...)
271 __attribute__ ((format (printf, 1, 2)));
272
273 static void tracing (const char fmt[], ...)
274 __attribute__ ((format (printf, 1, 2)));
275
276 static jint javaPriorityLevel (GThreadPriority priority)
277 __attribute__ ((const));
278
279 /************************************************************************/
280 /* Trouble-handling, including utilities to reflect exceptions */
281 /* back to the VM. Also some status reporting. */
282 /************************************************************************/
283
284 /* How are we going to handle problems?
285
286 There are several approaches:
287
288 1) Report them with the GError mechanism.
289
290 (*thread_create)() is the only one of these functions that takes a
291 GError pointer. And the only G_THREAD error defined maps onto EAGAIN.
292 We don't have any errors in our (*thread_create)() implementation that
293 can be mapped to EAGAIN. So this idea is a non-starter.
294
295 2) Reflect the exception back to the VM, wrapped in a RuntimeException.
296 This will fail sometimes, if we're so broken (BADLY_BROKEN) that we
297 fail to throw the exception.
298
299 3) Abort execution. This is what the glib functions themselves do for
300 errors that they can't report via GError.
301
302 Enable DIE_IF_BROKEN and/or DIE_IF_BADLY_BROKEN to
303 make this the default for BROKEN and/or BADLY_BROKEN trouble.
304
305 4) Display messages to stderr. We always do this for BADLY_BROKEN
306 trouble. The glib functions do that for errors they can't report via
307 GError.
308
309 There are some complications.
310
311 When I attempted to report a problem in g_thread_self() using g_critical (a
312 macro around g_log(), I found that g_log in turn looks for thread-private
313 data and calls g_thread_self() again.
314
315 We got a segfault, probably due to stack overflow. So, this code doesn't
316 use the g_critical() and g_error() functions any more. Nor do we use
317 g_assert(); we use the C library's assert() instead.
318 */
319
320
321 #define WHERE __FILE__ ":" G_STRINGIFY(__LINE__) ": "
322
323 /* This is portable to older compilers that lack variable-argument macros.
324 This used to be just g_critical(), but then we ran into the error reporting
325 problem discussed above.
326 */
327 static void
fatalMsg(const char fmt[],...)328 fatalMsg (const char fmt[], ...)
329 {
330 va_list ap;
331 va_start (ap, fmt);
332 vfprintf (stderr, fmt, ap);
333 va_end (ap);
334 fputs ("\nAborting execution\n", stderr);
335 abort ();
336 }
337
338
339 static void
criticalMsg(const char fmt[],...)340 criticalMsg (const char fmt[], ...)
341 {
342 va_list ap;
343 va_start (ap, fmt);
344 vfprintf (stderr, fmt, ap);
345 va_end (ap);
346 putc ('\n', stderr);
347 }
348
349 /* Unlike the other two, this one does not append a newline. This is only
350 used if one of the TRACE_ macros is defined. */
351 static void
tracing(const char fmt[],...)352 tracing (const char fmt[], ...)
353 {
354 va_list ap;
355 va_start (ap, fmt);
356 vfprintf (stderr, fmt, ap);
357 va_end (ap);
358 }
359
360 #define assert_not_reached() \
361 do \
362 { \
363 fputs(WHERE "You should never get here. Aborting execution.\n", \
364 stderr); \
365 abort(); \
366 } \
367 while(0)
368
369
370 #if DIE_IF_BADLY_BROKEN
371 #define BADLY_BROKEN fatalMsg
372 #else
373 #define BADLY_BROKEN criticalMsg
374 /* So, the user may still attempt to recover, even though we do not advise
375 this. */
376 #endif
377
378 /* I find it so depressing to have to use C without varargs macros. */
379 #define BADLY_BROKEN_MSG WHERE "Something fundamental" \
380 " to GNU Classpath's AWT JNI broke while we were trying to pass up a Java error message"
381
382 #define BADLY_BROKEN0() \
383 BADLY_BROKEN(BADLY_BROKEN_MSG);
384 #define BADLY_BROKEN1(msg) \
385 BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg)
386 #define BADLY_BROKEN2(msg, arg) \
387 BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg)
388 #define BADLY_BROKEN3(msg, arg, arg2) \
389 BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg1, arg2)
390 #define BADLY_BROKEN4(msg, arg, arg2, arg3) \
391 BADLY_BROKEN(BADLY_BROKEN_MSG ": " msg, arg1, arg2, arg3)
392
393 #define DELETE_LOCAL_REF(env, ref) \
394 do \
395 { \
396 if ( DELETE_LOCAL_REFS ) \
397 { \
398 (*env)->DeleteLocalRef (env, ref); \
399 (ref) = NULL; \
400 } \
401 } \
402 while(0)
403
404 /* Cached info for Exception-wrapping */
405
406 static jclass runtimeException_class; /* java.lang.RuntimeException */
407 static jmethodID runtimeException_ctor; /* constructor for it */
408
409
410 /* Throw a new RuntimeException. It may wrap around an existing exception.
411 1 if we did rethrow, -1 if we had trouble while rethrowing.
412 isBroken is always true in this case. */
413 static int
throw(JNIEnv * env,jthrowable cause,const char * message,gboolean isBroken,const char * file,int line)414 throw (JNIEnv * env, jthrowable cause, const char *message,
415 gboolean isBroken, const char *file, int line)
416 {
417 jstring jmessage;
418 gboolean describedException = FALSE; /* Did we already describe the
419 exception to stderr or the
420 equivalent? */
421 jthrowable wrapper;
422
423 /* allocate local message in Java */
424 const char fmt[] = "In AWT JNI, %s (at %s:%d)";
425 size_t len = strlen (message) + strlen (file) + sizeof fmt + 25;
426 char *buf;
427
428 if (EXPLAIN_TROUBLE || (isBroken && EXPLAIN_BROKEN))
429 {
430 criticalMsg ("%s:%d: AWT JNI failure%s: %s\n", file, line,
431 isBroken ? " (BROKEN)" : "", message);
432 if (cause)
433 {
434 jthrowable currentException = (*env)->ExceptionOccurred (env);
435
436 if (cause == currentException)
437 {
438 criticalMsg ("Description follows to System.err:");
439 (*env)->ExceptionDescribe (env);
440 /* ExceptionDescribe has the side-effect of clearing the pending
441 exception; relaunch it. */
442 describedException = TRUE;
443
444 if ((*env)->Throw (env, cause))
445 {
446 BADLY_BROKEN1
447 ("Relaunching an exception with Throw failed.");
448 return -1;
449 }
450 }
451 else
452 {
453 DELETE_LOCAL_REF (env, currentException);
454 criticalMsg (WHERE
455 "currentException != cause; something else happened"
456 " while handling an exception.");
457 }
458 }
459 } /* if (EXPLAIN_TROUBLE) */
460
461 if (isBroken && DIE_IF_BROKEN)
462 fatalMsg ("%s:%d: Aborting execution; BROKEN: %s\n", file, line, message);
463
464 if ((buf = malloc (len)))
465 {
466 memset (buf, 0, len);
467 g_snprintf (buf, len, fmt, message, file, line);
468 jmessage = (*env)->NewStringUTF (env, buf);
469 free (buf);
470 }
471 else
472 {
473 jmessage = NULL;
474 }
475
476 /* Create the RuntimeException wrapper object and throw it. It is OK for
477 CAUSE to be NULL. */
478 wrapper = (jthrowable) (*env)->NewObject
479 (env, runtimeException_class, runtimeException_ctor, jmessage, cause);
480 DELETE_LOCAL_REF (env, jmessage);
481
482 if (!wrapper)
483 {
484 /* I think this should only happen:
485 - if there are bugs in my JNI code, or
486 - if the VM is broken, or
487 - if we run out of memory.
488 */
489 if (EXPLAIN_TROUBLE)
490 {
491 criticalMsg (WHERE "GNU Classpath: JNI NewObject() could not create"
492 " a new java.lang.RuntimeException.");
493 criticalMsg ("We were trying to warn about the following"
494 " previous failure:");
495 criticalMsg ("%s:%d: %s", file, line, message);
496 criticalMsg ("The latest (NewObject()) exception's description"
497 " follows, to System.err:");
498 (*env)->ExceptionDescribe (env);
499 }
500 BADLY_BROKEN1 ("Failure of JNI NewObject()"
501 " to make a java.lang.RuntimeException");
502 return -1;
503 }
504
505
506 /* throw it */
507 if ((*env)->Throw (env, wrapper))
508 {
509 /* Throw() should just never fail, unless we're in such severe trouble
510 that we might as well die. */
511 BADLY_BROKEN1
512 ("GNU Classpath: Failure of JNI Throw to report an Exception");
513 return -1;
514 }
515
516 DELETE_LOCAL_REF (env, wrapper);
517 return 1;
518 }
519
520
521
522 /* Rethrow an exception we received, wrapping it with a RuntimeException. 1
523 if we did rethrow, -1 if we had trouble while rethrowing.
524 CAUSE should be identical to the most recent exception that happened, so
525 that ExceptionDescribe will work. (Otherwise nix.) */
526 static int
rethrow(JNIEnv * env,jthrowable cause,const char * message,gboolean isBroken,const char * file,int line)527 rethrow (JNIEnv * env, jthrowable cause, const char *message,
528 gboolean isBroken, const char *file, int line)
529 {
530 assert (cause);
531 return throw (env, cause, message, isBroken, file, line);
532 }
533
534
535 /* This function checks for a pending exception, and rethrows it with
536 * a wrapper RuntimeException to deal with possible type problems (in
537 * case some calling piece of code does not expect the exception being
538 * thrown) and to include the given extra message.
539 *
540 * Returns 0 if no problems found (so no exception thrown), 1 if we rethrew an
541 * exception. Returns -1 on failure.
542 */
543 static int
maybe_rethrow(JNIEnv * env,const char * message,gboolean isBroken,const char * file,int line)544 maybe_rethrow (JNIEnv * env, const char *message, gboolean isBroken,
545 const char *file, int line)
546 {
547 jthrowable cause = (*env)->ExceptionOccurred (env);
548 int ret = 0;
549
550 /* rethrow if an exception happened */
551 if (cause)
552 {
553 ret = rethrow (env, cause, message, isBroken, file, line);
554 DELETE_LOCAL_REF (env, cause);
555 }
556
557 return 0;
558 }
559
560 /* MAYBE_TROUBLE() is used to include a source location in the exception
561 message. Once we have run maybe_rethrow, if there WAS trouble,
562 return TRUE, else FALSE.
563
564 MAYBE_TROUBLE() is actually never used; all problems that throw exceptions
565 are BROKEN, at least. Nothing is recoverable :(. See the discussion of
566 possible errors at thread_create_jni_impl(). */
567 #define MAYBE_TROUBLE(_env, _message) \
568 maybe_rethrow(_env, _message, FALSE, __FILE__, __LINE__)
569
570 /* MAYBE_TROUBLE(), but something would be BROKEN if it were true. */
571 #define MAYBE_BROKEN(_env, _message) \
572 maybe_rethrow(_env, _message, TRUE, __FILE__, __LINE__)
573
574 /* Like MAYBE_TROUBLE(), TROUBLE() is never used. */
575 #define TROUBLE(_env, _message) \
576 rethrow(_env, (*env)->ExceptionOccurred (env), _message, FALSE, \
577 __FILE__, __LINE__)
578
579 #define BROKEN(_env, _message) \
580 rethrow (_env, (*env)->ExceptionOccurred (env), _message, TRUE, \
581 __FILE__, __LINE__)
582
583 /* Like MAYBE_TROUBLE(), NEW_TROUBLE() is never used. */
584 #define NEW_TROUBLE(_env, _message) \
585 throw (_env, NULL, _message, FALSE, __FILE__, __LINE__)
586
587 #define NEW_BROKEN(_env, _message) \
588 throw (_env, NULL, _message, TRUE, __FILE__, __LINE__)
589
590 /* Like MAYBE_TROUBLE(), RETHROW_CAUSE() is never used. */
591 #define RETHROW_CAUSE(_env, _cause, _message) \
592 rethrow (_env, _cause, _message, FALSE, __FILE__, __LINE__)
593
594 #define BROKEN_CAUSE(_env, _cause, _message) \
595 rethrow (_env, _cause, _message, TRUE, __FILE__, __LINE__)
596
597 /* Macros to handle the possibility that someone might have called one of the
598 GThreadFunctions API functions with a Java exception pending. It is
599 generally discouraged to continue to use JNI after a Java exception has
600 been raised. Sun's JNI book advises that one trap JNI errors immediately
601 and not continue with an exception pending.
602
603 These are #if'd out for these reasons:
604
605 1) They do not work in the C '89 subset that Classpath is currently
606 (2004 May 10) sticking to; HIDE_OLD_TROUBLE() includes a declaration
607 that should be in scope for the rest of the function, so it needs a
608 language version that lets you mix declarations and statements. (This
609 could be worked around if it were important.)
610
611 2) They chew up more time and resources.
612
613 3) There does not ever seem to be old trouble -- the assertion in
614 HIDE_OLD_TROUBLE never goes off.
615
616 You will want to re-enable them if this code needs to be used in a context
617 where old exceptions might be pending when the GThread functions are
618 called.
619
620 The implementations in this file are responsible for skipping around calls
621 to SHOW_OLD_TROUBLE() if they've raised exceptions during the call. So, if
622 we reach SHOW_OLD_TROUBLE, we are guaranteed that there are no exceptions
623 pending. */
624 #if 1
625 #define HIDE_OLD_TROUBLE(env) \
626 assert ( NULL == (*env)->ExceptionOccurred (env) )
627
628 #define SHOW_OLD_TROUBLE() \
629 assert ( NULL == (*env)->ExceptionOccurred (env) )
630 #else /* 0 */
631 #define HIDE_OLD_TROUBLE(env) \
632 jthrowable savedTrouble = (*env)->ExceptionOccurred (env); \
633 (*env)->ExceptionClear (env);
634
635 #define SHOW_OLD_TROUBLE() do \
636 { \
637 assert ( NULL == (*env)->ExceptionOccurred (env) ) \
638 if (savedTrouble) \
639 { \
640 if ((*env)->Throw (env, savedTrouble)) \
641 BADLY_BROKEN ("ReThrowing the savedTrouble failed"); \
642 } \
643 DELETE_LOCAL_REF (env, savedTrouble); \
644 } while(0)
645
646 #endif /* 0 */
647
648 /* Set up the cache of jclass and jmethodID primitives we need
649 in order to throw new exceptions and rethrow exceptions. We do this
650 independently of the other caching. We need to have this cache set up
651 first, so that we can then report errors properly.
652
653 If any errors while setting up the error cache, the world is BADLY_BROKEN.
654
655 May be called more than once.
656
657 Returns -1 if the cache was not initialized properly, 1 if it was.
658 */
659 static int
setup_exception_cache(JNIEnv * env)660 setup_exception_cache (JNIEnv * env)
661 {
662 static int exception_cache_initialized = 0; /* -1 for trouble, 1 for proper
663 init. */
664
665 jclass lcl_class; /* a class used for local refs */
666
667 if (exception_cache_initialized)
668 return exception_cache_initialized;
669 lcl_class = (*env)->FindClass (env, "java/lang/RuntimeException");
670 if ( ! lcl_class )
671 {
672 BADLY_BROKEN1 ("Broken Class library or VM?"
673 " Couldn't find java/lang/RuntimeException");
674 return exception_cache_initialized = -1;
675 }
676 /* Pin it down. */
677 runtimeException_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
678 DELETE_LOCAL_REF (env, lcl_class);
679 if (!runtimeException_class)
680 {
681 BADLY_BROKEN1 ("Serious trouble: could not turn"
682 " java.lang.RuntimeException into a global reference");
683 return exception_cache_initialized = -1;
684 }
685
686 runtimeException_ctor =
687 (*env)->GetMethodID (env, runtimeException_class, "<init>",
688 "(Ljava/lang/String;Ljava/lang/Throwable;)V");
689 if ( ! runtimeException_ctor )
690 {
691 BADLY_BROKEN1 ("Serious trouble: classpath couldn't find a"
692 " two-arg constructor for java/lang/RuntimeException");
693 return exception_cache_initialized = -1;
694 }
695
696 return exception_cache_initialized = 1;
697 }
698
699
700 /**********************************************************/
701 /***** The main cache *************************************/
702 /**********************************************************/
703
704 /** This is a cache of all classes, methods, and field IDs that we use during
705 the run. We maintain a permanent global reference to each of the classes
706 we cache, since otherwise the (local) jclass that refers to that class
707 would go out of scope and possibly be reused in further calls.
708
709 The permanent global reference also achieves the secondary goal of
710 protecting the validity of the methods and field IDs in case the classes
711 were otherwise unloaded and then later loaded again. Obviously, this will
712 never happen to classes such as java.lang.Thread and java.lang.Object, but
713 the primary reason for maintaining permanent global refs is sitll valid.
714
715 The code in jnilink.c has a similar objective. TODO: Consider using that
716 code instead.
717
718 --Steven Augart
719 */
720
721 /* All of these are cached classes and method IDs: */
722 /* java.lang.Object */
723 static jclass obj_class; /* java.lang.Object */
724 static jmethodID obj_ctor; /* no-arg Constructor for java.lang.Object */
725 static jmethodID obj_notify_mth; /* java.lang.Object.notify() */
726 static jmethodID obj_notifyall_mth; /* java.lang.Object.notifyall() */
727 static jmethodID obj_wait_mth; /* java.lang.Object.wait() */
728 static jmethodID obj_wait_nanotime_mth; /* java.lang.Object.wait(JI) */
729
730 /* GThreadMutex and its methods */
731 static jclass mutex_class;
732 static jmethodID mutex_ctor;
733 static jfieldID mutex_lockForPotentialLockers_fld;
734 static jfieldID mutex_potentialLockers_fld;
735
736 /* java.lang.Thread and its methods*/
737 static jclass thread_class; /* java.lang.Thread */
738 static jmethodID thread_current_mth; /* Thread.currentThread() */
739 static jmethodID thread_equals_mth; /* Thread.equals() */
740 static jmethodID thread_join_mth; /* Thread.join() */
741 static jmethodID thread_setPriority_mth; /* Thread.setPriority() */
742 static jmethodID thread_stop_mth; /* Thread.stop() */
743 static jmethodID thread_yield_mth; /* Thread.yield() */
744
745 /* java.lang.ThreadLocal and its methods */
746 static jclass threadlocal_class; /* java.lang.ThreadLocal */
747 static jmethodID threadlocal_ctor; /* Its constructor */
748 static jmethodID threadlocal_set_mth; /* ThreadLocal.set() */
749 static jmethodID threadlocal_get_mth; /* ThreadLocal.get() */
750
751 /* java.lang.Long and its methods */
752 static jclass long_class; /* java.lang.Long */
753 static jmethodID long_ctor; /* constructor for it: (J) */
754 static jmethodID long_longValue_mth; /* longValue()J */
755
756
757 /* GThreadNativeMethodRunner */
758 static jclass runner_class;
759 static jmethodID runner_ctor;
760 static jmethodID runner_threadToThreadID_mth;
761 static jmethodID runner_threadIDToThread_mth;
762 static jmethodID runner_deRegisterJoinable_mth;
763 static jmethodID runner_start_mth; /* Inherited Thread.start() */
764
765
766 /* java.lang.InterruptedException */
767 static jclass interrupted_exception_class;
768
769
770
771
772 /* Returns a negative value if there was trouble during initialization.
773 Returns a positive value of the cache was initialized correctly.
774 Never returns zero. */
775 static int
setup_cache(JNIEnv * env)776 setup_cache (JNIEnv * env)
777 {
778 jclass lcl_class;
779 static int initialized = 0; /* 1 means initialized, 0 means uninitialized,
780 -1 means mis-initialized */
781
782 if (initialized)
783 return initialized;
784
785 /* make sure we can report on trouble */
786 if (setup_exception_cache (env) < 0)
787 return initialized = -1;
788
789 #ifdef JNI_VERSION_1_2
790 if (HAVE_JNI_VERSION_1_2)
791 assert ( ! (*env)->ExceptionCheck (env));
792 else
793 #endif
794 assert ( ! (*env)->ExceptionOccurred (env));
795
796 /* java.lang.Object and its methods */
797 lcl_class = (*env)->FindClass (env, "java/lang/Object");
798 if (!lcl_class)
799 {
800 BROKEN (env, "cannot find java.lang.Object");
801 return initialized = -1;
802 }
803
804 /* Pin it down. */
805 obj_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
806 DELETE_LOCAL_REF (env, lcl_class);
807 if (!obj_class)
808 {
809 BROKEN (env, "Cannot get a global reference to java.lang.Object");
810 return initialized = -1;
811 }
812
813 obj_ctor = (*env)->GetMethodID (env, obj_class, "<init>", "()V");
814 if (!obj_ctor)
815 {
816 BROKEN (env, "cannot find constructor for java.lang.Object");
817 return initialized = -1;
818 }
819
820 obj_notify_mth = (*env)->GetMethodID (env, obj_class, "notify", "()V");
821 if ( ! obj_notify_mth )
822 {
823 BROKEN (env, "cannot find java.lang.Object.notify()V");
824 return initialized = -1;
825 }
826
827 obj_notifyall_mth =
828 (*env)->GetMethodID (env, obj_class, "notifyAll", "()V");
829 if ( ! obj_notifyall_mth)
830 {
831 BROKEN (env, "cannot find java.lang.Object.notifyall()V");
832 return initialized = -1;
833 }
834
835 obj_wait_mth = (*env)->GetMethodID (env, obj_class, "wait", "()V");
836 if ( ! obj_wait_mth )
837 {
838 BROKEN (env, "cannot find Object.<wait()V>");
839 return initialized = -1;
840 }
841
842 obj_wait_nanotime_mth =
843 (*env)->GetMethodID (env, obj_class, "wait", "(JI)V");
844 if ( ! obj_wait_nanotime_mth )
845 {
846 BROKEN (env, "cannot find Object.<wait(JI)V>");
847 return initialized = -1;
848 }
849
850 /* GThreadMutex and its methods */
851 lcl_class = (*env)->FindClass (env, "gnu/java/awt/peer/gtk/GThreadMutex");
852 if ( ! lcl_class)
853 {
854 BROKEN (env, "cannot find gnu.java.awt.peer.gtk.GThreadMutex");
855 return initialized = -1;
856 }
857 /* Pin it down. */
858 mutex_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
859 DELETE_LOCAL_REF (env, lcl_class);
860 if ( ! mutex_class)
861 {
862 BROKEN (env, "Cannot get a global reference to GThreadMutex");
863 return initialized = -1;
864 }
865
866 mutex_ctor = (*env)->GetMethodID (env, mutex_class, "<init>", "()V");
867 if ( ! mutex_ctor)
868 {
869 BROKEN (env, "cannot find zero-arg constructor for GThreadMutex");
870 return initialized = -1;
871 }
872
873 mutex_potentialLockers_fld = (*env)->GetFieldID
874 (env, mutex_class, "potentialLockers", "I");
875 if ( ! mutex_class )
876 {
877 BROKEN (env, "cannot find GThreadMutex.potentialLockers");
878 return initialized = -1;
879 }
880
881 if (! (mutex_lockForPotentialLockers_fld = (*env)->GetFieldID
882 (env, mutex_class, "lockForPotentialLockers", "Ljava/lang/Object;")))
883 {
884 BROKEN (env, "cannot find GThreadMutex.lockForPotentialLockers");
885 return initialized = -1;
886 }
887
888
889 /* java.lang.Thread */
890 if (! (lcl_class = (*env)->FindClass (env, "java/lang/Thread")))
891 {
892 BROKEN (env, "cannot find java.lang.Thread");
893 return initialized = -1;
894 }
895
896 /* Pin it down. */
897 thread_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
898 DELETE_LOCAL_REF (env, lcl_class);
899 if (!thread_class)
900 {
901 BROKEN (env, "Cannot get a global reference to java.lang.Thread");
902 return initialized = -1;
903 }
904
905 thread_current_mth =
906 (*env)->GetStaticMethodID (env, thread_class, "currentThread",
907 "()Ljava/lang/Thread;");
908 if (!thread_current_mth)
909 {
910 BROKEN (env, "cannot find Thread.currentThread() method");
911 return initialized = -1;
912 }
913
914 thread_equals_mth =
915 (*env)->GetMethodID (env, thread_class, "equals", "(Ljava/lang/Object;)Z");
916 if (!thread_equals_mth)
917 {
918 BROKEN (env, "cannot find Thread.equals() method");
919 return initialized = -1;
920 }
921
922 thread_join_mth = (*env)->GetMethodID (env, thread_class, "join", "()V");
923 if (!thread_join_mth)
924 {
925 BROKEN (env, "cannot find Thread.join() method");
926 return initialized = -1;
927 }
928
929 thread_stop_mth = (*env)->GetMethodID (env, thread_class, "stop", "()V");
930 if ( ! thread_stop_mth )
931 {
932 BROKEN (env, "cannot find Thread.stop() method");
933 return initialized = -1;
934 }
935
936 thread_setPriority_mth =
937 (*env)->GetMethodID (env, thread_class, "setPriority", "(I)V");
938 if ( ! thread_setPriority_mth )
939 {
940 BROKEN (env, "cannot find Thread.setPriority() method");
941 return initialized = -1;
942 }
943
944 thread_yield_mth =
945 (*env)->GetStaticMethodID (env, thread_class, "yield", "()V");
946 if ( ! thread_yield_mth )
947 {
948 BROKEN (env, "cannot find Thread.yield() method");
949 return initialized = -1;
950 }
951
952 /* java.lang.ThreadLocal */
953 lcl_class = (*env)->FindClass (env, "java/lang/ThreadLocal");
954 if ( ! lcl_class )
955 {
956 BROKEN (env, "cannot find class java.lang.ThreadLocal");
957 return initialized = -1;
958 }
959
960 /* Pin it down. */
961 threadlocal_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
962 DELETE_LOCAL_REF (env, lcl_class);
963 if ( ! threadlocal_class )
964 {
965 BROKEN (env, "Cannot get a global reference to java.lang.ThreadLocal");
966 return initialized = -1;
967 }
968
969 threadlocal_ctor = (*env)->GetMethodID (env, threadlocal_class,
970 "<init>", "()V");
971 if ( ! threadlocal_ctor )
972 {
973 BROKEN (env, "cannot find ThreadLocal.<init>()V");
974 return initialized = -1;
975 }
976
977 threadlocal_get_mth = (*env)->GetMethodID (env, threadlocal_class,
978 "get", "()Ljava/lang/Object;");
979 if ( ! threadlocal_get_mth )
980 {
981 BROKEN (env, "cannot find java.lang.ThreadLocal.get()Object");
982 return initialized = -1;
983 }
984
985 threadlocal_set_mth = (*env)->GetMethodID (env, threadlocal_class,
986 "set", "(Ljava/lang/Object;)V");
987 if ( ! threadlocal_set_mth )
988 {
989 BROKEN (env, "cannot find ThreadLocal.set(Object)V");
990 return initialized = -1;
991 }
992
993 /* java.lang.Long */
994 lcl_class = (*env)->FindClass (env, "java/lang/Long");
995 if ( ! lcl_class )
996 {
997 BROKEN (env, "cannot find class java.lang.Long");
998 return initialized = -1;
999 }
1000
1001 /* Pin it down. */
1002 long_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
1003 DELETE_LOCAL_REF (env, lcl_class);
1004 if (!long_class)
1005 {
1006 BROKEN (env, "Cannot get a global reference to java.lang.Long");
1007 return initialized = -1;
1008 }
1009
1010 long_ctor = (*env)->GetMethodID (env, long_class, "<init>", "(J)V");
1011 if (!long_ctor)
1012 {
1013 BROKEN (env, "cannot find method java.lang.Long.<init>(J)V");
1014 return initialized = -1;
1015 }
1016
1017 long_longValue_mth =
1018 (*env)->GetMethodID (env, long_class, "longValue", "()J");
1019 if (!long_longValue_mth)
1020 {
1021 BROKEN (env, "cannot find method java.lang.Long.longValue()J");
1022 return initialized = -1;
1023 }
1024
1025
1026 /* GThreadNativeMethodRunner */
1027 lcl_class =
1028 (*env)->FindClass (env,
1029 "gnu/java/awt/peer/gtk/GThreadNativeMethodRunner");
1030 if ( ! lcl_class )
1031 {
1032 BROKEN (env,
1033 "cannot find gnu.java.awt.peer.gtk.GThreadNativeMethodRunner");
1034 return initialized = -1;
1035 }
1036
1037 /* Pin it down. */
1038 runner_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
1039 DELETE_LOCAL_REF (env, lcl_class);
1040 if (!runner_class)
1041 {
1042 BROKEN (env,
1043 "Cannot get a global reference to the class GThreadNativeMethodRunner");
1044 return initialized = -1;
1045 }
1046
1047 runner_ctor = (*env)->GetMethodID (env, runner_class, "<init>", "(JJZ)V");
1048 if ( ! runner_ctor )
1049 {
1050 BROKEN (env,
1051 "cannot find method GThreadNativeMethodRunner.<init>(JJZ)");
1052 return initialized = -1;
1053 }
1054
1055 runner_start_mth = (*env)->GetMethodID (env, runner_class, "start", "()V");
1056 if ( ! runner_start_mth )
1057 {
1058 BROKEN (env, "cannot find method GThreadNativeMethodRunner.start()V");
1059 return initialized = -1;
1060 }
1061
1062
1063 runner_threadToThreadID_mth =
1064 (*env)->GetStaticMethodID (env, runner_class,
1065 "threadToThreadID", "(Ljava/lang/Thread;)I");
1066 if ( ! runner_threadToThreadID_mth )
1067 {
1068 BROKEN (env,
1069 "cannot find method GThreadNativeMethodRunner.threadToThreadID(java.lang.Thread)I");
1070 return initialized = -1;
1071 }
1072
1073
1074 runner_threadIDToThread_mth =
1075 (*env)->GetStaticMethodID (env, runner_class,
1076 "threadIDToThread", "(I)Ljava/lang/Thread;");
1077 if ( ! runner_threadIDToThread_mth )
1078 {
1079 BROKEN (env,
1080 "cannot find method GThreadNativeMethodRunner.threadIDToThread(I)java.lang.Thread");
1081 return initialized = -1;
1082 }
1083
1084
1085 runner_deRegisterJoinable_mth =
1086 (*env)->GetStaticMethodID (env, runner_class, "deRegisterJoinable",
1087 "(Ljava/lang/Thread;)V");
1088 if (!runner_deRegisterJoinable_mth)
1089 {
1090 BROKEN (env,
1091 "cannot find method GThreadNativeMethodRunner.deRegisterJoinable(java.lang.Thread)V");
1092 return initialized = -1;
1093 }
1094
1095
1096 /* java.lang.InterruptedException */
1097 lcl_class = (*env)->FindClass (env, "java/lang/InterruptedException");
1098 if ( ! lcl_class )
1099 {
1100 BROKEN (env, "cannot find class java.lang.InterruptedException");
1101 return initialized = -1;
1102 }
1103
1104 /* Pin it down. */
1105 interrupted_exception_class = (jclass) (*env)->NewGlobalRef (env, lcl_class);
1106 DELETE_LOCAL_REF (env, lcl_class);
1107 if (!interrupted_exception_class)
1108 {
1109 BROKEN (env, "Cannot make a global reference"
1110 " to java.lang.InterruptedException");
1111 return initialized = -1;
1112 }
1113
1114 #ifdef JNI_VERSION_1_2
1115 if (HAVE_JNI_VERSION_1_2)
1116 assert ( ! (*env)->ExceptionCheck (env));
1117 else
1118 #endif
1119 assert ( ! (*env)->ExceptionOccurred (env));
1120
1121
1122 return initialized = 1;
1123 }
1124
1125
1126
1127
1128
1129 /************************************************************************/
1130 /* Utilities to allocate and free java.lang.Objects */
1131 /************************************************************************/
1132
1133 /* The condition variables are java.lang.Object objects,
1134 * which this method allocates and returns a global ref. Note that global
1135 * refs must be explicitly freed (isn't C fun?).
1136 */
1137 static jobject
allocatePlainObject(JNIEnv * env)1138 allocatePlainObject (JNIEnv * env)
1139 {
1140 jobject lcl_obj, global_obj;
1141
1142 lcl_obj = (*env)->NewObject (env, obj_class, obj_ctor);
1143 if (!lcl_obj)
1144 {
1145 BROKEN (env, "cannot allocate object");
1146 return NULL;
1147 }
1148
1149 global_obj = (*env)->NewGlobalRef (env, lcl_obj);
1150 DELETE_LOCAL_REF (env, lcl_obj);
1151 if (!global_obj)
1152 {
1153 NEW_BROKEN (env, "cannot make global ref for a new plain Java object");
1154 /* Deliberate fall-through */
1155 }
1156
1157 return global_obj;
1158 }
1159
1160 /* Frees any Java object given a global ref (isn't C fun?) */
1161 static void
freeObject(JNIEnv * env,jobject obj)1162 freeObject (JNIEnv * env, jobject obj)
1163 {
1164 if (obj)
1165 {
1166 (*env)->DeleteGlobalRef (env, obj);
1167 /* DeleteGlobalRef can never fail */
1168 }
1169 }
1170
1171
1172 /************************************************************************/
1173 /* Utilities to allocate and free Java mutexes */
1174 /************************************************************************/
1175
1176 /* The mutexes are gnu.java.awt.peer.gtk.GThreadMutex objects,
1177 * which this method allocates and returns a global ref. Note that global
1178 * refs must be explicitly freed (isn't C fun?).
1179 *
1180 * Free this with freeObject()
1181 */
1182 static jobject
allocateMutexObject(JNIEnv * env)1183 allocateMutexObject (JNIEnv * env)
1184 {
1185 jobject lcl_obj, global_obj;
1186
1187 lcl_obj = (*env)->NewObject (env, mutex_class, mutex_ctor);
1188 if (!lcl_obj)
1189 {
1190 BROKEN (env, "cannot allocate a GThreadMutex");
1191 return NULL;
1192 }
1193
1194 global_obj = (*env)->NewGlobalRef (env, lcl_obj);
1195 DELETE_LOCAL_REF (env, lcl_obj);
1196 if (!global_obj)
1197 {
1198 NEW_BROKEN (env, "cannot make global ref");
1199 /* Deliberate fallthrough */
1200 }
1201
1202 return global_obj;
1203 }
1204
1205
1206 /************************************************************************/
1207 /* Locking code */
1208 /************************************************************************/
1209
1210 /* Lock a Java object */
1211 #define ENTER_MONITOR(env, m) \
1212 enterMonitor(env, m, G_STRINGIFY(m))
1213
1214 /* Return -1 on failure, 0 on success. */
1215 static int
enterMonitor(JNIEnv * env,jobject monitorObj,const char monName[])1216 enterMonitor (JNIEnv * env, jobject monitorObj, const char monName[])
1217 {
1218 if (TRACE_MONITORS)
1219 tracing (" <MonitorEnter(%s)>", monName);
1220 assert (monitorObj);
1221 if ((*env)->MonitorEnter (env, monitorObj) < 0)
1222 {
1223 BROKEN (env, "cannot enter monitor");
1224 return -1;
1225 }
1226 return 0;
1227 }
1228
1229
1230 /* Unlock a Java object */
1231 #define EXIT_MONITOR(env, m) \
1232 exitMonitor(env, m, G_STRINGIFY(m))
1233
1234 static int
exitMonitor(JNIEnv * env,jobject mutexObj,const char monName[])1235 exitMonitor (JNIEnv * env, jobject mutexObj, const char monName[])
1236 {
1237 if (TRACE_MONITORS)
1238 tracing (" <MonitorExit(%s)>", monName);
1239 assert (mutexObj);
1240 if ((*env)->MonitorExit (env, mutexObj) < 0)
1241 {
1242 BROKEN (env, "cannot exit monitor ");
1243 return -1;
1244 }
1245 return 0;
1246 }
1247
1248
1249 /************************************************************************/
1250 /* Miscellaneous utilities */
1251 /************************************************************************/
1252
1253 /* Get the Java Thread object that corresponds to a particular thread ID.
1254 A negative thread Id gives us a null object.
1255
1256 Returns a local reference.
1257 */
1258 static jobject
getThreadFromThreadID(JNIEnv * env,gpointer gThreadID)1259 getThreadFromThreadID (JNIEnv * env, gpointer gThreadID)
1260 {
1261 jint threadNum = (jint) gThreadID;
1262 jobject thread;
1263
1264 if (threadNum < 0)
1265 {
1266 NEW_BROKEN (env, "getThreadFromThreadID asked to look up"
1267 " a negative thread index");
1268 return NULL;
1269 }
1270
1271 thread = (*env)->CallStaticObjectMethod
1272 (env, runner_class, runner_threadIDToThread_mth, threadNum);
1273
1274 if (MAYBE_BROKEN (env, "cannot get Thread for threadID "))
1275 return NULL;
1276
1277 return thread;
1278 }
1279
1280 /** Return the unique threadID of THREAD.
1281
1282 Error handling: Return (gpointer) -1 on all failures,
1283 and propagate an exception.
1284 */
1285 static gpointer
getThreadIDFromThread(JNIEnv * env,jobject thread)1286 getThreadIDFromThread (JNIEnv * env, jobject thread)
1287 {
1288 jint threadNum;
1289
1290 if (ENABLE_EXPENSIVE_ASSERTIONS)
1291 assert ((*env)->IsInstanceOf (env, thread, thread_class));
1292
1293 HIDE_OLD_TROUBLE (env);
1294
1295 threadNum = (*env)->CallStaticIntMethod
1296 (env, runner_class, runner_threadToThreadID_mth, thread);
1297
1298 if (MAYBE_BROKEN (env, "cannot get ThreadID for a Thread "))
1299 {
1300 threadNum = -1;
1301 goto done;
1302 }
1303
1304
1305 SHOW_OLD_TROUBLE ();
1306
1307 done:
1308 return (gpointer) threadNum;
1309 }
1310
1311
1312 /************************************************************************/
1313 /* The Actual JNI functions that we pass to the function vector. */
1314 /************************************************************************/
1315
1316
1317 /************************************************************************/
1318 /* Mutex Functions */
1319 /************************************************************************/
1320
1321 /*** Mutex Utilities ****/
1322 struct mutexObj_cache
1323 {
1324 jobject lockForPotentialLockersObj; /* Lock for the potentialLockers
1325 field. Local reference. */
1326 jobject lockObj; /* The real lock we use. This is a GLOBAL
1327 reference and must not be freed. */
1328 };
1329
1330 /* Initialize the cache of sub-locks for a particular mutex object.
1331
1332 -1 on error, 0 on success. The caller is not responsible for freeing the
1333 partially-populated cache in case of failure (but in practice does anyway)
1334 (This actually never fails, though, since GetObjectField allegedly never
1335 fails.)
1336
1337 Guaranteed to leave all fields of the cache initialized, even if only to
1338 zero.
1339 */
1340 static int
populate_mutexObj_cache(JNIEnv * env,jobject mutexObj,struct mutexObj_cache * mcache)1341 populate_mutexObj_cache (JNIEnv * env, jobject mutexObj,
1342 struct mutexObj_cache *mcache)
1343 {
1344 mcache->lockObj = mutexObj; /* the mutexObj is its own lock. */
1345 assert (mcache->lockObj);
1346
1347 mcache->lockForPotentialLockersObj = (*env)->GetObjectField
1348 (env, mutexObj, mutex_lockForPotentialLockers_fld);
1349 /* GetObjectField can never fail. */
1350
1351 /* Retrieving a NULL object could only happen if we somehow got a
1352 a mutex object that was not properly intialized. */
1353 assert (mcache->lockForPotentialLockersObj);
1354
1355 return 0;
1356 }
1357
1358
1359 /* Clean out the mutexObj_cache, even if it was never populated. */
1360 static void
clean_mutexObj_cache(JNIEnv * env,struct mutexObj_cache * mcache)1361 clean_mutexObj_cache (JNIEnv * env, struct mutexObj_cache *mcache)
1362 {
1363 /* OK to pass NULL refs to DELETE_LOCAL_REF */
1364 DELETE_LOCAL_REF (env, mcache->lockForPotentialLockersObj);
1365 /* mcache->lockObj is a GLOBAL reference. */
1366 mcache->lockObj = NULL;
1367 }
1368
1369 /* -1 on failure, 0 on success.
1370 The mutexObj_cache is already populated for this particular object. */
1371 static int
mutexObj_lock(JNIEnv * env,jobject mutexObj,struct mutexObj_cache * mcache)1372 mutexObj_lock (JNIEnv * env, jobject mutexObj, struct mutexObj_cache *mcache)
1373 {
1374 jint potentialLockers;
1375
1376 if (ENTER_MONITOR (env, mcache->lockForPotentialLockersObj))
1377 return -1;
1378
1379 assert(mutexObj);
1380 potentialLockers =
1381 (*env)->GetIntField (env, mutexObj, mutex_potentialLockers_fld);
1382 /* GetIntField() never fails. */
1383
1384 ++potentialLockers;
1385
1386 (*env)->SetIntField
1387 (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
1388
1389 if (EXIT_MONITOR (env, mcache->lockForPotentialLockersObj))
1390 return -1;
1391
1392 if (ENTER_MONITOR (env, mcache->lockObj))
1393 return -1;
1394
1395 SHOW_OLD_TROUBLE ();
1396
1397 return 0;
1398 }
1399
1400 /* Unlock a GMutex, once we're already in JNI and have already gotten the
1401 mutexObj for it. This skips the messages that TRACE_API_CALLS would
1402 print.
1403
1404 Returns -1 on error, 0 on success. */
1405 static int
mutexObj_unlock(JNIEnv * env,jobject mutexObj,struct mutexObj_cache * mcache)1406 mutexObj_unlock (JNIEnv * env, jobject mutexObj,
1407 struct mutexObj_cache *mcache)
1408 {
1409 jint potentialLockers;
1410 int ret = -1; /* assume failure until we suceed. */
1411
1412 /* Free the lock first, so that someone waiting for the lock can get it
1413 ASAP. */
1414 /* This is guaranteed not to block. */
1415 if (EXIT_MONITOR (env, mcache->lockObj) < 0)
1416 goto done;
1417
1418 /* Kick down potentialLockers by one. We do this AFTER we free the lock, so
1419 that we hold it no longer than necessary. */
1420 if (ENTER_MONITOR (env, mcache->lockForPotentialLockersObj) < 0)
1421 goto done;
1422
1423 potentialLockers = (*env)->GetIntField
1424 (env, mutexObj, mutex_potentialLockers_fld);
1425 /* GetIntField never fails */
1426
1427 assert (potentialLockers >= 1);
1428 --potentialLockers;
1429
1430 (*env)->SetIntField
1431 (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
1432 /* Never fails, so the JNI book says. */
1433
1434 /* Clean up. */
1435 if (EXIT_MONITOR (env, mcache->lockForPotentialLockersObj) < 0)
1436 goto done;
1437 ret = 0;
1438
1439 done:
1440 return ret;
1441 }
1442
1443 /*** Mutex Implementations ****/
1444
1445 /* Create a mutex, which is a java.lang.Object for us.
1446 In case of failure, we'll return NULL. Which will implicitly
1447 cause future calls to fail. */
1448 static GMutex *
mutex_new_jni_impl(void)1449 mutex_new_jni_impl (void)
1450 {
1451 jobject mutexObj;
1452 JNIEnv *env;
1453 union env_union e;
1454
1455 if (TRACE_API_CALLS)
1456 tracing ("mutex_new_jni_impl()");
1457
1458 e.jni_env = &env;
1459 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1460
1461 if (setup_cache (env) < 0)
1462 {
1463 mutexObj = NULL;
1464 goto done;
1465 }
1466
1467 mutexObj = allocateMutexObject (env);
1468
1469 done:
1470 if (TRACE_API_CALLS)
1471 tracing (" ==> %p \n", mutexObj);
1472
1473 return (GMutex *) mutexObj;
1474
1475 }
1476
1477 /* Lock a mutex. */
1478 static void
mutex_lock_jni_impl(GMutex * mutex)1479 mutex_lock_jni_impl (GMutex * mutex)
1480 {
1481 struct mutexObj_cache mcache;
1482 jobject mutexObj = (jobject) mutex;
1483 JNIEnv *env;
1484 union env_union e;
1485
1486 if (TRACE_API_CALLS)
1487 tracing ("mutex_lock_jni_impl( mutexObj = %p )", mutexObj);
1488
1489 assert (mutexObj);
1490 e.jni_env = &env;
1491 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1492
1493 if (setup_cache (env) < 0)
1494 goto done;
1495
1496 HIDE_OLD_TROUBLE (env);
1497
1498 if (populate_mutexObj_cache (env, mutexObj, &mcache) < 0)
1499 goto done;
1500
1501 mutexObj_lock (env, mutexObj, &mcache);
1502 /* No need to error check; we've already reported it in any case. */
1503
1504 done:
1505 clean_mutexObj_cache (env, &mcache);
1506 if (TRACE_API_CALLS)
1507 tracing (" ==> VOID \n");
1508 }
1509
1510
1511 /* Try to lock a mutex. Return TRUE if we succeed, FALSE if we fail.
1512 FALSE on error. */
1513 static gboolean
mutex_trylock_jni_impl(GMutex * gmutex)1514 mutex_trylock_jni_impl (GMutex * gmutex)
1515 {
1516 jobject mutexObj = (jobject) gmutex;
1517 jint potentialLockers;
1518 gboolean ret = FALSE;
1519 JNIEnv *env;
1520 union env_union e;
1521 struct mutexObj_cache mcache;
1522
1523 if (TRACE_API_CALLS)
1524 tracing ("mutex_trylock_jni_impl(mutexObj=%p)", mutexObj);
1525
1526 assert (mutexObj);
1527
1528 e.jni_env = &env;
1529 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1530 if (setup_cache (env) < 0)
1531 goto done;
1532 HIDE_OLD_TROUBLE (env);
1533
1534 if (populate_mutexObj_cache (env, mutexObj, &mcache) < 0)
1535 goto done;
1536
1537 if (ENTER_MONITOR (env, mcache.lockForPotentialLockersObj))
1538 goto done;
1539
1540 potentialLockers = (*env)->GetIntField
1541 (env, mutexObj, mutex_potentialLockers_fld);
1542
1543 assert (potentialLockers >= 0);
1544
1545 if (potentialLockers)
1546 {
1547 /* Already locked. Clean up and leave. */
1548 EXIT_MONITOR (env, mcache.lockForPotentialLockersObj);
1549 /* Ignore any error code from EXIT_MONITOR; there's nothing we could do
1550 at this level, in any case. */
1551 goto done;
1552 }
1553
1554 /* Guaranteed not to block. */
1555 if (ENTER_MONITOR (env, mcache.lockObj))
1556 {
1557 /* Clean up the existing lock. */
1558 EXIT_MONITOR (env, mcache.lockForPotentialLockersObj);
1559 /* Ignore any error code from EXIT_MONITOR; there's nothing we could do
1560 at this level, in any case. */
1561 goto done;
1562 }
1563
1564
1565 /* We have the monitor. Record that fact. */
1566 potentialLockers = 1;
1567 (*env)->SetIntField
1568 (env, mutexObj, mutex_potentialLockers_fld, potentialLockers);
1569 /* Set*Field() never fails */
1570
1571 ret = TRUE; /* We have the lock. */
1572
1573 /* Clean up. */
1574 if (EXIT_MONITOR (env, mcache.lockForPotentialLockersObj))
1575 goto done; /* If we fail at this point, still keep the
1576 main lock. */
1577
1578 SHOW_OLD_TROUBLE ();
1579 done:
1580 clean_mutexObj_cache (env, &mcache);
1581 if (TRACE_API_CALLS)
1582 tracing (" ==> %s\n", ret ? "TRUE" : "FALSE");
1583 return ret;
1584 }
1585
1586
1587 /* Unlock a mutex. */
1588 static void
mutex_unlock_jni_impl(GMutex * gmutex)1589 mutex_unlock_jni_impl (GMutex * gmutex)
1590 {
1591 jobject mutexObj = (jobject) gmutex;
1592 struct mutexObj_cache mcache;
1593 JNIEnv *env;
1594 union env_union e;
1595
1596 if (TRACE_API_CALLS)
1597 tracing ("mutex_unlock_jni_impl(mutexObj=%p)", mutexObj);
1598
1599 e.jni_env = &env;
1600 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1601 if (setup_cache (env) < 0)
1602 goto done;
1603 HIDE_OLD_TROUBLE (env);
1604
1605 assert (mutexObj);
1606
1607 if ( populate_mutexObj_cache (env, mutexObj, &mcache) < 0)
1608 goto done;
1609
1610 (void) mutexObj_unlock (env, mutexObj, &mcache);
1611
1612 SHOW_OLD_TROUBLE ();
1613
1614 done:
1615 clean_mutexObj_cache (env, &mcache);
1616 if (TRACE_API_CALLS)
1617 tracing (" ==> VOID\n");
1618 }
1619
1620
1621
1622 /* Free a mutex (isn't C fun?). OK this time for it to be NULL.
1623 No failure conditions, for a change. */
1624 static void
mutex_free_jni_impl(GMutex * mutex)1625 mutex_free_jni_impl (GMutex * mutex)
1626 {
1627 jobject mutexObj = (jobject) mutex;
1628 JNIEnv *env;
1629 union env_union e;
1630
1631 e.jni_env = &env;
1632 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1633
1634 if (TRACE_API_CALLS)
1635 tracing ("mutex_free_jni_impl(%p)", mutexObj);
1636
1637 freeObject (env, mutexObj);
1638
1639 if (TRACE_API_CALLS)
1640 tracing (" ==> VOID\n");
1641 }
1642
1643
1644
1645
1646 /************************************************************************/
1647 /* Condition variable code */
1648 /************************************************************************/
1649
1650 /* Create a new condition variable. This is a java.lang.Object for us. */
1651 static GCond *
cond_new_jni_impl(void)1652 cond_new_jni_impl (void)
1653 {
1654 jobject condObj;
1655 JNIEnv *env;
1656 union env_union e;
1657
1658 if (TRACE_API_CALLS)
1659 tracing ("mutex_free_jni_impl()");
1660
1661 e.jni_env = &env;
1662 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1663
1664 condObj = allocatePlainObject (env);
1665
1666 if (TRACE_API_CALLS)
1667 tracing (" ==> %p\n", condObj);
1668
1669 return (GCond *) condObj;
1670 }
1671
1672 /* Signal on a condition variable. This is simply calling Object.notify
1673 * for us.
1674 */
1675 static void
cond_signal_jni_impl(GCond * gcond)1676 cond_signal_jni_impl (GCond * gcond)
1677 {
1678 JNIEnv *env;
1679 union env_union e;
1680 jobject condObj = (jobject) gcond;
1681
1682 if (TRACE_API_CALLS)
1683 tracing ("cond_signal_jni_impl(condObj = %p)", condObj);
1684
1685 e.jni_env = &env;
1686 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1687 if (setup_cache (env) < 0)
1688 goto done;
1689 HIDE_OLD_TROUBLE (env);
1690
1691 assert (condObj);
1692
1693 /* Must have locked an object to call notify */
1694 if (ENTER_MONITOR (env, condObj))
1695 goto done;
1696
1697 (*env)->CallVoidMethod (env, condObj, obj_notify_mth);
1698 if (MAYBE_BROKEN (env, "cannot signal mutex with Object.notify()"))
1699 {
1700 if (EXIT_MONITOR (env, condObj))
1701 BADLY_BROKEN1 ("Failed to unlock a monitor; the VM may deadlock.");
1702 goto done;
1703 }
1704
1705 EXIT_MONITOR (env, condObj);
1706
1707 SHOW_OLD_TROUBLE ();
1708
1709 done:
1710 if (TRACE_API_CALLS)
1711 tracing (" ==> VOID\n");
1712 }
1713
1714 /* Broadcast to all waiting on a condition variable. This is simply
1715 * calling Object.notifyAll for us.
1716 */
1717 static void
cond_broadcast_jni_impl(GCond * gcond)1718 cond_broadcast_jni_impl (GCond * gcond)
1719 {
1720 jobject condObj = (jobject) gcond;
1721 JNIEnv *env;
1722 union env_union e;
1723
1724 if (TRACE_API_CALLS)
1725 tracing ("cond_broadcast_jni_impl(condObj=%p)", condObj);
1726
1727 e.jni_env = &env;
1728 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1729 if (setup_cache (env) < 0)
1730 goto done;
1731 HIDE_OLD_TROUBLE (env);
1732
1733 assert (condObj);
1734 /* Must have locked an object to call notifyAll */
1735 if (ENTER_MONITOR (env, condObj))
1736 goto done;
1737
1738 (*env)->CallVoidMethod (env, condObj, obj_notifyall_mth);
1739 if (MAYBE_BROKEN (env, "cannot broadcast to mutex with Object.notify()"))
1740 {
1741 EXIT_MONITOR (env, condObj);
1742 goto done;
1743 }
1744
1745 EXIT_MONITOR (env, condObj);
1746
1747 SHOW_OLD_TROUBLE ();
1748
1749 done:
1750 if (TRACE_API_CALLS)
1751 tracing (" ==> VOID\n");
1752 }
1753
1754
1755 /* Wait on a condition variable. For us, this simply means calling
1756 * Object.wait.
1757 *
1758 * Throws a Java exception on trouble; may leave the mutexes set arbitrarily.
1759 * XXX TODO: Further improve error recovery.
1760 */
1761 static void
cond_wait_jni_impl(GCond * gcond,GMutex * gmutex)1762 cond_wait_jni_impl (GCond * gcond, GMutex * gmutex)
1763 {
1764 struct mutexObj_cache cache;
1765 jobject condObj = (jobject) gcond;
1766 jobject mutexObj = (jobject) gmutex;
1767 JNIEnv *env;
1768 union env_union e;
1769
1770 if (TRACE_API_CALLS)
1771 tracing ("cond_wait_jni_impl(condObj=%p, mutexObj=%p)",
1772 condObj, mutexObj);
1773
1774 e.jni_env = &env;
1775 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1776 if (setup_cache (env) < 0)
1777 goto done;
1778 HIDE_OLD_TROUBLE (env);
1779
1780 assert (condObj);
1781 assert (mutexObj);
1782 /* Must have locked a Java object to call wait on it */
1783 if (ENTER_MONITOR (env, condObj) < 0)
1784 goto done;
1785
1786 /* Our atomicity is now guaranteed; we're protected by the Java monitor on
1787 condObj. Unlock the GMutex. */
1788 if (mutexObj_unlock (env, mutexObj, &cache))
1789 goto done;
1790
1791 (*env)->CallVoidMethod (env, condObj, obj_wait_mth);
1792 if (MAYBE_BROKEN (env, "cannot wait on condObj"))
1793 {
1794 EXIT_MONITOR (env, condObj); /* ignore err checking */
1795 goto done;
1796 }
1797
1798 /* Re-acquire the lock on the GMutex. Do this while we're protected by the
1799 Java monitor on condObj. */
1800 if (mutexObj_lock (env, mutexObj, &cache))
1801 goto done;
1802
1803 EXIT_MONITOR (env, condObj);
1804
1805 SHOW_OLD_TROUBLE ();
1806
1807 done:
1808 if (TRACE_API_CALLS)
1809 tracing (" ==> VOID\n");
1810 }
1811
1812
1813 /** Wait on a condition variable until a timeout. This is a little tricky
1814 * for us. We first call Object.wait(J) giving it the appropriate timeout
1815 * value. On return, we check whether an InterruptedException happened. If
1816 * so, that is Java-speak for wait timing out.
1817 *
1818 * We return FALSE if we timed out. Return TRUE if the condition was
1819 * signalled first, before we timed out.
1820 *
1821 * In case of trouble we throw a Java exception. Whether we return FALSE or
1822 * TRUE depends upon whether the condition was raised before the trouble
1823 * happened.
1824 *
1825 * I believe that this function goes to the proper lengths to try to unlock
1826 * all of the locked mutexes and monitors, as appropriate, and that it further
1827 * tries to make sure that the thrown exception is the current one, not any
1828 * future cascaded one from something like a failure to unlock the monitors.
1829 */
1830 static gboolean
cond_timed_wait_jni_impl(GCond * gcond,GMutex * gmutex,GTimeVal * end_time)1831 cond_timed_wait_jni_impl (GCond * gcond, GMutex * gmutex, GTimeVal * end_time)
1832 {
1833 JNIEnv *env;
1834 union env_union e;
1835 jlong time_millisec;
1836 jint time_nanosec;
1837 jthrowable cause;
1838 jobject condObj = (jobject) gcond;
1839 jobject mutexObj = (jobject) gmutex;
1840 gboolean condRaised = FALSE; /* Condition has not been raised yet. */
1841 struct mutexObj_cache cache;
1842 gboolean interrupted;
1843
1844 if (TRACE_API_CALLS)
1845 {
1846 tracing ("cond_timed_wait_jni_impl(cond=%p, mutex=%p,"
1847 " end_time=< sec=%lu, usec=%lu >)", condObj, mutexObj,
1848 (unsigned long) end_time->tv_sec,
1849 (unsigned long) end_time->tv_usec);
1850 }
1851
1852
1853 e.jni_env = &env;
1854 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1855 if (setup_cache (env) < 0)
1856 goto done;
1857 HIDE_OLD_TROUBLE (env);
1858
1859 time_millisec = end_time->tv_sec * 1000 + end_time->tv_usec / 1000;
1860 time_nanosec = 1000 * (end_time->tv_usec % 1000);
1861
1862 /* Must have locked an object to call wait */
1863 if (ENTER_MONITOR (env, condObj) < 0)
1864 goto done;
1865
1866 if (mutexObj_unlock (env, mutexObj, &cache) < 0)
1867 {
1868 if (EXIT_MONITOR (env, condObj) < 0)
1869 criticalMsg
1870 ("Unable to unlock an existing lock on a condition; your proram may deadlock");
1871 goto done;
1872 }
1873
1874
1875 (*env)->CallVoidMethod (env, condObj, obj_wait_nanotime_mth,
1876 time_millisec, time_nanosec);
1877
1878 /* If there was trouble, save that fact, and the reason for the trouble. We
1879 want to respond to this condition as fast as possible. */
1880 cause = (*env)->ExceptionOccurred (env);
1881
1882 if ( ! cause )
1883 {
1884 condRaised = TRUE; /* condition was signalled */
1885 }
1886 else if ((*env)->IsInstanceOf (env, cause, interrupted_exception_class))
1887 {
1888 condRaised = FALSE; /* Condition was not raised before timeout.
1889 (This is redundant with the initialization
1890 of condRaised above) */
1891 (*env)->ExceptionClear (env); /* Clear the InterruptedException. */
1892 cause = NULL; /* no pending cause now. */
1893 }
1894 else
1895 {
1896 interrupted = FALSE; /* Trouble, but not because of
1897 InterruptedException. Assume the condition
1898 was not raised. */
1899 /* Leave condRaised set to FALSE */
1900 }
1901
1902 /* Irrespective of whether there is a pending problem to report, go ahead
1903 and try to clean up. This may end up throwing an exception that is
1904 different from the one that was thrown by the call to Object.wait().
1905 So we will override it with the first exception (don't want to have
1906 cascading problems). */
1907 if (mutexObj_lock (env, mutexObj, &cache) && !cause)
1908 {
1909 cause = (*env)->ExceptionOccurred (env);
1910 assert (cause);
1911 }
1912
1913 if (EXIT_MONITOR (env, condObj) && !cause)
1914 {
1915 cause = (*env)->ExceptionOccurred (env);
1916 assert (cause);
1917 }
1918
1919 if (cause) /* Raise the first cause. */
1920 {
1921 BROKEN_CAUSE (env, cause, "error in timed wait or during its cleanup");
1922 goto done;
1923 }
1924
1925 SHOW_OLD_TROUBLE ();
1926
1927 done:
1928 if (TRACE_API_CALLS)
1929 tracing (" ==> condRaised = %s\n", condRaised ? "TRUE" : "FALSE");
1930 return condRaised;
1931 }
1932
1933
1934 /* Free a condition variable. (isn't C fun?). Can not fail. */
1935 static void
cond_free_jni_impl(GCond * cond)1936 cond_free_jni_impl (GCond * cond)
1937 {
1938 jobject condObj = (jobject) cond;
1939 JNIEnv *env;
1940 union env_union e;
1941
1942 if (TRACE_API_CALLS)
1943 tracing ("cond_free_jni_impl(condObj = %p)", condObj);
1944 e.jni_env = &env;
1945 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1946
1947 freeObject (env, condObj);
1948
1949 if (TRACE_API_CALLS)
1950 tracing (" ==> VOID\n");
1951 }
1952
1953
1954 /************************************************************************/
1955 /* Thread-local data code */
1956 /************************************************************************/
1957
1958 /* Create a new thread-local key. We use java.lang.ThreadLocal objects
1959 * for this. This returns the pointer representation of a Java global
1960 * reference.
1961 *
1962 * We will throw a Java exception and return NULL in case of failure.
1963 */
1964 static GPrivate *
private_new_jni_impl(GDestroyNotify notify)1965 private_new_jni_impl (GDestroyNotify notify __attribute__ ((unused)))
1966 {
1967 JNIEnv *env;
1968 union env_union e;
1969 jobject lcl_key;
1970 jobject global_key;
1971 GPrivate *gkey = NULL; /* Error return code */
1972
1973 if (TRACE_API_CALLS)
1974 tracing ("private_new_jni_impl()");
1975
1976 e.jni_env = &env;
1977 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
1978 if (setup_cache (env) < 0)
1979 goto done;
1980 HIDE_OLD_TROUBLE (env);
1981
1982 lcl_key = (*env)->NewObject (env, threadlocal_class, threadlocal_ctor);
1983 if ( ! lcl_key )
1984 {
1985 BROKEN (env, "cannot allocate a ThreadLocal");
1986 goto done;
1987 }
1988
1989 global_key = ((*env)->NewGlobalRef (env, lcl_key));
1990 DELETE_LOCAL_REF (env, lcl_key);
1991 if ( ! global_key)
1992 {
1993 NEW_BROKEN (env, "cannot create a GlobalRef to a new ThreadLocal");
1994 goto done;
1995 }
1996
1997 gkey = (GPrivate *) global_key;
1998 SHOW_OLD_TROUBLE ();
1999
2000 done:
2001 if (TRACE_API_CALLS)
2002 tracing (" ==> %p\n", (void *) gkey);
2003
2004 return gkey;
2005 }
2006
2007 /* Get this thread's value for a thread-local key. This is simply
2008 * ThreadLocal.get for us. Return NULL if no value. (I can't think of
2009 * anything else to do.)
2010 */
2011 static gpointer
private_get_jni_impl(GPrivate * gkey)2012 private_get_jni_impl (GPrivate * gkey)
2013 {
2014 JNIEnv *env;
2015 union env_union e;
2016 jobject val_wrapper;
2017 jobject keyObj = (jobject) gkey;
2018 gpointer thread_specific_data = NULL; /* Init to the error-return value */
2019
2020 jlong val;
2021
2022 if (TRACE_API_CALLS)
2023 tracing ("private_get_jni_impl(keyObj=%p)", keyObj);
2024
2025 e.jni_env = &env;
2026 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
2027 if (setup_cache (env) < 0)
2028 goto done;
2029 HIDE_OLD_TROUBLE (env);
2030
2031 val_wrapper = (*env)->CallObjectMethod (env, keyObj, threadlocal_get_mth);
2032 if (MAYBE_BROKEN (env, "cannot find thread-local object"))
2033 goto done;
2034
2035 if (! val_wrapper )
2036 {
2037 /* It's Java's "null" object. No ref found. This is OK; we must never
2038 have set a value in this thread. Note that this next statement is
2039 not necessary, strictly speaking, since we're already initialized to
2040 NULL. A good optimizing C compiler will detect that and optimize out
2041 this statement. */
2042 thread_specific_data = NULL;
2043 goto done;
2044 }
2045
2046 val = (*env)->CallLongMethod (env, val_wrapper, long_longValue_mth);
2047
2048 if (MAYBE_BROKEN (env, "cannot get thread local value"))
2049 goto done;
2050
2051 thread_specific_data = (gpointer) (intptr_t) val;
2052
2053 /* Only re-raise the old pending exception if a new one hasn't come along to
2054 supersede it. */
2055 SHOW_OLD_TROUBLE ();
2056
2057 done:
2058
2059 if (TRACE_API_CALLS)
2060 tracing (" ==> %p\n", thread_specific_data);
2061
2062 return thread_specific_data;
2063 }
2064
2065 /* Set this thread's value for a thread-local key. This is simply
2066 * ThreadLocal.set() for us.
2067 */
2068 static void
private_set_jni_impl(GPrivate * gkey,gpointer thread_specific_data)2069 private_set_jni_impl (GPrivate * gkey, gpointer thread_specific_data)
2070 {
2071 JNIEnv *env;
2072 union env_union e;
2073 jobject val_wrapper;
2074 jobject keyObj = (jobject) gkey;
2075
2076
2077 if (TRACE_API_CALLS)
2078 tracing ("private_set_jni_impl(keyObj=%p, thread_specific_data=%p)",
2079 keyObj, thread_specific_data);
2080
2081 e.jni_env = &env;
2082 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
2083 if (setup_cache (env) < 0)
2084 goto done;
2085 HIDE_OLD_TROUBLE (env);
2086
2087 /* We are just going to always use a Java long to represent a C pointer.
2088 Otherwise all of the code would end up being conditionalized for various
2089 pointer sizes, and that seems like too much of a hassle, in order to save
2090 a paltry few bytes, especially given the horrendous overhead of JNI in
2091 any case.
2092 */
2093
2094 val_wrapper = (*env)->NewObject (env, long_class, long_ctor,
2095 (jlong) (intptr_t) thread_specific_data);
2096 if ( ! val_wrapper )
2097 {
2098 BROKEN (env, "cannot create a java.lang.Long");
2099 goto done;
2100 }
2101
2102 /* At this point, we now have set lcl_obj as a numeric class that wraps
2103 around the thread-specific data we were given. */
2104 (*env)->CallVoidMethod (env, keyObj, threadlocal_set_mth, val_wrapper);
2105 if (MAYBE_BROKEN (env, "cannot set thread local value"))
2106 goto done;
2107
2108 SHOW_OLD_TROUBLE ();
2109 done:
2110 if (TRACE_API_CALLS)
2111 tracing (" ==> VOID\n");
2112 }
2113
2114
2115 /** Create an object of type gnu.java.awt.peer.gtk.GThreadNativeMethodRunner.
2116 Run it.
2117
2118 We need to create joinable threads. We handle the notion of a joinable
2119 thread by determining whether or not we are going to maintain a permanent
2120 hard reference to it until it croaks.
2121
2122 Posix does not appear to have a Java-like concept of daemon threads, where
2123 the JVM will exit when there are only daemon threads running.
2124
2125 Error handling:
2126
2127 To quote from the glib guide:
2128 "GError should only be used to report recoverable runtime errors, never
2129 to report programming errors."
2130
2131 So how do we consider the failure to create a thread? Well, each of the
2132 failure cases in this function are discussed, and none of them are really
2133 recoverable.
2134
2135 The glib library is really designed so that you should fail
2136 catastrophically in case of "programming errors". The only error defined
2137 for the GThread functions is G_THREAD_ERROR_AGAIN, and that for
2138 thread_create.
2139
2140 Most of these GThread functions could fail if we run out of memory, for
2141 example, but the only one capable of reporting that fact is
2142 thread_create. */
2143 static void
thread_create_jni_impl(GThreadFunc func,gpointer data,gulong stack_size,gboolean joinable,gboolean bound,GThreadPriority gpriority,gpointer threadIDp,GError ** errorp)2144 thread_create_jni_impl (GThreadFunc func,
2145 gpointer data,
2146 gulong stack_size __attribute__((unused)),
2147 gboolean joinable,
2148 gboolean bound __attribute__((unused)),
2149 GThreadPriority gpriority,
2150 /* This prototype is horrible. threadIDp is actually
2151 a gpointer to the thread's thread-ID. Which is,
2152 of course, itself a gpointer-typed value. Ouch. */
2153 gpointer threadIDp,
2154 /* Do not touch the GError stuff unless you have
2155 RECOVERABLE trouble. There is no recoverable
2156 trouble in this implementation. */
2157 GError **errorp __attribute__((unused)))
2158 {
2159 JNIEnv *env;
2160 union env_union e;
2161 union func_union f;
2162 jboolean jjoinable = joinable;
2163 jobject newThreadObj;
2164 gpointer threadID; /* to be filled in */
2165
2166 if (TRACE_API_CALLS)
2167 {
2168 f.g_func = func;
2169 tracing ("thread_create_jni_impl(func=%p, data=%p, joinable=%s,"
2170 " threadIDp=%p, *(int *) threadIDp = %d)",
2171 f.void_func, data, joinable ? "TRUE" : "FALSE",
2172 threadIDp, *(int *) threadIDp);
2173 }
2174
2175 e.jni_env = &env;
2176 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
2177 if (setup_cache (env) < 0)
2178 {
2179 /* The failed call to setup the cache is certainly not recoverable;
2180 not appropriate for G_THREAD_ERROR_AGAIN. */
2181 *(gpointer *) threadIDp = NULL;
2182 goto done;
2183 }
2184 HIDE_OLD_TROUBLE (env);
2185
2186 /* If a thread is joinable, then notify its constructor. The constructor
2187 will enter a hard reference for it, and the hard ref. won't go away until
2188 the thread has been joined. */
2189 newThreadObj =
2190 (*env)->NewObject (env, runner_class, runner_ctor,
2191 (jlong) (intptr_t) func, (jlong) (intptr_t) data,
2192 jjoinable);
2193 if ( ! newThreadObj )
2194 {
2195 BROKEN (env, "creating a new thread failed in the constructor");
2196 *(gpointer *) threadIDp = NULL;
2197 /* The failed call to the constructor does not throw any errors such
2198 that G_THREAD_ERROR_AGAIN is appropriate. No other recoverable
2199 errors defined. Once again, we go back to the VM. */
2200 goto done;
2201 }
2202
2203 if (threadObj_set_priority (env, newThreadObj, gpriority) < 0)
2204 {
2205 *(gpointer *) threadIDp = NULL;
2206 /* None of these possible exceptions from Thread.setPriority() are
2207 recoverable, so they are not appropriate for EAGAIN. So we should
2208 fail. */
2209 goto done;
2210 }
2211
2212 (*env)->CallVoidMethod (env, runner_class, runner_start_mth);
2213
2214 if (MAYBE_BROKEN (env, "starting a new thread failed"))
2215 {
2216 *(gpointer *) threadIDp = NULL;
2217 /* The only exception Thread.start() throws is
2218 IllegalStateException. And that would indicate a programming error.
2219
2220 So there are no situations such that G_THREAD_ERROR_AGAIN would be
2221 OK.
2222
2223 So, we don't use g_set_error() here to perform any error reporting.
2224 */
2225 goto done;
2226 }
2227
2228 threadID = getThreadIDFromThread (env, newThreadObj);
2229
2230 *(gpointer *) threadIDp = threadID;
2231 SHOW_OLD_TROUBLE ();
2232
2233 done:
2234 if (TRACE_API_CALLS)
2235 tracing (" ==> (threadID = %p) \n", threadID);
2236 }
2237
2238
2239 /* Wraps a call to g_thread_yield. */
2240 static void
thread_yield_jni_impl(void)2241 thread_yield_jni_impl (void)
2242 {
2243 JNIEnv *env;
2244 union env_union e;
2245
2246 if (TRACE_API_CALLS)
2247 tracing ("thread_yield_jni_impl()");
2248
2249 e.jni_env = &env;
2250 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
2251 if (setup_cache (env) < 0)
2252 goto done;
2253 HIDE_OLD_TROUBLE (env);
2254
2255 (*env)->CallStaticVoidMethod (env, thread_class, thread_yield_mth);
2256 if (MAYBE_BROKEN (env, "Thread.yield() failed"))
2257 goto done;
2258
2259 SHOW_OLD_TROUBLE ();
2260
2261 done:
2262 if (TRACE_API_CALLS)
2263 tracing (" ==> VOID\n");
2264 }
2265
2266
2267 static void
thread_join_jni_impl(gpointer threadID)2268 thread_join_jni_impl (gpointer threadID)
2269 {
2270 JNIEnv *env;
2271 union env_union e;
2272 jobject threadObj = NULL;
2273
2274 if ( TRACE_API_CALLS )
2275 tracing ("thread_join_jni_impl(threadID=%p) ", threadID);
2276
2277 e.jni_env = &env;
2278 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
2279 if (setup_cache (env) < 0)
2280 goto done;
2281 HIDE_OLD_TROUBLE (env);
2282
2283 threadObj = getThreadFromThreadID (env, threadID);
2284 if ( ! threadObj ) /* Already reported with BROKEN */
2285 goto done;
2286
2287 (*env)->CallVoidMethod (env, threadObj, thread_join_mth);
2288 if (MAYBE_BROKEN (env, "Thread.join() failed"))
2289 goto done;
2290
2291
2292 (*env)->CallStaticVoidMethod
2293 (env, runner_class, runner_deRegisterJoinable_mth, threadObj);
2294 if (MAYBE_BROKEN (env, "Thread.deRegisterJoinableThread() failed"))
2295 goto done;
2296
2297 SHOW_OLD_TROUBLE ();
2298
2299 done:
2300 DELETE_LOCAL_REF (env, threadObj);
2301 if (TRACE_API_CALLS)
2302 tracing (" ==> VOID \n");
2303 }
2304
2305 /* Terminate the current thread. Unlike pthread_exit(), here we do not need
2306 to bother with a return value or exit value for the thread which is about
2307 to croak. (The gthreads abstraction doesn't use it.) However, we *do*
2308 need to bail immediately. We handle this with Thread.stop(), which is
2309 a deprecated method.
2310
2311 It's deprecated since we might leave objects protected by monitors in
2312 half-constructed states on the way out -- Thread.stop() throws a
2313 ThreadDeath exception, which is usually unchecked. There is no good
2314 solution that I can see. */
2315 static void
thread_exit_jni_impl(void)2316 thread_exit_jni_impl (void)
2317 {
2318 JNIEnv *env;
2319 union env_union e;
2320 jobject this_thread;
2321
2322 if (TRACE_API_CALLS)
2323 tracing ("thread_exit_jni_impl() ");
2324
2325 e.jni_env = &env;
2326 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
2327 if (setup_cache (env) < 0)
2328 goto done;
2329
2330 HIDE_OLD_TROUBLE (env);
2331
2332 this_thread = (*env)->
2333 CallStaticObjectMethod (env, thread_class, thread_current_mth);
2334
2335 if ( ! this_thread )
2336 {
2337 BROKEN (env, "cannot get current thread");
2338 goto done;
2339 }
2340
2341 (*env)->CallVoidMethod (env, this_thread, thread_stop_mth);
2342 if (MAYBE_BROKEN (env, "cannot call Thread.stop() on current thread"))
2343 goto done;
2344
2345 SHOW_OLD_TROUBLE ();
2346
2347 done:
2348 if (TRACE_API_CALLS)
2349 tracing (" ==> VOID \n");
2350 }
2351
2352
2353 /* Translate a GThreadPriority to a Java priority level. */
2354 static jint
javaPriorityLevel(GThreadPriority priority)2355 javaPriorityLevel (GThreadPriority priority)
2356 {
2357 /* We have these fields in java.lang.Thread to play with:
2358
2359 static int MIN_PRIORITY The minimum priority that a thread can have.
2360 static int NORM_PRIORITY The default priority that is assigned to a
2361 thread.
2362 static int MAX_PRIORITY The maximum priority that a thread can have.
2363
2364 We get these from the header file generated by javah, even though they're
2365 documented as being 1, 5, and 10.
2366 */
2367 static const jint minJPri =
2368 gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MIN_PRIORITY;
2369 static const jint normJPri =
2370 gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_NORM_PRIORITY;
2371 static const jint maxJPri =
2372 gnu_java_awt_peer_gtk_GThreadNativeMethodRunner_MAX_PRIORITY;
2373
2374 switch (priority)
2375 {
2376 case G_THREAD_PRIORITY_LOW:
2377 return minJPri;
2378 break;
2379
2380 default:
2381 assert_not_reached ();
2382 /* Deliberate fall-through if assertions are turned off; also shuts up
2383 GCC warnings if they're turned on. */
2384 case G_THREAD_PRIORITY_NORMAL:
2385 return normJPri;
2386 break;
2387
2388 case G_THREAD_PRIORITY_HIGH:
2389 return (normJPri + maxJPri) / 2;
2390 break;
2391
2392 case G_THREAD_PRIORITY_URGENT:
2393 return maxJPri;
2394 break;
2395 }
2396 }
2397
2398
2399 /** It would be safe not to implement this, according to the JNI docs, since
2400 not all platforms do thread priorities. However, we might as well
2401 provide the hint for those who want it.
2402 */
2403 static void
thread_set_priority_jni_impl(gpointer gThreadID,GThreadPriority gpriority)2404 thread_set_priority_jni_impl (gpointer gThreadID, GThreadPriority gpriority)
2405 {
2406 jobject threadObj = NULL;
2407 JNIEnv *env;
2408 union env_union e;
2409
2410 if (TRACE_API_CALLS)
2411 tracing ("thread_set_priority_jni_impl(gThreadID=%p, gpriority = %u) ",
2412 gThreadID, gpriority);
2413
2414 e.jni_env = &env;
2415 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
2416
2417 if (setup_cache (env) < 0)
2418 goto done;
2419
2420 HIDE_OLD_TROUBLE (env);
2421
2422
2423 threadObj = getThreadFromThreadID (env, gThreadID);
2424 if ( ! threadObj) /* Reported with BROKEN already. */
2425 goto done;
2426
2427 if (threadObj_set_priority (env, threadObj, gpriority))
2428 goto done;
2429
2430 SHOW_OLD_TROUBLE ();
2431
2432 done:
2433 DELETE_LOCAL_REF (env, threadObj);
2434
2435 if (TRACE_API_CALLS)
2436 tracing (" ==> VOID\n");
2437 }
2438
2439
2440 /** It would be safe not to implement this, according to the JNI docs, since
2441 not all platforms do thread priorities. However, we might as well
2442 provide the hint for those who want it.
2443
2444 -1 on failure, 0 on success. */
2445 static int
threadObj_set_priority(JNIEnv * env,jobject threadObj,GThreadPriority gpriority)2446 threadObj_set_priority (JNIEnv * env, jobject threadObj,
2447 GThreadPriority gpriority)
2448 {
2449 jint javaPriority = javaPriorityLevel (gpriority);
2450 (*env)->CallVoidMethod (env, threadObj, thread_setPriority_mth,
2451 javaPriority);
2452 return MAYBE_BROKEN (env, "Thread.setPriority() failed");
2453 }
2454
2455
2456 /** Return the result of Thread.currentThread(), a static method. */
2457 static void
thread_self_jni_impl(gpointer my_thread_IDp)2458 thread_self_jni_impl (/* Another confusing glib prototype. This is
2459 actually a gpointer to the thread's thread-ID.
2460 Which is, of course, a gpointer. */
2461 gpointer my_thread_IDp)
2462 {
2463 JNIEnv *env;
2464 union env_union e;
2465 jobject this_thread;
2466 gpointer my_threadID;
2467
2468 if (TRACE_API_CALLS)
2469 tracing ("thread_self_jni_impl(my_thread_IDp=%p)", my_thread_IDp);
2470
2471 e.jni_env = &env;
2472 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
2473
2474 if (setup_cache (env) < 0)
2475 return;
2476
2477 HIDE_OLD_TROUBLE (env);
2478
2479 this_thread = (*env)->
2480 CallStaticObjectMethod (env, thread_class, thread_current_mth);
2481 if (! this_thread )
2482 {
2483 BROKEN (env, "cannot get current thread");
2484 my_threadID = NULL;
2485 goto done;
2486 }
2487
2488 my_threadID = getThreadIDFromThread (env, this_thread);
2489 SHOW_OLD_TROUBLE ();
2490
2491 done:
2492 if (TRACE_API_CALLS)
2493 tracing (" ==> (my_threadID = %p) \n", my_threadID);
2494
2495 *(gpointer *) my_thread_IDp = my_threadID;
2496 }
2497
2498
2499 static gboolean
thread_equal_jni_impl(gpointer thread1,gpointer thread2)2500 thread_equal_jni_impl (gpointer thread1, gpointer thread2)
2501 {
2502 JNIEnv *env;
2503 union env_union e;
2504
2505 gpointer threadID1 = *(gpointer *) thread1;
2506 gpointer threadID2 = *(gpointer *) thread2;
2507
2508 jobject thread1_obj = NULL;
2509 jobject thread2_obj = NULL;
2510 gboolean ret;
2511
2512 if (TRACE_API_CALLS)
2513 tracing ("thread_equal_jni_impl(threadID1=%p, threadID2=%p)",
2514 threadID1, threadID2);
2515
2516 e.jni_env = &env;
2517 (*cp_gtk_the_vm)->GetEnv (cp_gtk_the_vm, e.void_env, JNI_VERSION_1_1);
2518 if (setup_cache (env) < 0)
2519 {
2520 ret = FALSE; /* what is safer? We really don't ever want
2521 to return from here. */
2522 goto done;
2523 }
2524
2525 HIDE_OLD_TROUBLE (env);
2526 thread1_obj = getThreadFromThreadID (env, threadID1);
2527 thread2_obj = getThreadFromThreadID (env, threadID2);
2528
2529 ret = (*env)->CallBooleanMethod (env, thread1_obj,
2530 thread_equals_mth, thread2_obj);
2531
2532 if (MAYBE_BROKEN (env, "Thread.equals() failed"))
2533 {
2534 ret = FALSE;
2535 goto done;
2536 }
2537
2538 SHOW_OLD_TROUBLE ();
2539
2540
2541 done:
2542 DELETE_LOCAL_REF (env, thread1_obj);
2543 DELETE_LOCAL_REF (env, thread2_obj);
2544
2545 if (TRACE_API_CALLS)
2546 tracing (" ==> %s\n", ret ? "TRUE" : "FALSE");
2547
2548 return ret;
2549 }
2550
2551
2552
2553
2554 /************************************************************************/
2555 /* GLIB interface */
2556 /************************************************************************/
2557
2558 /* set of function pointers to give to glib. */
2559 GThreadFunctions cp_gtk_portable_native_sync_jni_functions = {
2560 mutex_new_jni_impl, /* mutex_new */
2561 mutex_lock_jni_impl, /* mutex_lock */
2562 mutex_trylock_jni_impl, /* mutex_trylock */
2563 mutex_unlock_jni_impl, /* mutex_unlock */
2564 mutex_free_jni_impl, /* mutex_free */
2565 cond_new_jni_impl, /* cond_new */
2566 cond_signal_jni_impl, /* cond_signal */
2567 cond_broadcast_jni_impl, /* cond_broadcast */
2568 cond_wait_jni_impl, /* cond_wait */
2569 cond_timed_wait_jni_impl, /* cond_timed_wait */
2570 cond_free_jni_impl, /* cond_free */
2571 private_new_jni_impl, /* private_new */
2572 private_get_jni_impl, /* private_get */
2573 private_set_jni_impl, /* private_set */
2574 thread_create_jni_impl, /* thread_create */
2575 thread_yield_jni_impl, /* thread_yield */
2576 thread_join_jni_impl, /* thread_join */
2577 thread_exit_jni_impl, /* thread_exit */
2578 thread_set_priority_jni_impl, /* thread_set_priority */
2579 thread_self_jni_impl, /* thread_self */
2580 thread_equal_jni_impl, /* thread_equal */
2581 };
2582
2583
2584 /* Keep c-font-lock-extra-types in alphabetical order. */
2585 /* Local Variables: */
2586 /* c-file-style: "gnu" */
2587 /* c-font-lock-extra-types: ("\\sw+_t" "gboolean" "GError" "gpointer"
2588 "GPrivate" "GThreadFunc" "GThreadFunctions" "GThreadPriority"
2589 "gulong"
2590 "JNIEnv"
2591 "jboolean" "jclass" "jfieldID" "jint" "jlong" "jmethodID" "jobject" "jstring" "jthrowable" ) */
2592 /* End: */
2593