1 /*
   2  * Copyright (c) 2011, 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 <AppKit/AppKit.h>
  27 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  28 #import <objc/message.h>
  29 
  30 #import "ThreadUtilities.h"
  31 
  32 
  33 // The following must be named "jvm", as there are extern references to it in AWT
  34 JavaVM *jvm = NULL;
  35 static JNIEnv *appKitEnv = NULL;
  36 
  37 static NSArray *sPerformModes = nil;
  38 static NSArray *sAWTPerformModes = nil;
  39 
  40 static BOOL sLoggingEnabled = YES;
  41 
  42 #ifdef AWT_THREAD_ASSERTS_ENV_ASSERT
  43 int sAWTThreadAsserts = 0;
  44 #endif /* AWT_THREAD_ASSERTS_ENV_ASSERT */
  45 
  46 BOOL sInPerformFromJava = NO;
  47 
  48 // This class is used so that performSelectorOnMainThread can be
  49 // controlled a little more easily by us.  It has 2 roles.
  50 // The first is to set/unset a flag (sInPerformFromJava) that code can
  51 // check to see if we are in a synchronous perform initiated by a java thread.
  52 // The second is to implement the CocoaComponent backward compatibility mode.
  53 @interface CPerformer : NSObject {
  54     id fTarget;
  55     SEL fSelector;
  56     id fArg;
  57     BOOL fWait;
  58 }
  59 
  60 - (id) initWithTarget:(id)target selector:(SEL)selector arg:(id)arg wait:(BOOL)wait;
  61 - (void) perform;
  62 @end
  63 
  64 
  65 @implementation CPerformer
  66 
  67 - (id) initWithTarget:(id)target selector:(SEL)selector arg:(id)arg {
  68     return [self initWithTarget:target selector:selector arg:arg wait:YES];
  69 }
  70 
  71 - (id) initWithTarget:(id)target selector:(SEL)selector arg:(id)arg wait:(BOOL)wait {
  72     self = [super init];
  73     if (self != nil) {
  74         fTarget = [target retain];
  75         fSelector = selector;
  76         fArg = [arg retain];
  77         // Only set sInPerformFromJava if this is a synchronous perform
  78         fWait = wait;
  79     }
  80     return self;
  81 }
  82 
  83 - (void) dealloc {
  84     [fTarget release];
  85     [fArg release];
  86     [super dealloc];
  87 }
  88 //- (void)finalize { [super finalize]; }
  89 
  90 - (void) perform {
  91     AWT_ASSERT_APPKIT_THREAD;
  92 
  93     // If this is the first time we're going from java thread -> appkit thread,
  94     // set sInPerformFromJava for the duration of the invocation
  95     BOOL nestedPerform = sInPerformFromJava;
  96     if (fWait) {
  97         sInPerformFromJava = YES;
  98     }
  99 
 100     // Actually do the work (cheat to avoid a method call)
 101     @try {
 102         objc_msgSend(fTarget, fSelector, fArg);
 103         //[fTarget performSelector:fSelector withObject:fArg];
 104     } @catch (NSException *e) {
 105         NSLog(@"*** CPerformer: ignoring exception '%@' raised during perform of selector '%@' on target '%@' with args '%@'", e, NSStringFromSelector(fSelector), fTarget, fArg);
 106     } @finally {
 107         // If we actually set sInPerformFromJava, unset it now
 108         if (!nestedPerform && fWait) {
 109             sInPerformFromJava = NO;
 110         }
 111     }
 112 }
 113 @end
 114 
 115 
 116 @implementation ThreadUtilities
 117 
 118 + (JNIEnv*)getJNIEnv {
 119 AWT_ASSERT_APPKIT_THREAD;
 120     if (appKitEnv == NULL) {
 121         (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&appKitEnv, NULL);
 122     }
 123     return appKitEnv;
 124 }
 125 
 126 + (JNIEnv*)getJNIEnvUncached {
 127     JNIEnv *env = NULL;
 128     (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&env, nil);
 129     return env;
 130 }
 131 
 132 + (void)initialize {
 133     // Headless: BOTH
 134     // Embedded: BOTH
 135     // Multiple Calls: NO
 136     // Caller: Obj-C class initialization
 137     // Thread: ?
 138 
 139     if (sPerformModes == nil) {
 140         // Create list of Run Loop modes to perform on
 141         // The default performSelector, with no mode argument, runs in Default,
 142         // ModalPanel, and EventTracking modes
 143         sPerformModes =    [[NSArray alloc] initWithObjects:NSDefaultRunLoopMode, NSModalPanelRunLoopMode, nil];
 144         sAWTPerformModes = [[NSArray alloc] initWithObjects:NSDefaultRunLoopMode, NSModalPanelRunLoopMode, NSEventTrackingRunLoopMode, [JNFRunLoop javaRunLoopMode], nil];
 145 
 146 #ifdef AWT_THREAD_ASSERTS_ENV_ASSERT
 147         sAWTThreadAsserts = (getenv("COCOA_AWT_DISABLE_THREAD_ASSERTS") == NULL);
 148 #endif /* AWT_THREAD_ASSERTS_ENV_ASSERT */
 149     }
 150 }
 151 
 152 // These methods can behave slightly differently than the normal
 153 // performSelector...  In particular, we define a special runloop mode
 154 // (AWTRunLoopMode) so that we can "block" the main thread against the
 155 // java event thread without deadlocking. See CToolkit.invokeAndWait.
 156 + (void)performOnMainThread:(SEL)aSelector onObject:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait awtMode:(BOOL)inAWT {
 157     CPerformer *performer = [[CPerformer alloc] initWithTarget:target selector:aSelector arg:arg wait:wait];
 158     [performer performSelectorOnMainThread:@selector(perform) withObject:nil waitUntilDone:wait modes:((inAWT) ? sAWTPerformModes : sPerformModes)]; // AWT_THREADING Safe (cover method)
 159     [performer release];
 160 }
 161 
 162 + (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block {
 163     if ([NSThread isMainThread] && wait == YES) {
 164         block(); 
 165     } else { 
 166         [JNFRunLoop performOnMainThreadWaiting:wait withBlock:block]; 
 167     }
 168 }
 169 
 170 + (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait {
 171     if ([NSThread isMainThread] && wait == YES) {
 172         [target performSelector:aSelector withObject:arg];
 173     } else {
 174         [JNFRunLoop performOnMainThread:aSelector on:target withObject:arg waitUntilDone:wait];
 175     }
 176 }
 177 
 178 @end
 179 
 180 
 181 void OSXAPP_SetJavaVM(JavaVM *vm)
 182 {
 183     jvm = vm;
 184 }
 185