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