This patch makes the GDK lock reentrant. Basically it replaces the
portableNativeSync functionality by a simple new implementation. As of
GTK 2.4 (according to the docs), there is a function
gdk_threads_set_lock_functions() which allows to define a pair of
functions for locking and unlocking. I implemented it so that it uses
JNI MonitorEnter() and MonitorExit() on a global object in GtkToolkit.
It can be activated by setting the system property
gnu.classpath.awt.gtk.portable.native.sync to true. It completely
obsoletes a whole bunch of code, I'll remove that later.

This has a couple of advantages:
- The gdk_threads_enter() and gdk_threads_leave() are reentrant with
this enabled, by making use of the VM's locking impl.
- It is possible to easily lock on the Java side by using
synchronized(GtkToolkit.GTK_LOCK) now. ComponentGraphics says hi! Should
boost performance even.
- It should allow to cleanup a lot of workarounds.

For now this is not enabled by default. I propose to thouroughly test
this and make it default when things turn out to work. I tested the
Swing, AWT and Java2D demo as well as some JOGL pieces (which originally
caused problems with the JAWT Lock/Unlock functions) and those seem to
work well with this. Maybe turn on default after the release and weed
out issues in the following cycle.

Cheers, Roman

-- 
aicas Allerton Interworks Computer Automated Systems GmbH
Haid-und-Neu-Straße 18 * D-76131 Karlsruhe * Germany
http://www.aicas.com   * Tel: +49-721-663 968-0

USt-Id: DE216375633, Handelsregister HRB 109481, AG Karlsruhe
Geschäftsführer: Dr. James J. Hunt
Index: include/gnu_java_awt_peer_gtk_GtkToolkit.h
===================================================================
RCS file: /cvsroot/classpath/classpath/include/gnu_java_awt_peer_gtk_GtkToolkit.h,v
retrieving revision 1.15
diff -u -1 -5 -r1.15 gnu_java_awt_peer_gtk_GtkToolkit.h
--- include/gnu_java_awt_peer_gtk_GtkToolkit.h	3 Jan 2007 22:51:07 -0000	1.15
+++ include/gnu_java_awt_peer_gtk_GtkToolkit.h	3 Apr 2007 13:38:54 -0000
@@ -1,28 +1,28 @@
 /* DO NOT EDIT THIS FILE - it is machine generated */
 
 #ifndef __gnu_java_awt_peer_gtk_GtkToolkit__
 #define __gnu_java_awt_peer_gtk_GtkToolkit__
 
 #include <jni.h>
 
 #ifdef __cplusplus
 extern "C"
 {
 #endif
 
-JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkInit (JNIEnv *env, jclass, jint);
+JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkInit (JNIEnv *env, jclass, jint, jobject);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkMain (JNIEnv *env, jclass);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkQuit (JNIEnv *env, jclass);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_beep (JNIEnv *env, jobject);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_getScreenSizeDimensions (JNIEnv *env, jobject, jintArray);
 JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_getScreenResolution (JNIEnv *env, jobject);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_sync (JNIEnv *env, jobject);
 JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_loadSystemColors (JNIEnv *env, jobject, jintArray);
 JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_getLockState (JNIEnv *env, jobject, jint);
 JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_getMouseNumberOfButtons (JNIEnv *env, jobject);
 
 #ifdef __cplusplus
 }
 #endif
 
 #endif /* __gnu_java_awt_peer_gtk_GtkToolkit__ */
Index: native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c
===================================================================
RCS file: /cvsroot/classpath/classpath/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c,v
retrieving revision 1.33
diff -u -1 -5 -r1.33 gnu_java_awt_peer_gtk_GtkToolkit.c
--- native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c	29 Mar 2007 20:24:51 -0000	1.33
+++ native/jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c	3 Apr 2007 13:38:54 -0000
@@ -1,15 +1,16 @@
+
 /* gtktoolkit.c -- Native portion of GtkToolkit
    Copyright (C) 1998, 1999, 2005, 2007  Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
 GNU Classpath is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.
 
 GNU Classpath is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 General Public License for more details.
 
@@ -76,104 +77,112 @@
 #define VK_SHIFT 16
 #define VK_CONTROL 17
 #define VK_ALT 18
 #define VK_CAPS_LOCK 20
 #define VK_META 157
 
 struct state_table *cp_gtk_native_state_table;
 struct state_table *cp_gtk_native_global_ref_table;
 
 static jclass gtkgenericpeer;
 static jclass gtktoolkit;
 static JavaVM *java_vm;
 static jmethodID printCurrentThreadID;
 static jmethodID setRunningID;
 
+/**
+ * The global AWT lock object.
+ */
+static jobject global_lock;
+
 union env_union
 {
   void *void_env;
   JNIEnv *jni_env;
 };
 
 JNIEnv *
 cp_gtk_gdk_env()
 {
   union env_union tmp;
   g_assert((*java_vm)->GetEnv(java_vm, &tmp.void_env, JNI_VERSION_1_2) == JNI_OK);
   return tmp.jni_env;
 }
 
 
 GtkWindowGroup *cp_gtk_global_window_group;
 double cp_gtk_dpi_conversion_factor;
 
-static void init_glib_threads(JNIEnv *, jint);
+static void jni_lock_cb();
+static void jni_unlock_cb();
+static void init_glib_threads(JNIEnv *, jint, jobject);
 static gboolean post_set_running_flag (gpointer);
 static gboolean set_running_flag (gpointer);
 static gboolean clear_running_flag (gpointer);
 static void init_dpi_conversion_factor (void);
 static void dpi_changed_cb (GtkSettings  *settings,
                             GParamSpec   *pspec);
 
 #if GTK_MINOR_VERSION > 4
 static GLogFunc old_glog_func;
 static void glog_func (const gchar *log_domain,
 		       GLogLevelFlags log_level,
 		       const gchar *message,
 		       gpointer user_data);
 #endif
 
 /*
  * Call gtk_init.  It is very important that this happen before any other
  * gtk calls.
  *
  * The portableNativeSync argument may have the values:
  *   1 if the Java property gnu.classpath.awt.gtk.portable.native.sync
  *     is set to "true".  
  *   0 if it is set to "false"
  *  -1 if unset.
  */
 
 
 JNIEXPORT void JNICALL 
 Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkInit (JNIEnv *env, 
 					       jclass clazz __attribute__((unused)),
-					       jint portableNativeSync)
+					       jint portableNativeSync,
+                                               jobject lock)
 {
   int argc = 1;
   char **argv;
   char *homedir, *rcpath = NULL;
 
   gtkgenericpeer = (*env)->FindClass(env, "gnu/java/awt/peer/gtk/GtkGenericPeer");
 
   gtkgenericpeer = (*env)->NewGlobalRef(env, gtkgenericpeer);
 
   printCurrentThreadID = (*env)->GetStaticMethodID (env, gtkgenericpeer,
                                                     "printCurrentThread", "()V");
  
   NSA_INIT (env, gtkgenericpeer);
 
   g_assert((*env)->GetJavaVM(env, &java_vm) == 0);
 
   /* GTK requires a program's argc and argv variables, and requires that they
      be valid.  Set it up. */
   argv = (char **) g_malloc (sizeof (char *) * 2);
   argv[0] = (char *) g_malloc(1);
   argv[0][0] = '\0';
   argv[1] = NULL;
   
-  init_glib_threads(env, portableNativeSync);
+  init_glib_threads(env, portableNativeSync, lock);
 
   /* From GDK 2.0 onwards we have to explicitly call gdk_threads_init */
   gdk_threads_init();
 
   gtk_init (&argc, &argv);
 
 #if SYNCHRONIZE_GDK
   XSynchronize (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), True);
 #endif
 
   gtk_widget_set_default_colormap (gdk_rgb_get_colormap ());
 
   /* Make sure queued calls don't get sent to GTK/GDK while 
      we're shutting down. */
   atexit (gdk_threads_enter);
@@ -203,56 +212,84 @@
   cp_gtk_list_init_jni ();
   cp_gtk_menuitem_init_jni ();
   cp_gtk_scrollbar_init_jni ();
   cp_gtk_textcomponent_init_jni ();
   cp_gtk_window_init_jni ();
 
   cp_gtk_global_window_group = gtk_window_group_new ();
 
   init_dpi_conversion_factor ();
 
   gtktoolkit = (*env)->FindClass(env, "gnu/java/awt/peer/gtk/GtkMainThread");
   setRunningID = (*env)->GetStaticMethodID (env, gtktoolkit,
                                             "setRunning", "(Z)V");
 }
 
+/**
+ * A callback function that implements gdk_threads_enter(). This is
+ * implemented to wrap the JNI MonitorEnter() function.
+ */
+static void jni_lock_cb()
+{
+  JNIEnv * env = cp_gtk_gdk_env();
+  if ((*env)->MonitorEnter(env, global_lock) != JNI_OK)
+    {
+      printf("failure while entering GTK monitor\n");
+    }
+}
+
+/**
+ * A callback function that implements gdk_threads_leave(). This is
+ * implemented to wrap the JNI MonitorExit() function.
+ */
+static void jni_unlock_cb()
+{
+
+  JNIEnv * env = cp_gtk_gdk_env();
+  if ((*env)->MonitorExit(env, global_lock))
+    {
+      printf("failure while exiting GTK monitor\n");
+    }
+}
 
 /** Initialize GLIB's threads properly, based on the value of the
     gnu.classpath.awt.gtk.portable.native.sync Java system property.  If
     that's unset, use the PORTABLE_NATIVE_SYNC config.h macro.  (TODO: 
     In some release following 0.10, that config.h macro will go away.)
     */ 
 static void 
-init_glib_threads(JNIEnv *env, jint portableNativeSync)
+init_glib_threads(JNIEnv *env, jint portableNativeSync, jobject lock)
 {
   if (portableNativeSync < 0)
     {
 #ifdef PORTABLE_NATIVE_SYNC /* Default value, if not set by the Java system
                                property */ 
       portableNativeSync = 1;
 #else
       portableNativeSync = 0;
 #endif
     }
   
   (*env)->GetJavaVM( env, &cp_gtk_the_vm );
   if (!g_thread_supported ())
     {
       if (portableNativeSync)
-        g_thread_init ( &cp_gtk_portable_native_sync_jni_functions );
-      else
-        g_thread_init ( NULL );
+        {
+          global_lock = lock;
+          gdk_threads_set_lock_functions(&jni_lock_cb, &jni_unlock_cb);
+        }
+      g_thread_init(NULL);
     }
   else
     {
       /* Warn if portable native sync is desired but the threading
          system is already initialized.  In that case we can't
          override the threading implementation with our portable
          native sync functions. */
       if (portableNativeSync)
         g_printerr ("peer warning: portable native sync disabled.\n");
     }
 
   /* Debugging progress message; uncomment if needed: */
   /*   printf("called gthread init\n"); */
 }
 
@@ -324,38 +361,38 @@
       JCL_ThrowException (env, "java/lang/InternalError", detail);
       g_free (detail);
       (*env)->ExceptionDescribe (env);
       if (exc != NULL)
 	(*env)->Throw (env, exc);
       else
 	(*env)->ExceptionClear (env);
     }
 }
 #endif
 
 JNIEXPORT void JNICALL
 Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkMain
 (JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)))
 {
-  gdk_threads_enter ();
+  gdk_threads_enter();
 
   gtk_init_add (post_set_running_flag, NULL);
   gtk_quit_add (gtk_main_level (), clear_running_flag, NULL);
 
   gtk_main ();
 
-  gdk_threads_leave ();
+  gdk_threads_leave();
 }
 
 JNIEXPORT void JNICALL
 Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkQuit
 (JNIEnv *env __attribute__((unused)), jobject obj __attribute__((unused)))
 {
   gdk_threads_enter ();
 
   gtk_main_quit ();
 
   gdk_threads_leave ();
 }
 
 
 static jint gdk_color_to_java_color (GdkColor color);
@@ -579,15 +616,16 @@
 {
   (*cp_gtk_gdk_env ())->CallStaticVoidMethod (cp_gtk_gdk_env (),
                                               gtktoolkit,
                                               setRunningID, TRUE);
   return FALSE;
 }
 
 static gboolean
 clear_running_flag (gpointer data __attribute__((unused)))
 {
   (*cp_gtk_gdk_env ())->CallStaticVoidMethod (cp_gtk_gdk_env (),
                                               gtktoolkit,
                                               setRunningID, FALSE);
   return FALSE;
 }
+
Index: gnu/java/awt/peer/gtk/GtkToolkit.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/peer/gtk/GtkToolkit.java,v
retrieving revision 1.100
diff -u -1 -5 -r1.100 GtkToolkit.java
--- gnu/java/awt/peer/gtk/GtkToolkit.java	12 Feb 2007 21:39:20 -0000	1.100
+++ gnu/java/awt/peer/gtk/GtkToolkit.java	3 Apr 2007 13:38:54 -0000
@@ -122,54 +122,57 @@
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Properties;
 
 import javax.imageio.spi.IIORegistry;
 
 /* This class uses a deprecated method java.awt.peer.ComponentPeer.getPeer().
    This merits comment.  We are basically calling Sun's bluff on this one.
    We think Sun has deprecated it simply to discourage its use as it is
    bad programming style.  However, we need to get at a component's peer in
    this class.  If getPeer() ever goes away, we can implement a hash table
    that will keep up with every window's peer, but for now this is faster. */
 
 public class GtkToolkit extends gnu.java.awt.ClasspathToolkit
 {
+  static final Object GTK_LOCK;
+
   private static EventQueue q;
 
-  static native void gtkInit(int portableNativeSync);
+  static native void gtkInit(int portableNativeSync, Object lock);
 
   static native void gtkMain();
 
   static native void gtkQuit();
 
   static
   {
     System.loadLibrary("gtkpeer");
       
     int portableNativeSync;     
     String portNatSyncProp = 
       System.getProperty("gnu.classpath.awt.gtk.portable.native.sync");
       
     if (portNatSyncProp == null)
       portableNativeSync = -1;  // unset
     else if (Boolean.valueOf(portNatSyncProp).booleanValue())
       portableNativeSync = 1;   // true
     else
       portableNativeSync = 0;   // false
-      
-    gtkInit(portableNativeSync);
+
+    GTK_LOCK = new String("GTK LOCK");
+    gtkInit(portableNativeSync, GTK_LOCK);
   }
 
   public GtkToolkit ()
   {
   }
 
   public native void beep();
 
   private native void getScreenSizeDimensions(int[] xy);
   
   public int checkImage (Image image, int width, int height, 
                          ImageObserver observer) 
   {
     int status = ImageObserver.ALLBITS 
       | ImageObserver.WIDTH 

Reply via email to