1 /* gthread-jni.c -- JNI threading routines for GLIB
2 Copyright (C) 1998 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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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 * Julian Dolby (dolby@us.ibm.com)
44 * February 7, 2003
45 *
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.
51 *
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.
55 *
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.
60 *
61 * NOTES:
62 *
63 * I have tested it only on JikesRVM---the CVS head as of early February
64 * 2003.
65 *
66 * Currently, use of this code is governed by the configuration option
67 * --enable-portable-native-sync
68 *
69 */
70
71
72 /************************************************************************/
73 /* Global data */
74 /************************************************************************/
75
76 #include "gthread-jni.h"
77
78 /* The VM handle. This is set in GtkToolkitMain.gtkInit */
79 JavaVM *gdk_vm;
80
81
82 /************************************************************************/
83 /* Utilities to reflect exceptions back to the VM */
84 /************************************************************************/
85
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.
90 */
maybe_rethrow(JNIEnv * gdk_env,char * message,char * file,int line)91 static void maybe_rethrow(JNIEnv *gdk_env, char *message, char *file, int line) {
92 jthrowable cause;
93
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;
100
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);
107
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);
112
113 /* throw it */
114 (*gdk_env)->Throw(gdk_env, (jthrowable)obj);
115 }
116 }
117
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__)
121
122
123 /************************************************************************/
124 /* Utilities to allocate and free java.lang.Objects */
125 /************************************************************************/
126
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?).
130 */
allocatePlainObject()131 static jobject *allocatePlainObject() {
132 jclass obj_class;
133 jobject *obj;
134 JNIEnv *gdk_env;
135 jmethodID ctor;
136
137 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
138
139 obj_class = (*gdk_env)->FindClass (gdk_env, "java/lang/Object");
140 MAYBE_RETHROW(gdk_env, "cannot find Object");
141
142 ctor = (*gdk_env)->GetMethodID(gdk_env, obj_class, "<init>", "()V");
143 MAYBE_RETHROW(gdk_env, "cannot find constructor");
144
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");
148
149 *obj = (*gdk_env)->NewGlobalRef (gdk_env, *obj);
150 MAYBE_RETHROW(gdk_env, "cannot make global ref");
151
152 return obj;
153 }
154
155 /* Frees a Java object given a global ref (isn't C fun?) */
freePlainObject(jobject * obj)156 static void freePlainObject(jobject *obj) {
157 JNIEnv *gdk_env;
158
159 if (obj) {
160 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
161
162 (*gdk_env)->DeleteGlobalRef (gdk_env, *obj);
163 MAYBE_RETHROW(gdk_env, "cannot delete global ref");
164
165 g_free (obj);
166 }
167 }
168
169
170 /************************************************************************/
171 /* Locking code */
172 /************************************************************************/
173
174 /* Lock a Java object */
takeLock(JNIEnv * gdk_env,void * mutex)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");
178 }
179
180 /* Unlock a Java object */
releaseLock(JNIEnv * gdk_env,void * mutex)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");
184 }
185
186 /* Create a mutex, which is a java.lang.Object for us */
g_mutex_new_jni_impl(void)187 static GMutex *g_mutex_new_jni_impl (void) {
188 return (GMutex*) allocatePlainObject();
189 }
190
191 /* Lock a mutex. */
g_mutex_lock_jni_impl(GMutex * mutex)192 static void g_mutex_lock_jni_impl (GMutex *mutex __attribute__((unused))) {
193 JNIEnv *gdk_env;
194
195 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
196
197 takeLock(gdk_env, mutex);
198 }
199
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.
203 */
g_mutex_trylock_jni_impl(GMutex * mutex)204 static gboolean g_mutex_trylock_jni_impl
205 (GMutex *mutex __attribute__((unused)))
206 {
207 // Shall we implement this in a JikesRVM-specific way under a flag?
208 return FALSE;
209 }
210
211 /* Unlock a mutex. */
g_mutex_unlock_jni_impl(GMutex * mutex)212 static void g_mutex_unlock_jni_impl (GMutex *mutex) {
213 JNIEnv *gdk_env;
214
215 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
216
217 releaseLock(gdk_env, mutex);
218 }
219
220 /* Free a mutex (isn't C fun?) */
g_mutex_free_jni_impl(GMutex * mutex)221 static void g_mutex_free_jni_impl (GMutex *mutex)
222 {
223 freePlainObject( (jobject*)mutex );
224 }
225
226
227 /************************************************************************/
228 /* Condition variable code */
229 /************************************************************************/
230
231 /* Create a new condition variable. This is a java.lang.Object for us. */
g_cond_new_jni_impl()232 static GCond *g_cond_new_jni_impl () {
233 return (GCond*)allocatePlainObject();
234 }
235
236 /* Signal on a condition variable. This is simply calling Object.notify
237 * for us.
238 */
g_cond_signal_jni_impl(GCond * cond)239 static void g_cond_signal_jni_impl (GCond *cond) {
240 jclass lcl_class;
241 jmethodID signal_mth;
242 JNIEnv *gdk_env;
243
244 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
245
246 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
247 MAYBE_RETHROW(gdk_env, "cannot find Object");
248
249 signal_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notify", "()V");
250 MAYBE_RETHROW(gdk_env, "cannot find Object.<notify>");
251
252 /* Must have locked an object to call notify */
253 takeLock(gdk_env, cond);
254
255 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, signal_mth);
256 MAYBE_RETHROW(gdk_env, "cannot signal mutex");
257
258 releaseLock(gdk_env, cond);
259 }
260
261 /* Broadcast to all waiting on a condition variable. This is simply
262 * calling Object.notifyAll for us.
263 */
g_cond_broadcast_jni_impl(GCond * cond)264 static void g_cond_broadcast_jni_impl (GCond *cond) {
265 jclass lcl_class;
266 jmethodID bcast_mth;
267 JNIEnv *gdk_env;
268
269 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
270
271 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
272 MAYBE_RETHROW(gdk_env, "cannot find Object");
273
274 bcast_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notifyAll", "()V");
275 MAYBE_RETHROW(gdk_env, "cannot find Object.<notifyAll>");
276
277 /* Must have locked an object to call notifyAll */
278 takeLock(gdk_env, cond);
279
280 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, bcast_mth);
281 MAYBE_RETHROW(gdk_env, "cannot broadcast to mutex");
282
283 releaseLock(gdk_env, cond);
284 }
285
286
287 /* Wait on a condition variable. For us, this simply means call
288 * Object.wait.
289 */
g_cond_wait_jni_impl(GCond * cond,GMutex * mutex)290 static void g_cond_wait_jni_impl
291 (GCond *cond, GMutex *mutex __attribute__((unused)))
292 {
293 jclass lcl_class;
294 jmethodID wait_mth;
295 JNIEnv *gdk_env;
296
297 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
298
299 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
300 MAYBE_RETHROW(gdk_env, "cannot find Object");
301
302 wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "()V");
303 MAYBE_RETHROW(gdk_env, "cannot find Object.<wait>");
304
305 /* Must have locked an object to call wait */
306 takeLock(gdk_env, cond);
307
308 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth);
309 MAYBE_RETHROW(gdk_env, "cannot wait on mutex");
310
311 releaseLock(gdk_env, cond);
312 }
313
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.
318 */
319 static gboolean
g_cond_timed_wait_jni_impl(GCond * cond,GMutex * mutex,GTimeVal * end_time)320 g_cond_timed_wait_jni_impl
321 (GCond *cond, GMutex *mutex __attribute__((unused)),
322 GTimeVal *end_time)
323 {
324 jclass lcl_class;
325 jmethodID wait_mth;
326 JNIEnv *gdk_env;
327 jlong time;
328 jthrowable cause;
329
330 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
331
332 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
333 MAYBE_RETHROW(gdk_env, "cannot find Object");
334
335 wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "(J)V");
336 MAYBE_RETHROW(gdk_env, "cannot find Object.<wait(J)>");
337
338 time = end_time->tv_sec*1000;
339 time += end_time->tv_usec/1000;
340
341 /* Must have locked an object to call wait */
342 takeLock(gdk_env, cond);
343
344 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth, time);
345
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");
353 }
354 }
355
356 releaseLock(gdk_env, cond);
357
358 return TRUE;
359 }
360
361 /* Free a condition variable. (isn't C fun?) */
g_cond_free_jni_impl(GCond * cond)362 static void g_cond_free_jni_impl (GCond *cond) {
363 freePlainObject( (jobject*)cond );
364 }
365
366
367 /************************************************************************/
368 /* Thread-local data code */
369 /************************************************************************/
370
371 /* Create a new thread-local key. We use java.lang.ThreadLocal objects
372 * for this.
373 */
g_private_new_jni_impl(GDestroyNotify notify)374 static GPrivate *g_private_new_jni_impl
375 (GDestroyNotify notify __attribute__((unused)))
376 {
377 jclass lcl_class;
378 jobject *local;
379 JNIEnv *gdk_env;
380 jmethodID ctor;
381
382 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
383
384 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
385 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
386
387 ctor = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "<init>", "()V");
388 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<init>");
389
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");
393
394 *local = ((*gdk_env)->NewGlobalRef (gdk_env, *local));
395 MAYBE_RETHROW(gdk_env, "cannot create a GlobalRef");
396
397 return (GPrivate*) local;
398 }
399
400 /* Get this thread's value for a thread-local key. This is simply
401 * ThreadLocal.get for us.
402 */
g_private_get_jni_impl(GPrivate * private)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;
411
412 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
413
414 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
415 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
416
417 get_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "get", "()Ljava/lang/Object;");
418 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<get>");
419
420 lcl_obj = (*gdk_env)->CallObjectMethod(gdk_env, *(jobject*)private, get_mth);
421 MAYBE_RETHROW(gdk_env, "cannot find thread-local object");
422
423 int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
424 MAYBE_RETHROW(gdk_env, "cannot find Integer");
425
426 val_mth = (*gdk_env)->GetMethodID(gdk_env, int_class, "intValue", "()I");
427 MAYBE_RETHROW(gdk_env, "cannot find Integer.<intValue>");
428
429 int_val = (*gdk_env)->CallIntMethod(gdk_env, lcl_obj, val_mth);
430 MAYBE_RETHROW(gdk_env, "cannot get thread local value");
431
432 return (gpointer) int_val;
433 }
434
435 /* Set this thread's value for a thread-local key. This is simply
436 * ThreadLocal.set for us.
437 */
g_private_set_jni_impl(GPrivate * private,gpointer data)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;
443
444 (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
445
446 int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
447 MAYBE_RETHROW(gdk_env, "cannot find Integer");
448
449 new_int = (*gdk_env)->GetMethodID(gdk_env, int_class, "<init>", "(I)V");
450 MAYBE_RETHROW(gdk_env, "cannot find Integer.<init>");
451
452 lcl_obj = (*gdk_env)->NewObject(gdk_env, int_class, new_int, (jint)data);
453 MAYBE_RETHROW(gdk_env, "cannot create an Integer");
454
455 lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
456 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
457
458 set_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "set", "(Ljava/lang/Object;)V");
459 MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<set>");
460
461 (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)private, set_mth, lcl_obj);
462 MAYBE_RETHROW(gdk_env, "cannot set thread local value");
463 }
464
465
466 /************************************************************************/
467 /* GLIB interface */
468 /************************************************************************/
469
470 /* set of function pointers to give to glib. */
471 GThreadFunctions g_thread_jni_functions =
472 {
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
494 };
495
496 /* ??? */
gdk_threads_wake()497 void gdk_threads_wake () {
498
499 }
500