2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / jni / gtk-peer / gthread-jni.c
bloba762b4e56edf87018b910ecc0bc73a1f1948de6f
1 /* gthread-jni.c -- JNI threading routines for GLIB
2 Copyright (C) 1998 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
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.
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. */
38 /************************************************************************/
39 /* Header */
40 /************************************************************************/
43 * Julian Dolby (dolby@us.ibm.com)
44 * February 7, 2003
46 * This code implements the GThreadFunctions interface for GLIB using
47 * Java threading primitives. All of the locking and conditional variable
48 * functionality required by GThreadFunctions is implemented using the
49 * monitor and wait/notify functionality of Java objects. The thread-
50 * local fucntionality uses the java.lang.ThreadLocal class.
52 * This code is designed to be portable in that it makes no assumptions
53 * about the underlying VM beyond that it implements the JNI functionality
54 * that this code uses.
56 * The one piece that does not really work is trylock for mutexes. The
57 * Java locking model does not include such functionality, and I do not
58 * see how to implement it without knowing something about how the VM
59 * implements locking.
61 * NOTES:
63 * I have tested it only on JikesRVM---the CVS head as of early February
64 * 2003.
66 * Currently, use of this code is governed by the configuration option
67 * --enable-portable-native-sync
72 /************************************************************************/
73 /* Global data */
74 /************************************************************************/
76 #include "gthread-jni.h"
78 /* The VM handle. This is set in GtkToolkitMain.gtkInit */
79 JavaVM *gdk_vm;
82 /************************************************************************/
83 /* Utilities to reflect exceptions back to the VM */
84 /************************************************************************/
86 /* This function checks for a pending exception, and rethrows it with
87 * a wrapper RuntimeException to deal with possible type problems (in
88 * case some calling piece of code does not expect the exception being
89 * thrown) and to include the given extra message.
91 static void maybe_rethrow(JNIEnv *gdk_env, char *message, char *file, int line) {
92 jthrowable cause;
94 /* rethrow if an exception happened */
95 if ((cause = (*gdk_env)->ExceptionOccurred(gdk_env)) != NULL) {
96 jstring jmessage;
97 jclass obj_class;
98 jobject obj;
99 jmethodID ctor;
101 /* allocate local message in Java */
102 int len = strlen(message) + strlen(file) + 25;
103 char buf[ len ];
104 bzero(buf, len);
105 sprintf(buf, "%s (at %s:%d)", message, file, line);
106 jmessage = (*gdk_env)->NewStringUTF(gdk_env, buf);
108 /* create RuntimeException wrapper object */
109 obj_class = (*gdk_env)->FindClass (gdk_env, "java/lang/RuntimeException");
110 ctor = (*gdk_env)->GetMethodID(gdk_env, obj_class, "<init>", "(Ljava/langString;Ljava/lang/Throwable)V");
111 obj = (*gdk_env)->NewObject (gdk_env, obj_class, ctor, jmessage, cause);
113 /* throw it */
114 (*gdk_env)->Throw(gdk_env, (jthrowable)obj);
118 /* This macro is used to include a source location in the exception message */
119 #define MAYBE_RETHROW(_class, _message) \
120 maybe_rethrow(_class, _message, __FILE__, __LINE__)
123 /************************************************************************/
124 /* Utilities to allocate and free java.lang.Objects */
125 /************************************************************************/
127 /* Both the mutexes and the condition variables are java.lang.Object objects,
128 * which this method allocates and returns a global ref. Note that global
129 * refs must be explicitly freed (isn't C fun?).
131 static jobject *allocatePlainObject() {
132 jclass obj_class;
133 jobject *obj;
134 JNIEnv *gdk_env;
135 jmethodID ctor;
137 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
139 obj_class = (*gdk_env)->FindClass (gdk_env, "java/lang/Object");
140 MAYBE_RETHROW(gdk_env, "cannot find Object");
142 ctor = (*gdk_env)->GetMethodID(gdk_env, obj_class, "<init>", "()V");
143 MAYBE_RETHROW(gdk_env, "cannot find constructor");
145 obj = (jobject *) g_malloc (sizeof (jobject));
146 *obj = (*gdk_env)->NewObject (gdk_env, obj_class, ctor);
147 MAYBE_RETHROW(gdk_env, "cannot allocate object");
149 *obj = (*gdk_env)->NewGlobalRef (gdk_env, *obj);
150 MAYBE_RETHROW(gdk_env, "cannot make global ref");
152 return obj;
155 /* Frees a Java object given a global ref (isn't C fun?) */
156 static void freePlainObject(jobject *obj) {
157 JNIEnv *gdk_env;
159 if (obj) {
160 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
162 (*gdk_env)->DeleteGlobalRef (gdk_env, *obj);
163 MAYBE_RETHROW(gdk_env, "cannot delete global ref");
165 g_free (obj);
170 /************************************************************************/
171 /* Locking code */
172 /************************************************************************/
174 /* Lock a Java object */
175 static void takeLock(JNIEnv *gdk_env, void *mutex) {
176 (*gdk_env)->MonitorEnter (gdk_env, *((jobject *)mutex));
177 MAYBE_RETHROW(gdk_env, "cannot get lock");
180 /* Unlock a Java object */
181 static void releaseLock(JNIEnv *gdk_env, void *mutex) {
182 (*gdk_env)->MonitorExit (gdk_env, *((jobject *)mutex));
183 MAYBE_RETHROW(gdk_env, "cannot release lock");
186 /* Create a mutex, which is a java.lang.Object for us */
187 static GMutex *g_mutex_new_jni_impl (void) {
188 return (GMutex*) allocatePlainObject();
191 /* Lock a mutex. */
192 static void g_mutex_lock_jni_impl (GMutex *mutex __attribute__((unused))) {
193 JNIEnv *gdk_env;
195 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
197 takeLock(gdk_env, mutex);
200 /* Try to lock a mutex. Actually, do not try because Java objects
201 * do not provide such an interface. To be at least minimally correct,
202 * pretend we tried and failed.
204 static gboolean g_mutex_trylock_jni_impl
205 (GMutex *mutex __attribute__((unused)))
207 // Shall we implement this in a JikesRVM-specific way under a flag?
208 return FALSE;
211 /* Unlock a mutex. */
212 static void g_mutex_unlock_jni_impl (GMutex *mutex) {
213 JNIEnv *gdk_env;
215 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
217 releaseLock(gdk_env, mutex);
220 /* Free a mutex (isn't C fun?) */
221 static void g_mutex_free_jni_impl (GMutex *mutex)
223 freePlainObject( (jobject*)mutex );
227 /************************************************************************/
228 /* Condition variable code */
229 /************************************************************************/
231 /* Create a new condition variable. This is a java.lang.Object for us. */
232 static GCond *g_cond_new_jni_impl () {
233 return (GCond*)allocatePlainObject();
236 /* Signal on a condition variable. This is simply calling Object.notify
237 * for us.
239 static void g_cond_signal_jni_impl (GCond *cond) {
240 jclass lcl_class;
241 jmethodID signal_mth;
242 JNIEnv *gdk_env;
244 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
246 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
247 MAYBE_RETHROW(gdk_env, "cannot find Object");
249 signal_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notify", "()V");
250 MAYBE_RETHROW(gdk_env, "cannot find Object.<notify>");
252 /* Must have locked an object to call notify */
253 takeLock(gdk_env, cond);
255 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, signal_mth);
256 MAYBE_RETHROW(gdk_env, "cannot signal mutex");
258 releaseLock(gdk_env, cond);
261 /* Broadcast to all waiting on a condition variable. This is simply
262 * calling Object.notifyAll for us.
264 static void g_cond_broadcast_jni_impl (GCond *cond) {
265 jclass lcl_class;
266 jmethodID bcast_mth;
267 JNIEnv *gdk_env;
269 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
271 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
272 MAYBE_RETHROW(gdk_env, "cannot find Object");
274 bcast_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notifyAll", "()V");
275 MAYBE_RETHROW(gdk_env, "cannot find Object.<notifyAll>");
277 /* Must have locked an object to call notifyAll */
278 takeLock(gdk_env, cond);
280 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, bcast_mth);
281 MAYBE_RETHROW(gdk_env, "cannot broadcast to mutex");
283 releaseLock(gdk_env, cond);
287 /* Wait on a condition variable. For us, this simply means call
288 * Object.wait.
290 static void g_cond_wait_jni_impl
291 (GCond *cond, GMutex *mutex __attribute__((unused)))
293 jclass lcl_class;
294 jmethodID wait_mth;
295 JNIEnv *gdk_env;
297 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
299 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
300 MAYBE_RETHROW(gdk_env, "cannot find Object");
302 wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "()V");
303 MAYBE_RETHROW(gdk_env, "cannot find Object.<wait>");
305 /* Must have locked an object to call wait */
306 takeLock(gdk_env, cond);
308 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth);
309 MAYBE_RETHROW(gdk_env, "cannot wait on mutex");
311 releaseLock(gdk_env, cond);
314 /* Wait on a condition vairable until a timeout. This is a little tricky
315 * for us. We first call Object.wait(J) giving it the appropriate timeout
316 * value. On return, we check whether an InterruptedException happened. If
317 * so, that is Java-speak for wait timing out.
319 static gboolean
320 g_cond_timed_wait_jni_impl
321 (GCond *cond, GMutex *mutex __attribute__((unused)),
322 GTimeVal *end_time)
324 jclass lcl_class;
325 jmethodID wait_mth;
326 JNIEnv *gdk_env;
327 jlong time;
328 jthrowable cause;
330 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
332 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
333 MAYBE_RETHROW(gdk_env, "cannot find Object");
335 wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "(J)V");
336 MAYBE_RETHROW(gdk_env, "cannot find Object.<wait(J)>");
338 time = end_time->tv_sec*1000;
339 time += end_time->tv_usec/1000;
341 /* Must have locked an object to call wait */
342 takeLock(gdk_env, cond);
344 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth, time);
346 if ((cause = (*gdk_env)->ExceptionOccurred(gdk_env)) != NULL) {
347 jclass intr = (*gdk_env)->FindClass (gdk_env, "java.lang.InterruptedException");
348 if ( (*gdk_env)->IsInstanceOf(gdk_env, cause, intr) ) {
349 releaseLock(gdk_env, cond);
350 return FALSE;
351 } else {
352 MAYBE_RETHROW(gdk_env, "error in timed wait");
356 releaseLock(gdk_env, cond);
358 return TRUE;
361 /* Free a condition variable. (isn't C fun?) */
362 static void g_cond_free_jni_impl (GCond *cond) {
363 freePlainObject( (jobject*)cond );
367 /************************************************************************/
368 /* Thread-local data code */
369 /************************************************************************/
371 /* Create a new thread-local key. We use java.lang.ThreadLocal objects
372 * for this.
374 static GPrivate *g_private_new_jni_impl
375 (GDestroyNotify notify __attribute__((unused)))
377 jclass lcl_class;
378 jobject *local;
379 JNIEnv *gdk_env;
380 jmethodID ctor;
382 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
384 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
385 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
387 ctor = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "<init>", "()V");
388 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<init>");
390 local = (jobject *) g_malloc (sizeof (jobject));
391 *local = (*gdk_env)->NewObject(gdk_env, lcl_class, ctor);
392 MAYBE_RETHROW(gdk_env, "cannot allocate a ThreadLocal");
394 *local = ((*gdk_env)->NewGlobalRef (gdk_env, *local));
395 MAYBE_RETHROW(gdk_env, "cannot create a GlobalRef");
397 return (GPrivate*) local;
400 /* Get this thread's value for a thread-local key. This is simply
401 * ThreadLocal.get for us.
403 static gpointer g_private_get_jni_impl (GPrivate *private) {
404 jclass lcl_class;
405 jobject lcl_obj;
406 JNIEnv *gdk_env;
407 jmethodID get_mth;
408 jclass int_class;
409 jmethodID val_mth;
410 jint int_val;
412 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
414 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
415 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
417 get_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "get", "()Ljava/lang/Object;");
418 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<get>");
420 lcl_obj = (*gdk_env)->CallObjectMethod(gdk_env, *(jobject*)private, get_mth);
421 MAYBE_RETHROW(gdk_env, "cannot find thread-local object");
423 int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
424 MAYBE_RETHROW(gdk_env, "cannot find Integer");
426 val_mth = (*gdk_env)->GetMethodID(gdk_env, int_class, "intValue", "()I");
427 MAYBE_RETHROW(gdk_env, "cannot find Integer.<intValue>");
429 int_val = (*gdk_env)->CallIntMethod(gdk_env, lcl_obj, val_mth);
430 MAYBE_RETHROW(gdk_env, "cannot get thread local value");
432 return (gpointer) int_val;
435 /* Set this thread's value for a thread-local key. This is simply
436 * ThreadLocal.set for us.
438 static void g_private_set_jni_impl (GPrivate *private, gpointer data) {
439 jclass lcl_class, int_class;
440 jobject lcl_obj;
441 JNIEnv *gdk_env;
442 jmethodID new_int, set_mth;
444 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
446 int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
447 MAYBE_RETHROW(gdk_env, "cannot find Integer");
449 new_int = (*gdk_env)->GetMethodID(gdk_env, int_class, "<init>", "(I)V");
450 MAYBE_RETHROW(gdk_env, "cannot find Integer.<init>");
452 lcl_obj = (*gdk_env)->NewObject(gdk_env, int_class, new_int, (jint)data);
453 MAYBE_RETHROW(gdk_env, "cannot create an Integer");
455 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
456 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
458 set_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "set", "(Ljava/lang/Object;)V");
459 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<set>");
461 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)private, set_mth, lcl_obj);
462 MAYBE_RETHROW(gdk_env, "cannot set thread local value");
466 /************************************************************************/
467 /* GLIB interface */
468 /************************************************************************/
470 /* set of function pointers to give to glib. */
471 GThreadFunctions g_thread_jni_functions =
473 g_mutex_new_jni_impl, /* mutex_new */
474 g_mutex_lock_jni_impl, /* mutex_lock */
475 g_mutex_trylock_jni_impl, /* mutex_try_lock */
476 g_mutex_unlock_jni_impl, /* mutex_unlock */
477 g_mutex_free_jni_impl, /* mutex_free */
478 g_cond_new_jni_impl, /* cond_new */
479 g_cond_signal_jni_impl, /* cond_signal */
480 g_cond_broadcast_jni_impl, /* cond_broadcast */
481 g_cond_wait_jni_impl, /* cond_wait */
482 g_cond_timed_wait_jni_impl, /* cond_timed_wait */
483 g_cond_free_jni_impl, /* cond_free */
484 g_private_new_jni_impl, /* private_new */
485 g_private_get_jni_impl, /* private_get */
486 g_private_set_jni_impl, /* private_set */
487 NULL,
488 NULL,
489 NULL,
490 NULL,
491 NULL,
492 NULL,
493 NULL
496 /* ??? */
497 void gdk_threads_wake () {