jdk/src/macosx/native/sun/awt/awt.m
changeset 25563 248400a88627
parent 25562 af98568d21e9
child 25564 871d3490f14d
equal deleted inserted replaced
25562:af98568d21e9 25563:248400a88627
     1 /*
       
     2  * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 #import <pthread.h>
       
    27 #import <objc/runtime.h>
       
    28 #import <Cocoa/Cocoa.h>
       
    29 #import <Security/AuthSession.h>
       
    30 #import <JavaNativeFoundation/JavaNativeFoundation.h>
       
    31 #import <JavaRuntimeSupport/JavaRuntimeSupport.h>
       
    32 
       
    33 #import "NSApplicationAWT.h"
       
    34 #import "PropertiesUtilities.h"
       
    35 #import "ThreadUtilities.h"
       
    36 #import "AWT_debug.h"
       
    37 #import "ApplicationDelegate.h"
       
    38 
       
    39 #define DEBUG 0
       
    40 
       
    41 
       
    42 // The symbol is defined in libosxapp.dylib (ThreadUtilities.m)
       
    43 extern JavaVM *jvm;
       
    44 
       
    45 // Indicates if AWT is running embedded (in SWT, FX, elsewhere)
       
    46 static BOOL isEmbedded = NO;
       
    47 
       
    48 // Indicates that the app has been started with -XstartOnFirstThread
       
    49 // (directly or via WebStart settings), and AWT should not run its
       
    50 // own event loop in this mode. Even if a loop isn't running yet,
       
    51 // we expect an embedder (e.g. SWT) to start it some time later.
       
    52 static BOOL forceEmbeddedMode = NO;
       
    53 
       
    54 static bool ShouldPrintVerboseDebugging() {
       
    55     static int debug = -1;
       
    56     if (debug == -1) {
       
    57         debug = (int)(getenv("JAVA_AWT_VERBOSE") != NULL) || (DEBUG != 0);
       
    58     }
       
    59     return (bool)debug;
       
    60 }
       
    61 
       
    62 // This is the data necessary to have JNI_OnLoad wait for AppKit to start.
       
    63 static BOOL sAppKitStarted = NO;
       
    64 static pthread_mutex_t sAppKitStarted_mutex = PTHREAD_MUTEX_INITIALIZER;
       
    65 static pthread_cond_t sAppKitStarted_cv = PTHREAD_COND_INITIALIZER;
       
    66 
       
    67 void setBusy(BOOL isBusy);
       
    68 static void BusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg);
       
    69 static void NotBusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg);
       
    70 static void AWT_NSUncaughtExceptionHandler(NSException *exception);
       
    71 
       
    72 static CFRunLoopObserverRef busyObserver = NULL;
       
    73 static CFRunLoopObserverRef notBusyObserver = NULL;
       
    74 
       
    75 static void setUpAWTAppKit()
       
    76 {
       
    77     BOOL verbose = ShouldPrintVerboseDebugging();
       
    78     if (verbose) AWT_DEBUG_LOG(@"setting up busy observers");
       
    79 
       
    80     // Add CFRunLoopObservers to call into AWT so that AWT knows that the
       
    81     //  AWT thread (which is the AppKit main thread) is alive. This way AWT
       
    82     //  will not automatically shutdown.
       
    83     busyObserver = CFRunLoopObserverCreate(
       
    84             NULL,                        // CFAllocator
       
    85             kCFRunLoopAfterWaiting,      // CFOptionFlags
       
    86             true,                        // repeats
       
    87             NSIntegerMax,                // order
       
    88             &BusyObserver,               // CFRunLoopObserverCallBack
       
    89             NULL);                       // CFRunLoopObserverContext
       
    90 
       
    91     notBusyObserver = CFRunLoopObserverCreate(
       
    92             NULL,                        // CFAllocator
       
    93             kCFRunLoopBeforeWaiting,     // CFOptionFlags
       
    94             true,                        // repeats
       
    95             NSIntegerMin,                // order
       
    96             &NotBusyObserver,            // CFRunLoopObserverCallBack
       
    97             NULL);                       // CFRunLoopObserverContext
       
    98 
       
    99     CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
       
   100     CFRunLoopAddObserver(runLoop, busyObserver, kCFRunLoopDefaultMode);
       
   101     CFRunLoopAddObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
       
   102 
       
   103     CFRelease(busyObserver);
       
   104     CFRelease(notBusyObserver);
       
   105 
       
   106     setBusy(YES);
       
   107 }
       
   108 
       
   109 static void setUpAppKitThreadName()
       
   110 {
       
   111     BOOL verbose = ShouldPrintVerboseDebugging();
       
   112     JNIEnv *env = [ThreadUtilities getJNIEnv];
       
   113 
       
   114     // Set the java name of the AppKit main thread appropriately.
       
   115     jclass threadClass = NULL;
       
   116     jstring name = NULL;
       
   117     jobject curThread = NULL;
       
   118 
       
   119     threadClass = (*env)->FindClass(env, "java/lang/Thread");
       
   120     if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
       
   121     jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;");
       
   122     if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
       
   123     jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V");
       
   124     if (setName == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
       
   125 
       
   126     curThread = (*env)->CallStaticObjectMethod(env, threadClass, currentThreadID); // AWT_THREADING Safe (known object)
       
   127     if (curThread == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
       
   128     name = (*env)->NewStringUTF(env, "AWT-AppKit");
       
   129     if (name == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
       
   130     (*env)->CallVoidMethod(env, curThread, setName, name); // AWT_THREADING Safe (known object)
       
   131     if ((*env)->ExceptionCheck(env)) goto cleanup;
       
   132 
       
   133 cleanup:
       
   134     if (threadClass != NULL) {
       
   135         (*env)->DeleteLocalRef(env, threadClass);
       
   136     }
       
   137     if (name != NULL) {
       
   138         (*env)->DeleteLocalRef(env, name);
       
   139     }
       
   140     if (curThread != NULL) {
       
   141         (*env)->DeleteLocalRef(env, curThread);
       
   142     }
       
   143     if ((*env)->ExceptionCheck(env)) {
       
   144         (*env)->ExceptionDescribe(env);
       
   145         (*env)->ExceptionClear(env);
       
   146     }
       
   147 
       
   148     if (verbose) AWT_DEBUG_LOG(@"finished setting thread name");
       
   149 }
       
   150 
       
   151 
       
   152 // Returns true if java believes it is running headless
       
   153 BOOL isHeadless(JNIEnv *env) {
       
   154     // Just access the property directly, instead of using GraphicsEnvironment.isHeadless.
       
   155     //  This is because this may be called while AWT is being loaded, and calling AWT
       
   156     //  while it is being loaded will deadlock.
       
   157     static JNF_CLASS_CACHE(jc_Toolkit, "java/awt/GraphicsEnvironment");
       
   158     static JNF_STATIC_MEMBER_CACHE(jm_isHeadless, jc_Toolkit, "isHeadless", "()Z");
       
   159     return JNFCallStaticBooleanMethod(env, jm_isHeadless);
       
   160 }
       
   161 
       
   162 BOOL isSWTInWebStart(JNIEnv* env) {
       
   163     NSString *swtWebStart = [PropertiesUtilities javaSystemPropertyForKey:@"com.apple.javaws.usingSWT" withEnv:env];
       
   164     return [@"true" isCaseInsensitiveLike:swtWebStart];
       
   165 }
       
   166 
       
   167 void setBusy(BOOL busy) {
       
   168 AWT_ASSERT_APPKIT_THREAD;
       
   169 
       
   170     JNIEnv *env = [ThreadUtilities getJNIEnv];
       
   171     static JNF_CLASS_CACHE(jc_AWTAutoShutdown, "sun/awt/AWTAutoShutdown");
       
   172 
       
   173     if (busy) {
       
   174         static JNF_STATIC_MEMBER_CACHE(jm_notifyBusyMethod, jc_AWTAutoShutdown, "notifyToolkitThreadBusy", "()V");
       
   175         JNFCallStaticVoidMethod(env, jm_notifyBusyMethod);
       
   176     } else {
       
   177         static JNF_STATIC_MEMBER_CACHE(jm_notifyFreeMethod, jc_AWTAutoShutdown, "notifyToolkitThreadFree", "()V");
       
   178         JNFCallStaticVoidMethod(env, jm_notifyFreeMethod);
       
   179     }
       
   180 }
       
   181 
       
   182 static void BusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg) {
       
   183 AWT_ASSERT_APPKIT_THREAD;
       
   184 
       
   185     // This is only called with the selector kCFRunLoopAfterWaiting.
       
   186 #ifndef PRODUCT_BUILD
       
   187     assert(what == kCFRunLoopAfterWaiting);
       
   188 #endif /* PRODUCT_BUILD */
       
   189 
       
   190     setBusy(YES);
       
   191 }
       
   192 
       
   193 static void NotBusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg) {
       
   194 AWT_ASSERT_APPKIT_THREAD;
       
   195 
       
   196     // This is only called with the selector kCFRunLoopBeforeWaiting.
       
   197 #ifndef PRODUCT_BUILD
       
   198     assert(what == kCFRunLoopBeforeWaiting);
       
   199 #endif /* PRODUCT_BUILD */
       
   200 
       
   201     setBusy(NO);
       
   202 }
       
   203 
       
   204 static void AWT_NSUncaughtExceptionHandler(NSException *exception) {
       
   205     NSLog(@"Apple AWT Internal Exception: %@", [exception description]);
       
   206 }
       
   207 
       
   208 // This is an empty Obj-C object just so that -peformSelectorOnMainThread can be used.
       
   209 @interface AWTStarter : NSObject { }
       
   210 + (void)start:(BOOL)headless;
       
   211 - (void)starter:(NSArray*)args;
       
   212 + (void)appKitIsRunning:(id)arg;
       
   213 @end
       
   214 
       
   215 @implementation AWTStarter
       
   216 
       
   217 + (BOOL) isConnectedToWindowServer {
       
   218     SecuritySessionId session_id;
       
   219     SessionAttributeBits session_info;
       
   220     OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info);
       
   221     if (status != noErr) return NO;
       
   222     if (!(session_info & sessionHasGraphicAccess)) return NO;
       
   223     return YES;
       
   224 }
       
   225 
       
   226 + (BOOL) markAppAsDaemon {
       
   227     id jrsAppKitAWTClass = objc_getClass("JRSAppKitAWT");
       
   228     SEL markAppSel = @selector(markAppIsDaemon);
       
   229     if (![jrsAppKitAWTClass respondsToSelector:markAppSel]) return NO;
       
   230     return [jrsAppKitAWTClass performSelector:markAppSel] ? YES : NO;
       
   231 }
       
   232 
       
   233 + (void)appKitIsRunning:(id)arg {
       
   234     // Headless: NO
       
   235     // Embedded: BOTH
       
   236     // Multiple Calls: NO
       
   237     //  Callers: AppKit's NSApplicationDidFinishLaunchingNotification or +[AWTStarter startAWT:]
       
   238 AWT_ASSERT_APPKIT_THREAD;
       
   239 
       
   240     BOOL verbose = ShouldPrintVerboseDebugging();
       
   241     if (verbose) AWT_DEBUG_LOG(@"about to message AppKit started");
       
   242 
       
   243     // Signal that AppKit has started (or is already running).
       
   244     pthread_mutex_lock(&sAppKitStarted_mutex);
       
   245     sAppKitStarted = YES;
       
   246     pthread_cond_signal(&sAppKitStarted_cv);
       
   247     pthread_mutex_unlock(&sAppKitStarted_mutex);
       
   248 
       
   249     if (verbose) AWT_DEBUG_LOG(@"finished messaging AppKit started");
       
   250 }
       
   251 
       
   252 + (void)start:(BOOL)headless
       
   253 {
       
   254     BOOL verbose = ShouldPrintVerboseDebugging();
       
   255 
       
   256     // Headless: BOTH
       
   257     // Embedded: BOTH
       
   258     // Multiple Calls: NO
       
   259     //  Caller: JNI_OnLoad
       
   260 
       
   261     // onMainThread is NOT the same at SWT mode!
       
   262     // If the JVM was started on the first thread for SWT, but the SWT loads the AWT on a secondary thread,
       
   263     // onMainThread here will be false but SWT mode will be true.  If we are currently on the main thread, we don't
       
   264     // need to throw AWT startup over to another thread.
       
   265     BOOL onMainThread = (pthread_main_np() != 0);
       
   266 
       
   267     if (verbose) {
       
   268         NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d] { onMainThread:%d }", headless, onMainThread];
       
   269         AWT_DEBUG_LOG(msg);
       
   270     }
       
   271 
       
   272     if (!headless)
       
   273     {
       
   274         // Listen for the NSApp to start. This indicates that JNI_OnLoad can proceed.
       
   275         //  It must wait because there is a chance that another java thread will grab
       
   276         //  the AppKit lock before the +[NSApplication sharedApplication] returns.
       
   277         //  See <rdar://problem/3492666> for an example.
       
   278         [[NSNotificationCenter defaultCenter] addObserver:[AWTStarter class]
       
   279                                                  selector:@selector(appKitIsRunning:)
       
   280                                                      name:NSApplicationDidFinishLaunchingNotification
       
   281                                                    object:nil];
       
   282 
       
   283         if (verbose) NSLog(@"+[AWTStarter start:::]: registered NSApplicationDidFinishLaunchingNotification");
       
   284     }
       
   285 
       
   286     id st = [[AWTStarter alloc] init];
       
   287 
       
   288     NSArray * args = [NSArray arrayWithObjects:
       
   289                       [NSNumber numberWithBool: onMainThread],
       
   290                       [NSNumber numberWithBool: headless],
       
   291                       [NSNumber numberWithBool: verbose],
       
   292                       nil];
       
   293 
       
   294     if (onMainThread) {
       
   295         [st starter:args];
       
   296     } else {
       
   297         [st performSelectorOnMainThread: @selector(starter:) withObject:args waitUntilDone:NO];
       
   298     }
       
   299 
       
   300     if (!headless && !onMainThread) {
       
   301         if (verbose) AWT_DEBUG_LOG(@"about to wait on AppKit startup mutex");
       
   302 
       
   303         // Wait here for AppKit to have started (or for AWT to have been loaded into
       
   304         //  an already running NSApplication).
       
   305         pthread_mutex_lock(&sAppKitStarted_mutex);
       
   306         while (sAppKitStarted == NO) {
       
   307             pthread_cond_wait(&sAppKitStarted_cv, &sAppKitStarted_mutex);
       
   308         }
       
   309         pthread_mutex_unlock(&sAppKitStarted_mutex);
       
   310 
       
   311         // AWT gets here AFTER +[AWTStarter appKitIsRunning:] is called.
       
   312         if (verbose) AWT_DEBUG_LOG(@"got out of the AppKit startup mutex");
       
   313     }
       
   314 
       
   315     if (!headless) {
       
   316         // Don't set the delegate until the NSApplication has been created and
       
   317         // its finishLaunching has initialized it.
       
   318         //  ApplicationDelegate is the support code for com.apple.eawt.
       
   319         [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
       
   320             id<NSApplicationDelegate> delegate = [ApplicationDelegate sharedDelegate];
       
   321             if (delegate != nil) {
       
   322                 OSXAPP_SetApplicationDelegate(delegate);
       
   323             }        
       
   324         }];
       
   325     }
       
   326 }
       
   327 
       
   328 - (void)starter:(NSArray*)args {
       
   329     NSAutoreleasePool *pool = [NSAutoreleasePool new];
       
   330 
       
   331     BOOL onMainThread = [[args objectAtIndex:0] boolValue];
       
   332     BOOL headless = [[args objectAtIndex:1] boolValue];
       
   333     BOOL verbose = [[args objectAtIndex:2] boolValue];
       
   334 
       
   335     BOOL wasOnMainThread = onMainThread;
       
   336 
       
   337     // Add the exception handler of last resort
       
   338     NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler);
       
   339 
       
   340     // Headless mode trumps either ordinary AWT or SWT-in-AWT mode.  Declare us a daemon and return.
       
   341     if (headless) {
       
   342         // Note that we don't install run loop observers in headless mode
       
   343         // because we don't need them (see 7174704)
       
   344         if (!forceEmbeddedMode) {
       
   345             setUpAppKitThreadName();
       
   346         }
       
   347         [AWTStarter markAppAsDaemon];
       
   348         return;
       
   349     }
       
   350 
       
   351     if (forceEmbeddedMode) {
       
   352         if (verbose) NSLog(@"in SWT or SWT/WebStart mode");
       
   353 
       
   354         // Init a default NSApplication instance instead of the NSApplicationAWT.
       
   355         // Note that [NSApp isRunning] will return YES after that, though
       
   356         // this behavior isn't specified anywhere. We rely on that.
       
   357         NSApplicationLoad();
       
   358     }
       
   359 
       
   360     // This will create a NSApplicationAWT for standalone AWT programs, unless there is
       
   361     //  already a NSApplication instance. If there is already a NSApplication instance,
       
   362     //  and -[NSApplication isRunning] returns YES, AWT is embedded inside another
       
   363     //  AppKit Application.
       
   364     NSApplication *app = [NSApplicationAWT sharedApplication];
       
   365     isEmbedded = ![NSApp isKindOfClass:[NSApplicationAWT class]];
       
   366     [ThreadUtilities setAWTEmbedded:isEmbedded];
       
   367 
       
   368     if (!isEmbedded) {
       
   369         // Install run loop observers and set the AppKit Java thread name
       
   370         setUpAWTAppKit();
       
   371         setUpAppKitThreadName();
       
   372     }
       
   373 
       
   374     // AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent.
       
   375     if (![app isRunning]) {
       
   376         if (verbose) AWT_DEBUG_LOG(@"+[AWTStarter startAWT]: ![app isRunning]");
       
   377 
       
   378         // This is where the AWT AppKit thread parks itself to process events.
       
   379         [NSApplicationAWT runAWTLoopWithApp: app];
       
   380     } else {
       
   381         // We're either embedded, or showing a splash screen
       
   382         if (isEmbedded) {
       
   383             if (verbose) AWT_DEBUG_LOG(@"running embedded");
       
   384 
       
   385             // We don't track if the runloop is busy, so set it free to let AWT finish when it needs
       
   386             setBusy(NO);
       
   387         } else {
       
   388             if (verbose) AWT_DEBUG_LOG(@"running after showing a splash screen");
       
   389         }
       
   390 
       
   391         // Signal so that JNI_OnLoad can proceed.
       
   392         if (!wasOnMainThread) [AWTStarter appKitIsRunning:nil];
       
   393 
       
   394         // Proceed to exit this call as there is no reason to run the NSApplication event loop.
       
   395     }
       
   396 
       
   397     [pool drain];
       
   398 }
       
   399 
       
   400 @end
       
   401 
       
   402 
       
   403 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
       
   404     BOOL verbose = ShouldPrintVerboseDebugging();
       
   405     if (verbose) AWT_DEBUG_LOG(@"entered JNI_OnLoad");
       
   406 
       
   407     // Headless: BOTH
       
   408     // Embedded: BOTH
       
   409     // Multiple Calls: NO
       
   410     //  Caller: JavaVM classloader
       
   411 
       
   412     // Keep a static reference for other archives.
       
   413     OSXAPP_SetJavaVM(vm);
       
   414 
       
   415     JNIEnv *env = NULL;
       
   416 
       
   417     // Need JNIEnv for JNF_COCOA_ENTER(env); macro below
       
   418     jint status = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4);
       
   419     if (status != JNI_OK || env == NULL) {
       
   420         AWT_DEBUG_LOG(@"Can't get JNIEnv");
       
   421         return JNI_VERSION_1_4;
       
   422     }
       
   423 
       
   424 JNF_COCOA_ENTER(env);
       
   425 
       
   426     // Launcher sets this env variable if -XstartOnFirstThread is specified
       
   427     char envVar[80];
       
   428     snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
       
   429     if (getenv(envVar) != NULL) {
       
   430         forceEmbeddedMode = YES;
       
   431         unsetenv(envVar);
       
   432     }
       
   433 
       
   434     if (isSWTInWebStart(env)) {
       
   435         forceEmbeddedMode = YES;
       
   436     }
       
   437     JNIEnv* env = [ThreadUtilities getJNIEnvUncached];
       
   438     jclass jc_ThreadGroupUtils = (*env)->FindClass(env, "sun/awt/util/ThreadGroupUtils");
       
   439     jmethodID sjm_getRootThreadGroup = (*env)->GetStaticMethodID(env, jc_ThreadGroupUtils, "getRootThreadGroup", "()Ljava/lang/ThreadGroup;");
       
   440     jobject rootThreadGroup = (*env)->CallStaticObjectMethod(env, jc_ThreadGroupUtils, sjm_getRootThreadGroup);
       
   441     [ThreadUtilities setAppkitThreadGroup:(*env)->NewGlobalRef(env, rootThreadGroup)];
       
   442     // The current thread was attached in getJNIEnvUnchached.
       
   443     // Detach it back. It will be reattached later if needed with a proper TG
       
   444     [ThreadUtilities detachCurrentThread];
       
   445 
       
   446     BOOL headless = isHeadless(env);
       
   447 
       
   448     // We need to let Foundation know that this is a multithreaded application, if it isn't already.
       
   449     if (![NSThread isMultiThreaded]) {
       
   450         [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil];
       
   451     }
       
   452 
       
   453     [AWTStarter start:headless];
       
   454 
       
   455 JNF_COCOA_EXIT(env);
       
   456 
       
   457     if (verbose) AWT_DEBUG_LOG(@"exiting JNI_OnLoad");
       
   458 
       
   459     return JNI_VERSION_1_4;
       
   460 }