1 /* 2 * Copyright (c) 2011, 2012, 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