--- old/src/macosx/native/sun/awt/awt.m 2012-12-28 16:57:30.000000000 +0400 +++ new/src/macosx/native/sun/awt/awt.m 2012-12-28 16:57:30.000000000 +0400 @@ -42,6 +42,9 @@ // The symbol is defined in libosxapp.dylib (ThreadUtilities.m) extern JavaVM *jvm; +// Indicates if AWT is running embedded (in SWT, FX, elsewhere) +static BOOL isEmbedded = NO; + static bool ShouldPrintVerboseDebugging() { static int debug = -1; if (debug == -1) { @@ -63,13 +66,8 @@ static CFRunLoopObserverRef busyObserver = NULL; static CFRunLoopObserverRef notBusyObserver = NULL; -static void setUpAWTAppKit(BOOL swt_mode, BOOL headless) { -AWT_ASSERT_APPKIT_THREAD; - BOOL verbose = ShouldPrintVerboseDebugging(); - if (verbose) AWT_DEBUG_LOG(@"setting up busy observers"); - - JNIEnv *env = [ThreadUtilities getJNIEnv]; - +static void setUpAWTAppKit(JNIEnv *env) +{ // Add CFRunLoopObservers to call into AWT so that AWT knows that the // AWT thread (which is the AppKit main thread) is alive. This way AWT // will not automatically shutdown. @@ -96,28 +94,27 @@ CFRelease(busyObserver); CFRelease(notBusyObserver); - if (!headless) setBusy(YES); + setBusy(YES); + // Set the java name of the AppKit main thread appropriately. jclass threadClass = NULL; jstring name = NULL; jobject curThread = NULL; - if (!swt_mode) { - threadClass = (*env)->FindClass(env, "java/lang/Thread"); - if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;"); - if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V"); - if (setName == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - - curThread = (*env)->CallStaticObjectMethod(env, threadClass, currentThreadID); // AWT_THREADING Safe (known object) - if (curThread == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - name = (*env)->NewStringUTF(env, "AWT-AppKit"); - if (name == NULL || (*env)->ExceptionCheck(env)) goto cleanup; - (*env)->CallVoidMethod(env, curThread, setName, name); // AWT_THREADING Safe (known object) - if ((*env)->ExceptionCheck(env)) goto cleanup; - } + threadClass = (*env)->FindClass(env, "java/lang/Thread"); + if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;"); + if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V"); + if (setName == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + + curThread = (*env)->CallStaticObjectMethod(env, threadClass, currentThreadID); // AWT_THREADING Safe (known object) + if (curThread == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + name = (*env)->NewStringUTF(env, "AWT-AppKit"); + if (name == NULL || (*env)->ExceptionCheck(env)) goto cleanup; + (*env)->CallVoidMethod(env, curThread, setName, name); // AWT_THREADING Safe (known object) + if ((*env)->ExceptionCheck(env)) goto cleanup; cleanup: if (threadClass != NULL) { @@ -133,15 +130,9 @@ (*env)->ExceptionDescribe(env); (*env)->ExceptionClear(env); } - - // Add the exception handler of last resort - NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler); - - if (verbose) AWT_DEBUG_LOG(@"finished setting thread name"); } - // Returns true if java believes it is running headless BOOL isHeadless(JNIEnv *env) { // Just access the property directly, instead of using GraphicsEnvironment.isHeadless. @@ -152,11 +143,6 @@ return JNFCallStaticBooleanMethod(env, jm_isHeadless); } -BOOL isSWTInWebStart(JNIEnv* env) { - NSString *swtWebStart = [PropertiesUtilities javaSystemPropertyForKey:@"com.apple.javaws.usingSWT" withEnv:env]; - return [@"true" isCaseInsensitiveLike:swtWebStart]; -} - void setBusy(BOOL busy) { AWT_ASSERT_APPKIT_THREAD; @@ -200,7 +186,7 @@ // This is an empty Obj-C object just so that -peformSelectorOnMainThread can be used. @interface AWTStarter : NSObject { } -+ (void)start:(BOOL)headless swtMode:(BOOL)swtMode swtModeForWebStart:(BOOL)swtModeForWebStart; ++ (void)start:(BOOL)headless; - (void)starter:(NSArray*)args; + (void)appKitIsRunning:(id)arg; @end @@ -242,7 +228,7 @@ if (verbose) AWT_DEBUG_LOG(@"finished messaging AppKit started"); } -+ (void)start:(BOOL)headless swtMode:(BOOL)swtMode swtModeForWebStart:(BOOL)swtModeForWebStart ++ (void)start:(BOOL)headless { BOOL verbose = ShouldPrintVerboseDebugging(); @@ -258,7 +244,7 @@ BOOL onMainThread = (pthread_main_np() != 0); if (verbose) { - NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d swtMode:%d swtModeForWebStart:%d] { onMainThread:%d }", headless, swtMode, swtModeForWebStart, onMainThread]; + NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d] { onMainThread:%d }", headless, onMainThread]; AWT_DEBUG_LOG(msg); } @@ -280,9 +266,7 @@ NSArray * args = [NSArray arrayWithObjects: [NSNumber numberWithBool: onMainThread], - [NSNumber numberWithBool: swtMode], [NSNumber numberWithBool: headless], - [NSNumber numberWithBool: swtModeForWebStart], [NSNumber numberWithBool: verbose], nil]; @@ -324,14 +308,13 @@ NSAutoreleasePool *pool = [NSAutoreleasePool new]; BOOL onMainThread = [[args objectAtIndex:0] boolValue]; - BOOL swtMode = [[args objectAtIndex:1] boolValue]; - BOOL headless = [[args objectAtIndex:2] boolValue]; - BOOL swtModeForWebStart = [[args objectAtIndex:3] boolValue]; - BOOL verbose = [[args objectAtIndex:4] boolValue]; + BOOL headless = [[args objectAtIndex:1] boolValue]; + BOOL verbose = [[args objectAtIndex:2] boolValue]; BOOL wasOnMainThread = onMainThread; - setUpAWTAppKit(swtMode, headless); + // Add the exception handler of last resort + NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler); // Headless mode trumps either ordinary AWT or SWT-in-AWT mode. Declare us a daemon and return. if (headless) { @@ -339,38 +322,49 @@ return; } - if (swtMode || swtModeForWebStart) { - if (verbose) NSLog(@"in SWT or SWT/WebStart mode"); - - // The SWT should call NSApplicationLoad, but they don't know a priori that they will be using the AWT, so they don't. - NSApplicationLoad(); - } - // This will create a NSApplicationAWT for standalone AWT programs, unless there is // already a NSApplication instance. If there is already a NSApplication instance, // and -[NSApplication isRunning] returns YES, AWT is embedded inside another // AppKit Application. NSApplication *app = [NSApplicationAWT sharedApplication]; + isEmbedded = ![NSApp isKindOfClass:[NSApplicationAWT class]]; + + JNIEnv *env = [ThreadUtilities getJNIEnv]; + if (isEmbedded) { + static JNF_CLASS_CACHE(jc_AWTKeepAlive, "sun/lwawt/macosx/AWTKeepAlive"); + static JNF_STATIC_MEMBER_CACHE(jm_activate, jc_AWTKeepAlive, "activate", "()V"); + + // This will send native events every 0.5 s, thus letting the embedder know + // that AWT is alive. The events are only sent while there are active + // AWT windows present. + JNFCallStaticVoidMethod(env, jm_activate); + } else { + // Install run loop observers and set the AppKit Java thread name + setUpAWTAppKit(env); + } // AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent. if (![app isRunning]) { if (verbose) AWT_DEBUG_LOG(@"+[AWTStarter startAWT]: ![app isRunning]"); + // Note that in theory we can get here even if we're embedded and an + // embedder isn't running an event loop yet for some reason. However, + // it seems to be very unlikely. In any case, the runAWTLoopWithApp is + // able to deal with it anyway. Whether the embedder can deal with + // this is unknown. Also, it's unclear how the event loop is going to + // be terminated in this case. Setting up observers is not an option + // since the embedder could force termination. We wouldn't know and + // could crash when calling to Java. + // This is where the AWT AppKit thread parks itself to process events. [NSApplicationAWT runAWTLoopWithApp: app]; } else { // We're either embedded, or showing a splash screen - if (![NSApp isKindOfClass:[NSApplicationAWT class]]) { + if (isEmbedded) { if (verbose) AWT_DEBUG_LOG(@"running embedded"); - // Since we're embedded, no need to be swamping the runloop with the observers. - CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop]; - CFRunLoopRemoveObserver(runLoop, busyObserver, kCFRunLoopDefaultMode); - CFRunLoopRemoveObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode); // We don't track if the runloop is busy, so set it free to let AWT finish when it needs setBusy(NO); - busyObserver = NULL; - notBusyObserver = NULL; } else { if (verbose) AWT_DEBUG_LOG(@"running after showing a splash screen"); } @@ -408,49 +402,24 @@ return JNI_VERSION_1_4; } - // The following is true when AWT is attempting to connect to the window server - // when it isn't set up properly to do so. - // BOOL AWTLoadFailure = YES; For now we are skipping this check so i'm commenting out this variable as unused JNF_COCOA_ENTER(env); - // If -XstartOnFirstThread was used at invocation time, an environment variable will be set. - // (See java_md.c for the matching setenv call.) When that happens, we assume the SWT will be in use. - BOOL swt_compatible_mode = NO; - char envVar[80]; snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid()); if (getenv(envVar) != NULL) { - swt_compatible_mode = YES; + // If we don't need this in the long term, it makes sense to not set + // this env variable in the launcher code in the first place unsetenv(envVar); } - BOOL swt_in_webstart = isSWTInWebStart(env); BOOL headless = isHeadless(env); - // Make sure we're on the right thread. If not, we still need the JNIEnv to throw an exception. - if (pthread_main_np() != 0 && !swt_compatible_mode && !headless) { - AWT_DEBUG_LOG(@"Apple AWT Java VM was loaded on first thread -- can't start AWT."); - [JNFException raise:env as:kInternalError reason:"Can't start the AWT because Java was started on the first thread. Make sure StartOnFirstThread is " - "not specified in your application's Info.plist or on the command line"]; - return JNI_VERSION_1_4; - } - // We need to let Foundation know that this is a multithreaded application, if it isn't already. if (![NSThread isMultiThreaded]) { [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]; } -// if (swt_compatible_mode || headless || [AWTStarter isConnectedToWindowServer] || [AWTStarter isRemoteSession]) { -// No need in this check - we will try to launch AWTStarter anyways - to be able to run GUI application remotely -// AWTLoadFailure = NO; - - [AWTStarter start:headless swtMode:swt_compatible_mode swtModeForWebStart:swt_in_webstart]; - -// } - -/* if (AWTLoadFailure) { // We will not reach this code anyways - [JNFException raise:env as:kInternalError reason:"Can't connect to window server - not enough permissions."]; - } */ + [AWTStarter start:headless]; JNF_COCOA_EXIT(env); --- old/src/share/classes/sun/awt/AWTAutoShutdown.java 2012-12-28 16:57:31.000000000 +0400 +++ new/src/share/classes/sun/awt/AWTAutoShutdown.java 2012-12-28 16:57:31.000000000 +0400 @@ -232,10 +232,16 @@ * * @return true if AWT is in ready-to-shutdown state. */ - private boolean isReadyToShutdown() { - return (!toolkitThreadBusy && - peerMap.isEmpty() && - busyThreadSet.isEmpty()); + public boolean isReadyToShutdown() { + // We need to synchronize here since the method is public. + // The peerMap states it wants activationLock as well, + // but run() doesn't use it when calling this method, + // so we won't either. + synchronized (mainLock) { + return (!toolkitThreadBusy && + peerMap.isEmpty() && + busyThreadSet.isEmpty()); + } } /** --- /dev/null 2012-10-08 17:34:56.409901001 +0400 +++ new/src/macosx/classes/sun/lwawt/macosx/AWTKeepAlive.java 2012-12-28 16:57:32.000000000 +0400 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.lwawt.macosx; + +import sun.awt.AWTAutoShutdown; + +/** + * Support class for keeping AWT alive when it's embedded in another toolkit. + * This is done by sending a dummy native event to the native event loop. + */ +final class AWTKeepAlive extends Thread { + private final static AWTKeepAlive theInstance = new AWTKeepAlive(); + static { + theInstance.setName("AWTKeepAlive"); + theInstance.setDaemon(true); + } + + /** + * An interval for sending pings. + * + * Generally, it must be twice as less than the SAFETY_TIMEOUT + * in an auto-shutdown machinery of the mebedder's toolkit. + * Both AWT and FX use 1000 ms safety interval currently. + */ + private static final int PING_TIMEOUT = 500; + + /** + * Activate the AWTKeepAlive thread. + */ + public static void activate() { + theInstance.start(); + } + + @Override + public void run() { + try { + do { + if (!AWTAutoShutdown.getInstance().isReadyToShutdown()) { + // Send a dummy native event + LWCToolkit.getLWCToolkit().syncNativeQueue(1); + } + Thread.sleep(PING_TIMEOUT); + } while (true); + } catch (InterruptedException ex) { + } + } +}