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 sCocoaComponentCompatibility = NO; 41 static NSTimeInterval sCocoaComponentCompatibilityTimeout = 0.5; 42 static BOOL sLoggingEnabled = YES; 43 44 #ifdef AWT_THREAD_ASSERTS_ENV_ASSERT 45 int sAWTThreadAsserts = 0; 46 #endif /* AWT_THREAD_ASSERTS_ENV_ASSERT */ 47 48 49 // This is for backward compatibility for those people using CocoaComponent 50 // Since we've flipped the AWT threading model for Tiger (10.4), all the rules 51 // for CocoaComponent are wrong. 52 // So for existing CocoaComponent users, we can't be synchronous. 53 // Making things totally asynchronous breaks a _lot_, so we try to be 54 // synchronous and time out after a little bit. 55 #define NOT_READY 0 56 #define READY 1 57 #define IN_PROGRESS 2 58 59 BOOL sInPerformFromJava = NO; 60 NSUInteger sPerformCount = 0; 61 62 // This class is used so that performSelectorOnMainThread can be 63 // controlled a little more easily by us. It has 2 roles. 64 // The first is to set/unset a flag (sInPerformFromJava) that code can 65 // check to see if we are in a synchronous perform initiated by a java thread. 66 // The second is to implement the CocoaComponent backward compatibility mode. 67 @interface CPerformer : NSObject { 68 id fTarget; 69 SEL fSelector; 70 id fArg; 71 BOOL fWait; 72 } 73 74 - (id) initWithTarget:(id)target selector:(SEL)selector arg:(id)arg wait:(BOOL)wait; 75 - (void) perform; 76 - (void) performCompatible; 77 - (void) _performCompatible:(NSConditionLock *)resultLock; 78 @end 79 80 81 @implementation CPerformer 82 83 - (id) initWithTarget:(id)target selector:(SEL)selector arg:(id)arg { 84 return [self initWithTarget:target selector:selector arg:arg wait:YES]; 85 } 86 87 - (id) initWithTarget:(id)target selector:(SEL)selector arg:(id)arg wait:(BOOL)wait { 88 self = [super init]; 89 if (self != nil) { 90 fTarget = [target retain]; 91 fSelector = selector; 92 fArg = [arg retain]; 93 // Only set sInPerformFromJava if this is a synchronous perform 94 fWait = wait; 95 } 96 return self; 97 } 98 99 - (void) dealloc { 100 [fTarget release]; 101 [fArg release]; 102 [super dealloc]; 103 } 104 //- (void)finalize { [super finalize]; } 105 106 - (void) perform { 107 AWT_ASSERT_APPKIT_THREAD; 108 109 // If this is the first time we're going from java thread -> appkit thread, 110 // set sInPerformFromJava for the duration of the invocation 111 BOOL nestedPerform = sInPerformFromJava; 112 if (fWait) { 113 sInPerformFromJava = YES; 114 } 115 116 sPerformCount++; 117 118 // Actually do the work (cheat to avoid a method call) 119 @try { 120 objc_msgSend(fTarget, fSelector, fArg); 121 //[fTarget performSelector:fSelector withObject:fArg]; 122 } @catch (NSException *e) { 123 NSLog(@"*** CPerformer: ignoring exception '%@' raised during perform of selector '%@' on target '%@' with args '%@'", e, NSStringFromSelector(fSelector), fTarget, fArg); 124 } @finally { 125 // If we actually set sInPerformFromJava, unset it now 126 if (!nestedPerform && fWait) { 127 sInPerformFromJava = NO; 128 } 129 } 130 } 131 132 - (void) performCompatible { 133 // We check if we are on the AppKit thread because frequently, apps 134 // using CocoaComponent are doing things on the wrong thread! 135 if (pthread_main_np()) { 136 [fTarget performSelector:fSelector withObject:fArg]; 137 } else { 138 // Setup the lock 139 NSConditionLock *resultLock = 140 [[NSConditionLock alloc] initWithCondition:NOT_READY]; 141 142 // Make sure that if we return early, nothing gets released out 143 // from under us 144 [resultLock retain]; 145 [fTarget retain]; 146 [fArg retain]; 147 [self retain]; 148 // Do an asynchronous perform to the main thread. 149 [self performSelectorOnMainThread:@selector(_performCompatible:) 150 withObject:resultLock waitUntilDone:NO modes:sAWTPerformModes]; 151 152 // Wait for a little bit for it to finish 153 [resultLock lockWhenCondition:READY beforeDate:[NSDate dateWithTimeIntervalSinceNow:sCocoaComponentCompatibilityTimeout]]; 154 155 // If the _performCompatible is actually in progress, 156 // we should let it finish 157 if ([resultLock condition] == IN_PROGRESS) { 158 [resultLock lockWhenCondition:READY]; 159 } 160 161 if ([resultLock condition] == NOT_READY && sLoggingEnabled) { 162 NSLog(@"[Java CocoaComponent compatibility mode]: Operation timed out due to possible deadlock: selector '%@' on target '%@' with args '%@'", NSStringFromSelector(fSelector), fTarget, fArg); 163 } 164 165 [resultLock unlock]; 166 [resultLock autorelease]; 167 } 168 } 169 170 - (void) _performCompatible:(NSConditionLock *)resultLock { 171 // notify that the perform is in progress! 172 [resultLock lock]; 173 [resultLock unlockWithCondition:IN_PROGRESS]; 174 175 sPerformCount++; 176 177 // Actually do the work. 178 @try { 179 [fTarget performSelector:fSelector withObject:fArg]; 180 } @catch (NSException *e) { 181 NSLog(@"*** CPerformer: ignoring exception '%@' raised during performCompatible of selector '%@' on target '%@' with args '%@'", e, NSStringFromSelector(fSelector), fTarget, fArg); 182 } @finally { 183 // notify done! 184 [resultLock lock]; 185 [resultLock unlockWithCondition:READY]; 186 187 // Clean up after ourselves 188 [resultLock autorelease]; 189 [fTarget autorelease]; 190 [fArg autorelease]; 191 [self autorelease]; 192 } 193 } 194 @end 195 196 197 @implementation ThreadUtilities 198 199 + (JNIEnv*)getJNIEnv { 200 AWT_ASSERT_APPKIT_THREAD; 201 if (appKitEnv == NULL) { 202 (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&appKitEnv, NULL); 203 } 204 return appKitEnv; 205 } 206 207 + (JNIEnv*)getJNIEnvUncached { 208 JNIEnv *env = NULL; 209 (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void **)&env, nil); 210 return env; 211 } 212 213 + (void)initialize { 214 // Headless: BOTH 215 // Embedded: BOTH 216 // Multiple Calls: NO 217 // Caller: Obj-C class initialization 218 // Thread: ? 219 220 if (sPerformModes == nil) { 221 // Create list of Run Loop modes to perform on 222 // The default performSelector, with no mode argument, runs in Default, 223 // ModalPanel, and EventTracking modes 224 sPerformModes = [[NSArray alloc] initWithObjects:NSDefaultRunLoopMode, NSModalPanelRunLoopMode, nil]; 225 sAWTPerformModes = [[NSArray alloc] initWithObjects:NSDefaultRunLoopMode, NSModalPanelRunLoopMode, NSEventTrackingRunLoopMode, [JNFRunLoop javaRunLoopMode], nil]; 226 227 #ifdef AWT_THREAD_ASSERTS_ENV_ASSERT 228 sAWTThreadAsserts = (getenv("COCOA_AWT_DISABLE_THREAD_ASSERTS") == NULL); 229 #endif /* AWT_THREAD_ASSERTS_ENV_ASSERT */ 230 } 231 } 232 233 // These methods can behave slightly differently than the normal 234 // performSelector... In particular, we define a special runloop mode 235 // (AWTRunLoopMode) so that we can "block" the main thread against the 236 // java event thread without deadlocking. See CToolkit.invokeAndWait. 237 + (void)performOnMainThread:(SEL)aSelector onObject:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait awtMode:(BOOL)inAWT { 238 CPerformer *performer = [[CPerformer alloc] initWithTarget:target selector:aSelector arg:arg wait:wait]; 239 if (sCocoaComponentCompatibility && wait && inAWT) { 240 [performer performCompatible]; 241 [performer autorelease]; 242 } else { 243 [performer performSelectorOnMainThread:@selector(perform) withObject:nil waitUntilDone:wait modes:((inAWT) ? sAWTPerformModes : sPerformModes)]; // AWT_THREADING Safe (cover method) 244 [performer release]; 245 } 246 } 247 248 @end 249 250 251 void OSXAPP_SetJavaVM(JavaVM *vm) 252 { 253 jvm = vm; 254 } 255