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 { 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 + (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block { 249 if ([NSThread isMainThread] && wait == YES) { 250 block(); 251 } else { 252 [JNFRunLoop performOnMainThreadWaiting:wait withBlock:block]; 253 } 254 } 255 256 @end 257 258 259 void OSXAPP_SetJavaVM(JavaVM *vm) 260 { 261 jvm = vm; 262 } 263 | 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 { 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 |