/[classpath]/classpath/native/jni/gtk-peer/gthread-jni.c
ViewVC logotype

Diff of /classpath/native/jni/gtk-peer/gthread-jni.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.3 by mark, Tue Jan 22 22:27:02 2002 UTC revision 1.4 by cbj, Sat Feb 15 15:08:08 2003 UTC
# Line 35  this exception to your version of the li Line 35  this exception to your version of the li
35  obligated to do so.  If you do not wish to do so, delete this  obligated to do so.  If you do not wish to do so, delete this
36  exception statement from your version. */  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"  #include "gthread-jni.h"
77    
78  /*  /*  The VM handle.  This is set in GtkToolkitMain.gtkInit */
79   * This code has been written specifically to be used with GTK+ 1.2.  JavaVM *gdk_vm;
80   * `Real' GLIB threading is not supported.  We fake things where necessary.  
81   * Once we know we're running on a 1.2 VM, we can write a real implementation.  
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   */   */
91    static void maybe_rethrow(JNIEnv *gdk_env, char *message, char *file, int line) {
92      jthrowable cause;
93    
94  static GMutex *    /* rethrow if an exception happened */
95  g_mutex_new_jni_impl (void)    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     */
131    static jobject *allocatePlainObject() {
132    jclass obj_class;    jclass obj_class;
133    jobject *mutex;    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");    obj_class = (*gdk_env)->FindClass (gdk_env, "java/lang/Object");
140    if (obj_class == NULL)    MAYBE_RETHROW(gdk_env, "cannot find Object");
     return NULL;  
141    
142    mutex = (jobject *) g_malloc (sizeof (jobject));    ctor = (*gdk_env)->GetMethodID(gdk_env, obj_class, "<init>", "()V");
143    *mutex = (*gdk_env)->AllocObject (gdk_env, obj_class);    MAYBE_RETHROW(gdk_env, "cannot find constructor");
144    if (*mutex == NULL)  
145      {    obj = (jobject *) g_malloc (sizeof (jobject));
146        g_free (mutex);    *obj = (*gdk_env)->NewObject (gdk_env, obj_class, ctor);
147        return NULL;    MAYBE_RETHROW(gdk_env, "cannot allocate object");
148      }    
149    *mutex = (*gdk_env)->NewGlobalRef (gdk_env, *mutex);    *obj = (*gdk_env)->NewGlobalRef (gdk_env, *obj);
150      MAYBE_RETHROW(gdk_env, "cannot make global ref");
151    
152    return (GMutex *) mutex;    return obj;
153  }  }
154    
155  static void  /*  Frees a Java object given a global ref (isn't C fun?) */
156  g_mutex_lock_jni_impl (GMutex *mutex)  static void freePlainObject(jobject *obj) {
157  {    JNIEnv *gdk_env;
158    if (mutex && mutex == gdk_threads_mutex)  
159      (*gdk_env)->MonitorEnter (gdk_env, *((jobject *)mutex));    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  static gboolean  
170  g_mutex_trylock_jni_impl (GMutex *mutex)  /************************************************************************/
171  {  /* Locking code                                                         */
172    /************************************************************************/
173    
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");
178    }
179    
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");
184    }
185    
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();
189    }
190    
191    /* Lock a mutex. */
192    static void g_mutex_lock_jni_impl (GMutex *mutex) {
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     */
204    static gboolean g_mutex_trylock_jni_impl (GMutex *mutex) {
205      // Shall we implement this in a JikesRVM-specific way under a flag?
206    return FALSE;    return FALSE;
207  }  }
208    
209  static void  /* Unlock a mutex. */
210  g_mutex_unlock_jni_impl (GMutex *mutex)  static void g_mutex_unlock_jni_impl (GMutex *mutex) {
211  {    JNIEnv *gdk_env;
212    if (mutex && mutex == gdk_threads_mutex)  
213      (*gdk_env)->MonitorExit (gdk_env, *((jobject *)mutex));    (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
214    
215      releaseLock(gdk_env, mutex);
216  }  }
217    
218  static void  /* Free a mutex (isn't C fun?) */
219  g_mutex_free_jni_impl (GMutex *mutex)  static void g_mutex_free_jni_impl (GMutex *mutex)
220  {  {
221    if (mutex && mutex == gdk_threads_mutex)    freePlainObject( (jobject*)mutex );
     {  
       (*gdk_env)->DeleteGlobalRef (gdk_env, *((jobject *)mutex));  
       g_free (mutex);  
     }  
222  }  }
223    
224  static GPrivate *  
225  g_private_new_jni_impl (GDestroyNotify notify)  /************************************************************************/
226  {  /* Condition variable code                                              */
227    return NULL;  /************************************************************************/
228    
229    /* Create a new condition variable.  This is a java.lang.Object for us. */
230    static GCond *g_cond_new_jni_impl () {
231      return (GCond*)allocatePlainObject();
232  }  }
233    
234  static gpointer  /*  Signal on a condition variable.  This is simply calling Object.notify
235  g_private_get_jni_impl (GPrivate *private)   * for us.
236  {   */
237    return NULL;  static void g_cond_signal_jni_impl (GCond *cond) {
238      jclass lcl_class;
239      jmethodID signal_mth;
240      JNIEnv *gdk_env;
241    
242      (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
243    
244      lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
245      MAYBE_RETHROW(gdk_env, "cannot find Object");
246      
247      signal_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notify", "()V");
248      MAYBE_RETHROW(gdk_env, "cannot find Object.<notify>");
249    
250      /* Must have locked an object to call notify */
251      takeLock(gdk_env, cond);
252    
253      (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, signal_mth);
254      MAYBE_RETHROW(gdk_env, "cannot signal mutex");
255    
256      releaseLock(gdk_env, cond);
257  }  }
258    
259  static void  /*  Broadcast to all waiting on a condition variable.  This is simply
260  g_private_set_jni_impl (GPrivate *private, gpointer data)   * calling Object.notifyAll for us.
261  {   */
262    static void g_cond_broadcast_jni_impl (GCond *cond) {
263      jclass lcl_class;
264      jmethodID bcast_mth;
265      JNIEnv *gdk_env;
266    
267      (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
268    
269      lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
270      MAYBE_RETHROW(gdk_env, "cannot find Object");
271      
272      bcast_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "notifyAll", "()V");
273      MAYBE_RETHROW(gdk_env, "cannot find Object.<notifyAll>");
274    
275      /* Must have locked an object to call notifyAll */
276      takeLock(gdk_env, cond);
277    
278      (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, bcast_mth);
279      MAYBE_RETHROW(gdk_env, "cannot broadcast to mutex");
280    
281      releaseLock(gdk_env, cond);
282  }  }
283    
284  static GCond *  
285  g_cond_new_jni_impl ()  /*  Wait on a condition variable.  For us, this simply means call
286  {   * Object.wait.
287    return NULL;   */
288    static void g_cond_wait_jni_impl (GCond *cond, GMutex *mutex) {
289      jclass lcl_class;
290      jmethodID wait_mth;
291      JNIEnv *gdk_env;
292    
293      (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
294    
295      lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
296      MAYBE_RETHROW(gdk_env, "cannot find Object");
297      
298      wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "()V");
299      MAYBE_RETHROW(gdk_env, "cannot find Object.<wait>");
300    
301      /* Must have locked an object to call wait */
302      takeLock(gdk_env, cond);
303    
304      (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth);
305      MAYBE_RETHROW(gdk_env, "cannot wait on mutex");
306    
307      releaseLock(gdk_env, cond);
308  }  }
309    
310  static void  /*  Wait on a condition vairable until a timeout.  This is a little tricky
311  g_cond_signal_jni_impl (GCond *cond)   * for us.  We first call Object.wait(J) giving it the appropriate timeout
312  {   * value.  On return, we check whether an InterruptedException happened.  If
313     * so, that is Java-speak for wait timing out.
314     */
315    static gboolean
316    g_cond_timed_wait_jni_impl (GCond *cond, GMutex *mutex, GTimeVal *end_time) {
317      jclass lcl_class;
318      jmethodID wait_mth;
319      JNIEnv *gdk_env;
320      jlong time;
321      jthrowable cause;
322    
323      (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
324    
325      lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Object");
326      MAYBE_RETHROW(gdk_env, "cannot find Object");
327      
328      wait_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "wait", "(J)V");
329      MAYBE_RETHROW(gdk_env, "cannot find Object.<wait(J)>");
330      
331      time = end_time->tv_sec*1000;
332      time += end_time->tv_usec/1000;
333    
334      /* Must have locked an object to call wait */
335      takeLock(gdk_env, cond);
336    
337      (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)cond, wait_mth, time);
338    
339      if ((cause = (*gdk_env)->ExceptionOccurred(gdk_env)) != NULL) {
340        jclass intr = (*gdk_env)->FindClass (gdk_env, "java.lang.InterruptedException");
341        if ( (*gdk_env)->IsInstanceOf(gdk_env, cause, intr) ) {
342          releaseLock(gdk_env, cond);
343          return FALSE;
344        } else {
345          MAYBE_RETHROW(gdk_env, "error in timed wait");
346        }
347      }
348    
349      releaseLock(gdk_env, cond);
350    
351      return TRUE;
352  }  }
353    
354  static void  /* Free a condition variable.  (isn't C fun?) */
355  g_cond_broadcast_jni_impl (GCond *cond)  static void g_cond_free_jni_impl (GCond *cond) {
356  {    freePlainObject( (jobject*)cond );
357  }  }
358    
359  static void  
360  g_cond_wait_jni_impl (GCond *cond, GMutex *mutex)  /************************************************************************/
361  {  /* Thread-local data code                                               */
362    /************************************************************************/
363    
364    /*  Create a new thread-local key.  We use java.lang.ThreadLocal objects
365     * for this.
366     */
367    static GPrivate *g_private_new_jni_impl (GDestroyNotify notify) {
368      jclass lcl_class;
369      jobject *local;
370      JNIEnv *gdk_env;
371      jmethodID ctor;
372    
373      (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
374    
375      lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
376      MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
377    
378      ctor = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "<init>", "()V");
379      MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<init>");
380    
381      local = (jobject *) g_malloc (sizeof (jobject));
382      *local = (*gdk_env)->NewObject(gdk_env, lcl_class, ctor);
383      MAYBE_RETHROW(gdk_env, "cannot allocate a ThreadLocal");
384      
385      *local = ((*gdk_env)->NewGlobalRef (gdk_env, *local));
386      MAYBE_RETHROW(gdk_env, "cannot create a GlobalRef");
387    
388      return (GPrivate*) local;
389  }  }
390    
391  static gboolean  /*  Get this thread's value for a thread-local key.  This is simply
392  g_cond_timed_wait_jni_impl (GCond *cond, GMutex *mutex)   * ThreadLocal.get for us.
393  {   */
394    return FALSE;  static gpointer g_private_get_jni_impl (GPrivate *private) {
395      jclass lcl_class;
396      jobject lcl_obj;
397      JNIEnv *gdk_env;
398      jmethodID get_mth;
399      jclass int_class;
400      jmethodID val_mth;
401      jint int_val;
402    
403      (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
404    
405      lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
406      MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
407    
408      get_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "get", "()Ljava/lang/Object;");
409      MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<get>");
410    
411      lcl_obj = (*gdk_env)->CallObjectMethod(gdk_env, *(jobject*)private, get_mth);
412      MAYBE_RETHROW(gdk_env, "cannot find thread-local object");
413    
414      int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
415      MAYBE_RETHROW(gdk_env, "cannot find Integer");
416    
417      val_mth = (*gdk_env)->GetMethodID(gdk_env, int_class, "intValue", "()I");
418      MAYBE_RETHROW(gdk_env, "cannot find Integer.<intValue>");
419    
420      int_val = (*gdk_env)->CallIntMethod(gdk_env, lcl_obj, val_mth);
421      MAYBE_RETHROW(gdk_env, "cannot get thread local value");
422    
423      return (gpointer) int_val;
424  }  }
425    
426  static void  /*  Set this thread's value for a thread-local key.  This is simply
427  g_cond_free_jni_impl (GCond *cond)   * ThreadLocal.set for us.
428  {   */
429    static void g_private_set_jni_impl (GPrivate *private, gpointer data) {
430      jclass lcl_class, int_class;
431      jobject lcl_obj;
432      JNIEnv *gdk_env;
433      jmethodID new_int, set_mth;
434    
435      (*gdk_vm)->GetEnv(gdk_vm, (void **)&gdk_env, JNI_VERSION_1_1);
436    
437      int_class = (*gdk_env)->FindClass (gdk_env, "java.lang.Integer");
438      MAYBE_RETHROW(gdk_env, "cannot find Integer");
439    
440      new_int = (*gdk_env)->GetMethodID(gdk_env, int_class, "<init>", "(I)V");
441      MAYBE_RETHROW(gdk_env, "cannot find Integer.<init>");
442    
443      lcl_obj = (*gdk_env)->NewObject(gdk_env, int_class, new_int, (jint)data);
444      MAYBE_RETHROW(gdk_env, "cannot create an Integer");
445    
446      lcl_class = (*gdk_env)->FindClass (gdk_env, "java.lang.ThreadLocal");
447      MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal");
448    
449      set_mth = (*gdk_env)->GetMethodID(gdk_env, lcl_class, "set", "(Ljava/lang/Object;)V");
450      MAYBE_RETHROW(gdk_env, "cannot find ThreadLocal.<set>");
451    
452      (*gdk_env)->CallVoidMethod(gdk_env, *(jobject*)private, set_mth, lcl_obj);
453      MAYBE_RETHROW(gdk_env, "cannot set thread local value");
454  }  }
455    
456    
457    /************************************************************************/
458    /* GLIB interface                                                       */
459    /************************************************************************/
460    
461    /* set of function pointers to give to glib. */
462  GThreadFunctions g_thread_jni_functions =  GThreadFunctions g_thread_jni_functions =
463  {  {
464    g_mutex_new_jni_impl,       /* mutex_new */    g_mutex_new_jni_impl,       /* mutex_new */
# Line 163  GThreadFunctions g_thread_jni_functions Line 477  GThreadFunctions g_thread_jni_functions
477    g_private_set_jni_impl      /* private_set */    g_private_set_jni_impl      /* private_set */
478  };  };
479    
480  void  /* ??? */
481  gdk_threads_wake ()  void gdk_threads_wake () {
482  {  
483  }  }

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.4

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26