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 static bool ShouldPrintVerboseDebugging() {
46 static int debug = -1;
47 if (debug == -1) {
48 debug = (int)(getenv("JAVA_AWT_VERBOSE") != NULL) || (DEBUG != 0);
49 }
50 return (bool)debug;
51 }
52
53 // This is the data necessary to have JNI_OnLoad wait for AppKit to start.
54 static BOOL sAppKitStarted = NO;
55 static pthread_mutex_t sAppKitStarted_mutex = PTHREAD_MUTEX_INITIALIZER;
56 static pthread_cond_t sAppKitStarted_cv = PTHREAD_COND_INITIALIZER;
57
58 void setBusy(BOOL isBusy);
59 static void BusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg);
60 static void NotBusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg);
61 static void AWT_NSUncaughtExceptionHandler(NSException *exception);
62
63 static CFRunLoopObserverRef busyObserver = NULL;
64 static CFRunLoopObserverRef notBusyObserver = NULL;
65
66 static void setUpAWTAppKit(BOOL swt_mode, BOOL headless) {
67 AWT_ASSERT_APPKIT_THREAD;
68 BOOL verbose = ShouldPrintVerboseDebugging();
69 if (verbose) AWT_DEBUG_LOG(@"setting up busy observers");
70
71 JNIEnv *env = [ThreadUtilities getJNIEnv];
72
73 // Add CFRunLoopObservers to call into AWT so that AWT knows that the
74 // AWT thread (which is the AppKit main thread) is alive. This way AWT
75 // will not automatically shutdown.
76 busyObserver = CFRunLoopObserverCreate(
77 NULL, // CFAllocator
78 kCFRunLoopAfterWaiting, // CFOptionFlags
79 true, // repeats
80 NSIntegerMax, // order
81 &BusyObserver, // CFRunLoopObserverCallBack
82 NULL); // CFRunLoopObserverContext
83
84 notBusyObserver = CFRunLoopObserverCreate(
85 NULL, // CFAllocator
86 kCFRunLoopBeforeWaiting, // CFOptionFlags
87 true, // repeats
88 NSIntegerMin, // order
89 &NotBusyObserver, // CFRunLoopObserverCallBack
90 NULL); // CFRunLoopObserverContext
91
92 CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
93 CFRunLoopAddObserver(runLoop, busyObserver, kCFRunLoopDefaultMode);
94 CFRunLoopAddObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
95
96 CFRelease(busyObserver);
97 CFRelease(notBusyObserver);
98
99 if (!headless) setBusy(YES);
100
101 // Set the java name of the AppKit main thread appropriately.
102 jclass threadClass = NULL;
103 jstring name = NULL;
104 jobject curThread = NULL;
105
106 if (!swt_mode) {
107 threadClass = (*env)->FindClass(env, "java/lang/Thread");
108 if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
109 jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;");
110 if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
111 jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V");
112 if (setName == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
113
114 curThread = (*env)->CallStaticObjectMethod(env, threadClass, currentThreadID); // AWT_THREADING Safe (known object)
115 if (curThread == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
116 name = (*env)->NewStringUTF(env, "AWT-AppKit");
117 if (name == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
118 (*env)->CallVoidMethod(env, curThread, setName, name); // AWT_THREADING Safe (known object)
119 if ((*env)->ExceptionCheck(env)) goto cleanup;
120 }
121
122 cleanup:
123 if (threadClass != NULL) {
124 (*env)->DeleteLocalRef(env, threadClass);
125 }
126 if (name != NULL) {
127 (*env)->DeleteLocalRef(env, name);
128 }
129 if (curThread != NULL) {
130 (*env)->DeleteLocalRef(env, curThread);
131 }
132 if ((*env)->ExceptionCheck(env)) {
133 (*env)->ExceptionDescribe(env);
134 (*env)->ExceptionClear(env);
135 }
136
137 // Add the exception handler of last resort
138 NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler);
139
140 if (verbose) AWT_DEBUG_LOG(@"finished setting thread name");
141 }
142
143
144
145 // Returns true if java believes it is running headless
146 BOOL isHeadless(JNIEnv *env) {
147 // Just access the property directly, instead of using GraphicsEnvironment.isHeadless.
148 // This is because this may be called while AWT is being loaded, and calling AWT
149 // while it is being loaded will deadlock.
150 static JNF_CLASS_CACHE(jc_Toolkit, "java/awt/GraphicsEnvironment");
151 static JNF_STATIC_MEMBER_CACHE(jm_isHeadless, jc_Toolkit, "isHeadless", "()Z");
152 return JNFCallStaticBooleanMethod(env, jm_isHeadless);
153 }
154
155 BOOL isSWTInWebStart(JNIEnv* env) {
156 NSString *swtWebStart = [PropertiesUtilities javaSystemPropertyForKey:@"com.apple.javaws.usingSWT" withEnv:env];
157 return [@"true" isCaseInsensitiveLike:swtWebStart];
158 }
159
160 void setBusy(BOOL busy) {
161 AWT_ASSERT_APPKIT_THREAD;
162
163 JNIEnv *env = [ThreadUtilities getJNIEnv];
164 static JNF_CLASS_CACHE(jc_AWTAutoShutdown, "sun/awt/AWTAutoShutdown");
165
166 if (busy) {
167 static JNF_STATIC_MEMBER_CACHE(jm_notifyBusyMethod, jc_AWTAutoShutdown, "notifyToolkitThreadBusy", "()V");
168 JNFCallStaticVoidMethod(env, jm_notifyBusyMethod);
169 } else {
170 static JNF_STATIC_MEMBER_CACHE(jm_notifyFreeMethod, jc_AWTAutoShutdown, "notifyToolkitThreadFree", "()V");
171 JNFCallStaticVoidMethod(env, jm_notifyFreeMethod);
172 }
173 }
174
175 static void BusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg) {
176 AWT_ASSERT_APPKIT_THREAD;
177
178 // This is only called with the selector kCFRunLoopAfterWaiting.
179 #ifndef PRODUCT_BUILD
183 setBusy(YES);
184 }
185
186 static void NotBusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg) {
187 AWT_ASSERT_APPKIT_THREAD;
188
189 // This is only called with the selector kCFRunLoopBeforeWaiting.
190 #ifndef PRODUCT_BUILD
191 assert(what == kCFRunLoopBeforeWaiting);
192 #endif /* PRODUCT_BUILD */
193
194 setBusy(NO);
195 }
196
197 static void AWT_NSUncaughtExceptionHandler(NSException *exception) {
198 NSLog(@"Apple AWT Internal Exception: %@", [exception description]);
199 }
200
201 // This is an empty Obj-C object just so that -peformSelectorOnMainThread can be used.
202 @interface AWTStarter : NSObject { }
203 + (void)start:(BOOL)headless swtMode:(BOOL)swtMode swtModeForWebStart:(BOOL)swtModeForWebStart;
204 - (void)starter:(NSArray*)args;
205 + (void)appKitIsRunning:(id)arg;
206 @end
207
208 @implementation AWTStarter
209
210 + (BOOL) isConnectedToWindowServer {
211 SecuritySessionId session_id;
212 SessionAttributeBits session_info;
213 OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info);
214 if (status != noErr) return NO;
215 if (!(session_info & sessionHasGraphicAccess)) return NO;
216 return YES;
217 }
218
219 + (BOOL) markAppAsDaemon {
220 id jrsAppKitAWTClass = objc_getClass("JRSAppKitAWT");
221 SEL markAppSel = @selector(markAppIsDaemon);
222 if (![jrsAppKitAWTClass respondsToSelector:markAppSel]) return NO;
223 return (BOOL)[jrsAppKitAWTClass performSelector:markAppSel];
225
226 + (void)appKitIsRunning:(id)arg {
227 // Headless: NO
228 // Embedded: BOTH
229 // Multiple Calls: NO
230 // Callers: AppKit's NSApplicationDidFinishLaunchingNotification or +[AWTStarter startAWT:]
231 AWT_ASSERT_APPKIT_THREAD;
232
233 BOOL verbose = ShouldPrintVerboseDebugging();
234 if (verbose) AWT_DEBUG_LOG(@"about to message AppKit started");
235
236 // Signal that AppKit has started (or is already running).
237 pthread_mutex_lock(&sAppKitStarted_mutex);
238 sAppKitStarted = YES;
239 pthread_cond_signal(&sAppKitStarted_cv);
240 pthread_mutex_unlock(&sAppKitStarted_mutex);
241
242 if (verbose) AWT_DEBUG_LOG(@"finished messaging AppKit started");
243 }
244
245 + (void)start:(BOOL)headless swtMode:(BOOL)swtMode swtModeForWebStart:(BOOL)swtModeForWebStart
246 {
247 BOOL verbose = ShouldPrintVerboseDebugging();
248
249 // Headless: BOTH
250 // Embedded: BOTH
251 // Multiple Calls: NO
252 // Caller: JNI_OnLoad
253
254 // onMainThread is NOT the same at SWT mode!
255 // If the JVM was started on the first thread for SWT, but the SWT loads the AWT on a secondary thread,
256 // onMainThread here will be false but SWT mode will be true. If we are currently on the main thread, we don't
257 // need to throw AWT startup over to another thread.
258 BOOL onMainThread = (pthread_main_np() != 0);
259
260 if (verbose) {
261 NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d swtMode:%d swtModeForWebStart:%d] { onMainThread:%d }", headless, swtMode, swtModeForWebStart, onMainThread];
262 AWT_DEBUG_LOG(msg);
263 }
264
265 if (!headless)
266 {
267 // Listen for the NSApp to start. This indicates that JNI_OnLoad can proceed.
268 // It must wait because there is a chance that another java thread will grab
269 // the AppKit lock before the +[NSApplication sharedApplication] returns.
270 // See <rdar://problem/3492666> for an example.
271 [[NSNotificationCenter defaultCenter] addObserver:[AWTStarter class]
272 selector:@selector(appKitIsRunning:)
273 name:NSApplicationDidFinishLaunchingNotification
274 object:nil];
275
276 if (verbose) NSLog(@"+[AWTStarter start:::]: registered NSApplicationDidFinishLaunchingNotification");
277 }
278
279 id st = [[AWTStarter alloc] init];
280
281 NSArray * args = [NSArray arrayWithObjects:
282 [NSNumber numberWithBool: onMainThread],
283 [NSNumber numberWithBool: swtMode],
284 [NSNumber numberWithBool: headless],
285 [NSNumber numberWithBool: swtModeForWebStart],
286 [NSNumber numberWithBool: verbose],
287 nil];
288
289 if (onMainThread) {
290 [st starter:args];
291 } else {
292 [st performSelectorOnMainThread: @selector(starter:) withObject:args waitUntilDone:NO];
293 }
294
295 if (!headless && !onMainThread) {
296 if (verbose) AWT_DEBUG_LOG(@"about to wait on AppKit startup mutex");
297
298 // Wait here for AppKit to have started (or for AWT to have been loaded into
299 // an already running NSApplication).
300 pthread_mutex_lock(&sAppKitStarted_mutex);
301 while (sAppKitStarted == NO) {
302 pthread_cond_wait(&sAppKitStarted_cv, &sAppKitStarted_mutex);
303 }
304 pthread_mutex_unlock(&sAppKitStarted_mutex);
305
307 if (verbose) AWT_DEBUG_LOG(@"got out of the AppKit startup mutex");
308 }
309
310 // Don't set the delegate until the NSApplication has been created and
311 // its finishLaunching has initialized it.
312 // ApplicationDelegate is the support code for com.apple.eawt.
313 void (^setDelegateBlock)() = ^(){
314 OSXAPP_SetApplicationDelegate([ApplicationDelegate sharedDelegate]);
315 };
316 if (onMainThread) {
317 setDelegateBlock();
318 } else {
319 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:setDelegateBlock];
320 }
321 }
322
323 - (void)starter:(NSArray*)args {
324 NSAutoreleasePool *pool = [NSAutoreleasePool new];
325
326 BOOL onMainThread = [[args objectAtIndex:0] boolValue];
327 BOOL swtMode = [[args objectAtIndex:1] boolValue];
328 BOOL headless = [[args objectAtIndex:2] boolValue];
329 BOOL swtModeForWebStart = [[args objectAtIndex:3] boolValue];
330 BOOL verbose = [[args objectAtIndex:4] boolValue];
331
332 BOOL wasOnMainThread = onMainThread;
333
334 setUpAWTAppKit(swtMode, headless);
335
336 // Headless mode trumps either ordinary AWT or SWT-in-AWT mode. Declare us a daemon and return.
337 if (headless) {
338 BOOL didBecomeDaemon = [AWTStarter markAppAsDaemon];
339 return;
340 }
341
342 if (swtMode || swtModeForWebStart) {
343 if (verbose) NSLog(@"in SWT or SWT/WebStart mode");
344
345 // The SWT should call NSApplicationLoad, but they don't know a priori that they will be using the AWT, so they don't.
346 NSApplicationLoad();
347 }
348
349 // This will create a NSApplicationAWT for standalone AWT programs, unless there is
350 // already a NSApplication instance. If there is already a NSApplication instance,
351 // and -[NSApplication isRunning] returns YES, AWT is embedded inside another
352 // AppKit Application.
353 NSApplication *app = [NSApplicationAWT sharedApplication];
354
355 // AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent.
356 if (![app isRunning]) {
357 if (verbose) AWT_DEBUG_LOG(@"+[AWTStarter startAWT]: ![app isRunning]");
358
359 // This is where the AWT AppKit thread parks itself to process events.
360 [NSApplicationAWT runAWTLoopWithApp: app];
361 } else {
362 // We're either embedded, or showing a splash screen
363 if (![NSApp isKindOfClass:[NSApplicationAWT class]]) {
364 if (verbose) AWT_DEBUG_LOG(@"running embedded");
365
366 // Since we're embedded, no need to be swamping the runloop with the observers.
367 CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
368 CFRunLoopRemoveObserver(runLoop, busyObserver, kCFRunLoopDefaultMode);
369 CFRunLoopRemoveObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
370 // We don't track if the runloop is busy, so set it free to let AWT finish when it needs
371 setBusy(NO);
372 busyObserver = NULL;
373 notBusyObserver = NULL;
374 } else {
375 if (verbose) AWT_DEBUG_LOG(@"running after showing a splash screen");
376 }
377
378 // Signal so that JNI_OnLoad can proceed.
379 if (!wasOnMainThread) [AWTStarter appKitIsRunning:nil];
380
381 // Proceed to exit this call as there is no reason to run the NSApplication event loop.
382 }
383
384 [pool drain];
385 }
386
387 @end
388
389
390 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
391 BOOL verbose = ShouldPrintVerboseDebugging();
392 if (verbose) AWT_DEBUG_LOG(@"entered JNI_OnLoad");
393
394 // Headless: BOTH
395 // Embedded: BOTH
396 // Multiple Calls: NO
397 // Caller: JavaVM classloader
398
399 // Keep a static reference for other archives.
400 OSXAPP_SetJavaVM(vm);
401
402 JNIEnv *env = NULL;
403
404 // Need JNIEnv for JNF_COCOA_ENTER(env); macro below
405 jint status = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4);
406 if (status != JNI_OK || env == NULL) {
407 AWT_DEBUG_LOG(@"Can't get JNIEnv");
408 return JNI_VERSION_1_4;
409 }
410
411 // The following is true when AWT is attempting to connect to the window server
412 // when it isn't set up properly to do so.
413 // BOOL AWTLoadFailure = YES; For now we are skipping this check so i'm commenting out this variable as unused
414 JNF_COCOA_ENTER(env);
415
416 // If -XstartOnFirstThread was used at invocation time, an environment variable will be set.
417 // (See java_md.c for the matching setenv call.) When that happens, we assume the SWT will be in use.
418 BOOL swt_compatible_mode = NO;
419
420 char envVar[80];
421 snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
422 if (getenv(envVar) != NULL) {
423 swt_compatible_mode = YES;
424 unsetenv(envVar);
425 }
426
427 BOOL swt_in_webstart = isSWTInWebStart(env);
428 BOOL headless = isHeadless(env);
429
430 // Make sure we're on the right thread. If not, we still need the JNIEnv to throw an exception.
431 if (pthread_main_np() != 0 && !swt_compatible_mode && !headless) {
432 AWT_DEBUG_LOG(@"Apple AWT Java VM was loaded on first thread -- can't start AWT.");
433 [JNFException raise:env as:kInternalError reason:"Can't start the AWT because Java was started on the first thread. Make sure StartOnFirstThread is "
434 "not specified in your application's Info.plist or on the command line"];
435 return JNI_VERSION_1_4;
436 }
437
438 // We need to let Foundation know that this is a multithreaded application, if it isn't already.
439 if (![NSThread isMultiThreaded]) {
440 [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil];
441 }
442
443 // if (swt_compatible_mode || headless || [AWTStarter isConnectedToWindowServer] || [AWTStarter isRemoteSession]) {
444 // No need in this check - we will try to launch AWTStarter anyways - to be able to run GUI application remotely
445 // AWTLoadFailure = NO;
446
447 [AWTStarter start:headless swtMode:swt_compatible_mode swtModeForWebStart:swt_in_webstart];
448
449 // }
450
451 /* if (AWTLoadFailure) { // We will not reach this code anyways
452 [JNFException raise:env as:kInternalError reason:"Can't connect to window server - not enough permissions."];
453 } */
454
455 JNF_COCOA_EXIT(env);
456
457 if (verbose) AWT_DEBUG_LOG(@"exiting JNI_OnLoad");
458
459 return JNI_VERSION_1_4;
460 }
|
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 static bool ShouldPrintVerboseDebugging() {
49 static int debug = -1;
50 if (debug == -1) {
51 debug = (int)(getenv("JAVA_AWT_VERBOSE") != NULL) || (DEBUG != 0);
52 }
53 return (bool)debug;
54 }
55
56 // This is the data necessary to have JNI_OnLoad wait for AppKit to start.
57 static BOOL sAppKitStarted = NO;
58 static pthread_mutex_t sAppKitStarted_mutex = PTHREAD_MUTEX_INITIALIZER;
59 static pthread_cond_t sAppKitStarted_cv = PTHREAD_COND_INITIALIZER;
60
61 void setBusy(BOOL isBusy);
62 static void BusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg);
63 static void NotBusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg);
64 static void AWT_NSUncaughtExceptionHandler(NSException *exception);
65
66 static CFRunLoopObserverRef busyObserver = NULL;
67 static CFRunLoopObserverRef notBusyObserver = NULL;
68
69 static void setUpAWTAppKit(JNIEnv *env)
70 {
71 // Add CFRunLoopObservers to call into AWT so that AWT knows that the
72 // AWT thread (which is the AppKit main thread) is alive. This way AWT
73 // will not automatically shutdown.
74 busyObserver = CFRunLoopObserverCreate(
75 NULL, // CFAllocator
76 kCFRunLoopAfterWaiting, // CFOptionFlags
77 true, // repeats
78 NSIntegerMax, // order
79 &BusyObserver, // CFRunLoopObserverCallBack
80 NULL); // CFRunLoopObserverContext
81
82 notBusyObserver = CFRunLoopObserverCreate(
83 NULL, // CFAllocator
84 kCFRunLoopBeforeWaiting, // CFOptionFlags
85 true, // repeats
86 NSIntegerMin, // order
87 &NotBusyObserver, // CFRunLoopObserverCallBack
88 NULL); // CFRunLoopObserverContext
89
90 CFRunLoopRef runLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
91 CFRunLoopAddObserver(runLoop, busyObserver, kCFRunLoopDefaultMode);
92 CFRunLoopAddObserver(runLoop, notBusyObserver, kCFRunLoopDefaultMode);
93
94 CFRelease(busyObserver);
95 CFRelease(notBusyObserver);
96
97 setBusy(YES);
98
99
100 // Set the java name of the AppKit main thread appropriately.
101 jclass threadClass = NULL;
102 jstring name = NULL;
103 jobject curThread = NULL;
104
105 threadClass = (*env)->FindClass(env, "java/lang/Thread");
106 if (threadClass == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
107 jmethodID currentThreadID = (*env)->GetStaticMethodID(env, threadClass, "currentThread", "()Ljava/lang/Thread;");
108 if (currentThreadID == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
109 jmethodID setName = (*env)->GetMethodID(env, threadClass, "setName", "(Ljava/lang/String;)V");
110 if (setName == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
111
112 curThread = (*env)->CallStaticObjectMethod(env, threadClass, currentThreadID); // AWT_THREADING Safe (known object)
113 if (curThread == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
114 name = (*env)->NewStringUTF(env, "AWT-AppKit");
115 if (name == NULL || (*env)->ExceptionCheck(env)) goto cleanup;
116 (*env)->CallVoidMethod(env, curThread, setName, name); // AWT_THREADING Safe (known object)
117 if ((*env)->ExceptionCheck(env)) goto cleanup;
118
119 cleanup:
120 if (threadClass != NULL) {
121 (*env)->DeleteLocalRef(env, threadClass);
122 }
123 if (name != NULL) {
124 (*env)->DeleteLocalRef(env, name);
125 }
126 if (curThread != NULL) {
127 (*env)->DeleteLocalRef(env, curThread);
128 }
129 if ((*env)->ExceptionCheck(env)) {
130 (*env)->ExceptionDescribe(env);
131 (*env)->ExceptionClear(env);
132 }
133 }
134
135
136 // Returns true if java believes it is running headless
137 BOOL isHeadless(JNIEnv *env) {
138 // Just access the property directly, instead of using GraphicsEnvironment.isHeadless.
139 // This is because this may be called while AWT is being loaded, and calling AWT
140 // while it is being loaded will deadlock.
141 static JNF_CLASS_CACHE(jc_Toolkit, "java/awt/GraphicsEnvironment");
142 static JNF_STATIC_MEMBER_CACHE(jm_isHeadless, jc_Toolkit, "isHeadless", "()Z");
143 return JNFCallStaticBooleanMethod(env, jm_isHeadless);
144 }
145
146 void setBusy(BOOL busy) {
147 AWT_ASSERT_APPKIT_THREAD;
148
149 JNIEnv *env = [ThreadUtilities getJNIEnv];
150 static JNF_CLASS_CACHE(jc_AWTAutoShutdown, "sun/awt/AWTAutoShutdown");
151
152 if (busy) {
153 static JNF_STATIC_MEMBER_CACHE(jm_notifyBusyMethod, jc_AWTAutoShutdown, "notifyToolkitThreadBusy", "()V");
154 JNFCallStaticVoidMethod(env, jm_notifyBusyMethod);
155 } else {
156 static JNF_STATIC_MEMBER_CACHE(jm_notifyFreeMethod, jc_AWTAutoShutdown, "notifyToolkitThreadFree", "()V");
157 JNFCallStaticVoidMethod(env, jm_notifyFreeMethod);
158 }
159 }
160
161 static void BusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg) {
162 AWT_ASSERT_APPKIT_THREAD;
163
164 // This is only called with the selector kCFRunLoopAfterWaiting.
165 #ifndef PRODUCT_BUILD
169 setBusy(YES);
170 }
171
172 static void NotBusyObserver(CFRunLoopObserverRef ref, CFRunLoopActivity what, void* arg) {
173 AWT_ASSERT_APPKIT_THREAD;
174
175 // This is only called with the selector kCFRunLoopBeforeWaiting.
176 #ifndef PRODUCT_BUILD
177 assert(what == kCFRunLoopBeforeWaiting);
178 #endif /* PRODUCT_BUILD */
179
180 setBusy(NO);
181 }
182
183 static void AWT_NSUncaughtExceptionHandler(NSException *exception) {
184 NSLog(@"Apple AWT Internal Exception: %@", [exception description]);
185 }
186
187 // This is an empty Obj-C object just so that -peformSelectorOnMainThread can be used.
188 @interface AWTStarter : NSObject { }
189 + (void)start:(BOOL)headless;
190 - (void)starter:(NSArray*)args;
191 + (void)appKitIsRunning:(id)arg;
192 @end
193
194 @implementation AWTStarter
195
196 + (BOOL) isConnectedToWindowServer {
197 SecuritySessionId session_id;
198 SessionAttributeBits session_info;
199 OSStatus status = SessionGetInfo(callerSecuritySession, &session_id, &session_info);
200 if (status != noErr) return NO;
201 if (!(session_info & sessionHasGraphicAccess)) return NO;
202 return YES;
203 }
204
205 + (BOOL) markAppAsDaemon {
206 id jrsAppKitAWTClass = objc_getClass("JRSAppKitAWT");
207 SEL markAppSel = @selector(markAppIsDaemon);
208 if (![jrsAppKitAWTClass respondsToSelector:markAppSel]) return NO;
209 return (BOOL)[jrsAppKitAWTClass performSelector:markAppSel];
211
212 + (void)appKitIsRunning:(id)arg {
213 // Headless: NO
214 // Embedded: BOTH
215 // Multiple Calls: NO
216 // Callers: AppKit's NSApplicationDidFinishLaunchingNotification or +[AWTStarter startAWT:]
217 AWT_ASSERT_APPKIT_THREAD;
218
219 BOOL verbose = ShouldPrintVerboseDebugging();
220 if (verbose) AWT_DEBUG_LOG(@"about to message AppKit started");
221
222 // Signal that AppKit has started (or is already running).
223 pthread_mutex_lock(&sAppKitStarted_mutex);
224 sAppKitStarted = YES;
225 pthread_cond_signal(&sAppKitStarted_cv);
226 pthread_mutex_unlock(&sAppKitStarted_mutex);
227
228 if (verbose) AWT_DEBUG_LOG(@"finished messaging AppKit started");
229 }
230
231 + (void)start:(BOOL)headless
232 {
233 BOOL verbose = ShouldPrintVerboseDebugging();
234
235 // Headless: BOTH
236 // Embedded: BOTH
237 // Multiple Calls: NO
238 // Caller: JNI_OnLoad
239
240 // onMainThread is NOT the same at SWT mode!
241 // If the JVM was started on the first thread for SWT, but the SWT loads the AWT on a secondary thread,
242 // onMainThread here will be false but SWT mode will be true. If we are currently on the main thread, we don't
243 // need to throw AWT startup over to another thread.
244 BOOL onMainThread = (pthread_main_np() != 0);
245
246 if (verbose) {
247 NSString *msg = [NSString stringWithFormat:@"+[AWTStarter start headless:%d] { onMainThread:%d }", headless, onMainThread];
248 AWT_DEBUG_LOG(msg);
249 }
250
251 if (!headless)
252 {
253 // Listen for the NSApp to start. This indicates that JNI_OnLoad can proceed.
254 // It must wait because there is a chance that another java thread will grab
255 // the AppKit lock before the +[NSApplication sharedApplication] returns.
256 // See <rdar://problem/3492666> for an example.
257 [[NSNotificationCenter defaultCenter] addObserver:[AWTStarter class]
258 selector:@selector(appKitIsRunning:)
259 name:NSApplicationDidFinishLaunchingNotification
260 object:nil];
261
262 if (verbose) NSLog(@"+[AWTStarter start:::]: registered NSApplicationDidFinishLaunchingNotification");
263 }
264
265 id st = [[AWTStarter alloc] init];
266
267 NSArray * args = [NSArray arrayWithObjects:
268 [NSNumber numberWithBool: onMainThread],
269 [NSNumber numberWithBool: headless],
270 [NSNumber numberWithBool: verbose],
271 nil];
272
273 if (onMainThread) {
274 [st starter:args];
275 } else {
276 [st performSelectorOnMainThread: @selector(starter:) withObject:args waitUntilDone:NO];
277 }
278
279 if (!headless && !onMainThread) {
280 if (verbose) AWT_DEBUG_LOG(@"about to wait on AppKit startup mutex");
281
282 // Wait here for AppKit to have started (or for AWT to have been loaded into
283 // an already running NSApplication).
284 pthread_mutex_lock(&sAppKitStarted_mutex);
285 while (sAppKitStarted == NO) {
286 pthread_cond_wait(&sAppKitStarted_cv, &sAppKitStarted_mutex);
287 }
288 pthread_mutex_unlock(&sAppKitStarted_mutex);
289
291 if (verbose) AWT_DEBUG_LOG(@"got out of the AppKit startup mutex");
292 }
293
294 // Don't set the delegate until the NSApplication has been created and
295 // its finishLaunching has initialized it.
296 // ApplicationDelegate is the support code for com.apple.eawt.
297 void (^setDelegateBlock)() = ^(){
298 OSXAPP_SetApplicationDelegate([ApplicationDelegate sharedDelegate]);
299 };
300 if (onMainThread) {
301 setDelegateBlock();
302 } else {
303 [JNFRunLoop performOnMainThreadWaiting:YES withBlock:setDelegateBlock];
304 }
305 }
306
307 - (void)starter:(NSArray*)args {
308 NSAutoreleasePool *pool = [NSAutoreleasePool new];
309
310 BOOL onMainThread = [[args objectAtIndex:0] boolValue];
311 BOOL headless = [[args objectAtIndex:1] boolValue];
312 BOOL verbose = [[args objectAtIndex:2] boolValue];
313
314 BOOL wasOnMainThread = onMainThread;
315
316 // Add the exception handler of last resort
317 NSSetUncaughtExceptionHandler(AWT_NSUncaughtExceptionHandler);
318
319 // Headless mode trumps either ordinary AWT or SWT-in-AWT mode. Declare us a daemon and return.
320 if (headless) {
321 BOOL didBecomeDaemon = [AWTStarter markAppAsDaemon];
322 return;
323 }
324
325 // This will create a NSApplicationAWT for standalone AWT programs, unless there is
326 // already a NSApplication instance. If there is already a NSApplication instance,
327 // and -[NSApplication isRunning] returns YES, AWT is embedded inside another
328 // AppKit Application.
329 NSApplication *app = [NSApplicationAWT sharedApplication];
330 isEmbedded = ![NSApp isKindOfClass:[NSApplicationAWT class]];
331
332 JNIEnv *env = [ThreadUtilities getJNIEnv];
333 if (isEmbedded) {
334 static JNF_CLASS_CACHE(jc_AWTKeepAlive, "sun/lwawt/macosx/AWTKeepAlive");
335 static JNF_STATIC_MEMBER_CACHE(jm_activate, jc_AWTKeepAlive, "activate", "()V");
336
337 // This will send native events every 0.5 s, thus letting the embedder know
338 // that AWT is alive. The events are only sent while there are active
339 // AWT windows present.
340 JNFCallStaticVoidMethod(env, jm_activate);
341 } else {
342 // Install run loop observers and set the AppKit Java thread name
343 setUpAWTAppKit(env);
344 }
345
346 // AWT gets to this point BEFORE NSApplicationDidFinishLaunchingNotification is sent.
347 if (![app isRunning]) {
348 if (verbose) AWT_DEBUG_LOG(@"+[AWTStarter startAWT]: ![app isRunning]");
349
350 // Note that in theory we can get here even if we're embedded and an
351 // embedder isn't running an event loop yet for some reason. However,
352 // it seems to be very unlikely. In any case, the runAWTLoopWithApp is
353 // able to deal with it anyway. Whether the embedder can deal with
354 // this is unknown. Also, it's unclear how the event loop is going to
355 // be terminated in this case. Setting up observers is not an option
356 // since the embedder could force termination. We wouldn't know and
357 // could crash when calling to Java.
358
359 // This is where the AWT AppKit thread parks itself to process events.
360 [NSApplicationAWT runAWTLoopWithApp: app];
361 } else {
362 // We're either embedded, or showing a splash screen
363 if (isEmbedded) {
364 if (verbose) AWT_DEBUG_LOG(@"running embedded");
365
366 // We don't track if the runloop is busy, so set it free to let AWT finish when it needs
367 setBusy(NO);
368 } else {
369 if (verbose) AWT_DEBUG_LOG(@"running after showing a splash screen");
370 }
371
372 // Signal so that JNI_OnLoad can proceed.
373 if (!wasOnMainThread) [AWTStarter appKitIsRunning:nil];
374
375 // Proceed to exit this call as there is no reason to run the NSApplication event loop.
376 }
377
378 [pool drain];
379 }
380
381 @end
382
383
384 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
385 BOOL verbose = ShouldPrintVerboseDebugging();
386 if (verbose) AWT_DEBUG_LOG(@"entered JNI_OnLoad");
387
388 // Headless: BOTH
389 // Embedded: BOTH
390 // Multiple Calls: NO
391 // Caller: JavaVM classloader
392
393 // Keep a static reference for other archives.
394 OSXAPP_SetJavaVM(vm);
395
396 JNIEnv *env = NULL;
397
398 // Need JNIEnv for JNF_COCOA_ENTER(env); macro below
399 jint status = (*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4);
400 if (status != JNI_OK || env == NULL) {
401 AWT_DEBUG_LOG(@"Can't get JNIEnv");
402 return JNI_VERSION_1_4;
403 }
404
405 JNF_COCOA_ENTER(env);
406
407 char envVar[80];
408 snprintf(envVar, sizeof(envVar), "JAVA_STARTED_ON_FIRST_THREAD_%d", getpid());
409 if (getenv(envVar) != NULL) {
410 // If we don't need this in the long term, it makes sense to not set
411 // this env variable in the launcher code in the first place
412 unsetenv(envVar);
413 }
414
415 BOOL headless = isHeadless(env);
416
417 // We need to let Foundation know that this is a multithreaded application, if it isn't already.
418 if (![NSThread isMultiThreaded]) {
419 [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil];
420 }
421
422 [AWTStarter start:headless];
423
424 JNF_COCOA_EXIT(env);
425
426 if (verbose) AWT_DEBUG_LOG(@"exiting JNI_OnLoad");
427
428 return JNI_VERSION_1_4;
429 }
|