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