[iOS] Generate a simulated crash when the WebThread starts in MobileSafari
[WebKit.git] / Source / WebCore / platform / ios / wak / WebCoreThread.mm
1 /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WebCoreThread.h"
28
29 #if PLATFORM(IOS)
30
31 #import "CommonVM.h"
32 #import "FloatingPointEnvironment.h"
33 #import "RuntimeApplicationChecks.h"
34 #import "ThreadGlobalData.h"
35 #import "WAKWindow.h"
36 #import "WebCoreThreadInternal.h"
37 #import "WebCoreThreadMessage.h"
38 #import "WebCoreThreadRun.h"
39 #import "WKUtilities.h"
40
41 #import <Foundation/NSInvocation.h>
42 #import <libkern/OSAtomic.h>
43 #import <objc/runtime.h>
44 #import <runtime/InitializeThreading.h>
45 #import <runtime/JSLock.h>
46 #import <unistd.h>
47 #import <wtf/Assertions.h>
48 #import <wtf/MainThread.h>
49 #import <wtf/RunLoop.h>
50 #import <wtf/Threading.h>
51 #import <wtf/text/AtomicString.h>
52
53 #import "CrashReporterSupportSoftLink.h"
54
55 #define LOG_MESSAGES 0
56 #define LOG_WEB_LOCK 0
57 #define LOG_MAIN_THREAD_LOCKING 0
58 #define LOG_RELEASES 0
59
60 #define DistantFuture   (86400.0 * 2000 * 365.2425 + 86400.0)   // same as +[NSDate distantFuture]
61 #define MaxArgCount         5
62 #define DrawWaitInterval     10
63 #define DelegateWaitInterval 10
64
65 static void _WebThreadAutoLock(void);
66 static bool _WebTryThreadLock(bool shouldTry);
67 static void _WebThreadLockFromAnyThread(bool shouldLog);
68 static void _WebThreadUnlock(void);
69
70 @interface NSObject(ForwardDeclarations)
71 -(void)_webcore_releaseOnWebThread;
72 -(void)_webcore_releaseWithWebThreadLock;
73 @end
74
75 @implementation NSObject(WebCoreThreadAdditions)
76
77 - (void)releaseOnMainThread {
78     if ([NSThread isMainThread]) {
79         [self release];
80     } else {
81         [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
82     }
83 }
84
85 @end
86
87 typedef void *NSAutoreleasePoolMark;
88 #ifdef __cplusplus
89 extern "C" {
90 #endif
91 extern NSAutoreleasePoolMark NSPushAutoreleasePool(unsigned ignored);
92 extern void NSPopAutoreleasePool(NSAutoreleasePoolMark token);
93 #ifdef __cplusplus
94 }
95 #endif
96
97 static int WebTimedConditionLock (pthread_cond_t *condition, pthread_mutex_t *lock, CFAbsoluteTime interval);
98
99 static pthread_mutex_t webLock;
100 static NSAutoreleasePoolMark autoreleasePoolMark;
101 static CFRunLoopRef webThreadRunLoop;
102 static NSRunLoop* webThreadNSRunLoop;
103 static pthread_t webThread;
104 static BOOL isWebThreadLocked;
105 static BOOL webThreadStarted;
106 static unsigned webThreadLockCount;
107
108 static NSAutoreleasePoolMark savedAutoreleasePoolMark;
109 static BOOL isNestedWebThreadRunLoop;
110 typedef enum {
111     PushOrPopAutoreleasePool,
112     IgnoreAutoreleasePool
113 } AutoreleasePoolOperation;
114
115 static pthread_mutex_t WebThreadReleaseLock = PTHREAD_MUTEX_INITIALIZER;
116 static CFRunLoopSourceRef WebThreadReleaseSource;
117 static CFMutableArrayRef WebThreadReleaseObjArray;
118
119 static void MainThreadAdoptAndRelease(id obj);
120
121 static pthread_mutex_t delegateLock = PTHREAD_MUTEX_INITIALIZER;
122 static pthread_cond_t delegateCondition = PTHREAD_COND_INITIALIZER;
123 static NSInvocation *delegateInvocation;
124 static CFRunLoopSourceRef delegateSource = NULL;
125 static BOOL delegateHandled;
126 #if LOG_MAIN_THREAD_LOCKING
127 static BOOL sendingDelegateMessage;
128 #endif
129
130 static CFRunLoopObserverRef mainRunLoopAutoUnlockObserver;
131
132 static pthread_mutex_t startupLock = PTHREAD_MUTEX_INITIALIZER;
133 static pthread_cond_t startupCondition = PTHREAD_COND_INITIALIZER;
134
135 static WebThreadContext *webThreadContext;
136 static pthread_key_t threadContextKey;
137 static unsigned mainThreadLockCount;
138 static unsigned otherThreadLockCount;
139 static unsigned sMainThreadModalCount;
140
141 WEBCORE_EXPORT volatile bool webThreadShouldYield;
142
143 static pthread_mutex_t WebCoreReleaseLock;
144 static void WebCoreObjCDeallocOnWebThreadImpl(id self, SEL _cmd);
145 static void WebCoreObjCDeallocWithWebThreadLock(Class cls);
146 static void WebCoreObjCDeallocWithWebThreadLockImpl(id self, SEL _cmd);
147
148 static NSMutableArray *sAsyncDelegates = nil;
149
150 static CFStringRef delegateSourceRunLoopMode;
151
152 static inline void SendMessage(NSInvocation *invocation)
153 {
154     [invocation invoke];
155     MainThreadAdoptAndRelease(invocation);
156 }
157
158 static void HandleDelegateSource(void *info)
159 {
160     UNUSED_PARAM(info);
161     ASSERT(!WebThreadIsCurrent());
162
163 #if LOG_MAIN_THREAD_LOCKING
164     sendingDelegateMessage = YES;
165 #endif
166
167     _WebThreadAutoLock();
168
169     int result = pthread_mutex_lock(&delegateLock);
170     ASSERT_WITH_MESSAGE(result == 0, "delegate lock failed with code:%d", result);
171
172 #if LOG_MESSAGES
173     if ([[delegateInvocation target] isKindOfClass:[NSNotificationCenter class]]) {
174         id argument0;
175         [delegateInvocation getArgument:&argument0 atIndex:0];
176         NSLog(@"notification receive: %@", argument0);
177     } else {
178         NSLog(@"delegate receive: %@", NSStringFromSelector([delegateInvocation selector]));
179     }
180 #endif
181
182     SendMessage(delegateInvocation);
183
184     delegateHandled = YES;
185     pthread_cond_signal(&delegateCondition);
186
187     result = pthread_mutex_unlock(&delegateLock);
188     ASSERT_WITH_MESSAGE(result == 0, "delegate unlock failed with code:%d", result);
189
190 #if LOG_MAIN_THREAD_LOCKING
191     sendingDelegateMessage = NO;
192 #endif
193 }
194
195 static void SendDelegateMessage(NSInvocation *invocation)
196 {
197     if (WebThreadIsCurrent()) {
198         ASSERT(delegateSource);
199         int result = pthread_mutex_lock(&delegateLock);
200         ASSERT_WITH_MESSAGE(result == 0, "delegate lock failed with code:%d", result);
201
202         delegateInvocation = invocation;
203         delegateHandled = NO;
204
205 #if LOG_MESSAGES
206         if ([[delegateInvocation target] isKindOfClass:[NSNotificationCenter class]]) {
207             id argument0;
208             [delegateInvocation getArgument:&argument0 atIndex:0];
209             NSLog(@"notification send: %@", argument0);
210         } else {
211             NSLog(@"delegate send: %@", NSStringFromSelector([delegateInvocation selector]));
212         }
213 #endif
214
215         {
216             // Code block created to scope JSC::JSLock::DropAllLocks outside of WebThreadLock()
217             JSC::JSLock::DropAllLocks dropAllLocks(WebCore::commonVM());
218             _WebThreadUnlock();
219
220             CFRunLoopSourceSignal(delegateSource);
221             CFRunLoopWakeUp(CFRunLoopGetMain());
222
223             while (!delegateHandled) {
224                 if (WebTimedConditionLock(&delegateCondition, &delegateLock, DelegateWaitInterval) != 0) {
225                     id delegateInformation;
226                     if ([[delegateInvocation target] isKindOfClass:[NSNotificationCenter class]])
227                         [delegateInvocation getArgument:&delegateInformation atIndex:0];
228                     else
229                         delegateInformation = NSStringFromSelector([delegateInvocation selector]);
230         
231                     CFStringRef mode = CFRunLoopCopyCurrentMode(CFRunLoopGetMain());
232                     NSLog(@"%s: delegate (%@) failed to return after waiting %d seconds. main run loop mode: %@", __PRETTY_FUNCTION__, delegateInformation, DelegateWaitInterval, mode);
233                     if (mode)
234                         CFRelease(mode);
235                 }
236             }
237             result = pthread_mutex_unlock(&delegateLock);
238
239             ASSERT_WITH_MESSAGE(result == 0, "delegate unlock failed with code:%d", result);
240             _WebTryThreadLock(false);
241         }
242     } else {
243         SendMessage(invocation);
244     }
245 }
246
247 void WebThreadRunOnMainThread(void(^delegateBlock)())
248 {
249     if (!WebThreadIsCurrent()) {
250         ASSERT(pthread_main_np());
251         delegateBlock();
252         return;
253     }
254
255     JSC::JSLock::DropAllLocks dropAllLocks(WebCore::commonVM());
256     _WebThreadUnlock();
257
258     void (^delegateBlockCopy)() = Block_copy(delegateBlock);
259     dispatch_sync(dispatch_get_main_queue(), delegateBlockCopy);
260     Block_release(delegateBlockCopy);
261
262     _WebTryThreadLock(false);
263 }
264
265 static void MainThreadAdoptAndRelease(id obj)
266 {
267     if (!WebThreadIsEnabled() || CFRunLoopGetMain() == CFRunLoopGetCurrent()) {
268         [obj release];
269         return;
270     }
271 #if LOG_RELEASES
272     NSLog(@"Release send [web thread] : %@", obj);
273 #endif
274     // We own obj at this point, so we don't need the block to implicitly
275     // retain it.
276     __block id objNotRetained = obj;
277     dispatch_async(dispatch_get_main_queue(), ^{
278         [objNotRetained release];
279     });
280 }
281
282 void WebThreadAdoptAndRelease(id obj)
283 {
284     ASSERT(!WebThreadIsCurrent());
285     ASSERT(WebThreadReleaseSource);
286
287 #if LOG_RELEASES
288     NSLog(@"Release send [main thread]: %@", obj);
289 #endif        
290
291     int result = pthread_mutex_lock(&WebThreadReleaseLock);
292     ASSERT_WITH_MESSAGE(result == 0, "Release lock failed with code:%d", result);
293
294     if (WebThreadReleaseObjArray == nil)
295         WebThreadReleaseObjArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
296     CFArrayAppendValue(WebThreadReleaseObjArray, obj);
297     CFRunLoopSourceSignal(WebThreadReleaseSource);
298     CFRunLoopWakeUp(webThreadRunLoop);
299
300     result = pthread_mutex_unlock(&WebThreadReleaseLock);    
301     ASSERT_WITH_MESSAGE(result == 0, "Release unlock failed with code:%d", result);
302 }
303
304 static inline void lockWebCoreReleaseLock()
305 {
306     int lockcode = pthread_mutex_lock(&WebCoreReleaseLock);
307 #pragma unused (lockcode)
308     ASSERT_WITH_MESSAGE(lockcode == 0, "WebCoreReleaseLock lock failed with code:%d", lockcode);
309 }
310
311 static inline void unlockWebCoreReleaseLock()
312 {
313     int lockcode = pthread_mutex_unlock(&WebCoreReleaseLock);
314 #pragma unused (lockcode)
315     ASSERT_WITH_MESSAGE(lockcode == 0, "WebCoreReleaseLock unlock failed with code:%d", lockcode);
316 }
317
318 void WebCoreObjCDeallocOnWebThread(Class cls)
319 {
320     SEL releaseSEL = @selector(release);
321     SEL webThreadReleaseSEL = @selector(_webcore_releaseOnWebThread);
322
323     // get the existing release method
324     Method releaseMethod = class_getInstanceMethod(cls, releaseSEL);
325     if (!releaseMethod) {
326         ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocOnWebThread() failed to find %s for %@", releaseSEL, NSStringFromClass(cls));
327         return;
328     }
329
330     // add the implementation that ensures release WebThread release/deallocation
331     if (!class_addMethod(cls, webThreadReleaseSEL, (IMP)WebCoreObjCDeallocOnWebThreadImpl, method_getTypeEncoding(releaseMethod))) {
332         ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocOnWebThread() failed to add %s for %@", webThreadReleaseSEL, NSStringFromClass(cls));
333         return;
334     }
335
336     // ensure the implementation exists at cls in the class hierarchy
337     if (class_addMethod(cls, releaseSEL, class_getMethodImplementation(cls, releaseSEL), method_getTypeEncoding(releaseMethod)))
338         releaseMethod = class_getInstanceMethod(cls, releaseSEL);
339
340     // swizzle the old release for the new implementation
341     method_exchangeImplementations(releaseMethod, class_getInstanceMethod(cls, webThreadReleaseSEL));
342 }
343
344 void WebCoreObjCDeallocWithWebThreadLock(Class cls)
345 {
346     SEL releaseSEL = @selector(release);
347     SEL webThreadLockReleaseSEL = @selector(_webcore_releaseWithWebThreadLock);
348
349     // get the existing release method
350     Method releaseMethod = class_getInstanceMethod(cls, releaseSEL);
351     if (!releaseMethod) {
352         ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocWithWebThreadLock() failed to find %s for %@", releaseSEL, NSStringFromClass(cls));
353         return;
354     }
355
356     // add the implementation that ensures release WebThreadLock release/deallocation
357     if (!class_addMethod(cls, webThreadLockReleaseSEL, (IMP)WebCoreObjCDeallocWithWebThreadLockImpl, method_getTypeEncoding(releaseMethod))) {
358         ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocWithWebThreadLock() failed to add %s for %@", webThreadLockReleaseSEL, NSStringFromClass(cls));
359         return;
360     }
361
362     // ensure the implementation exists at cls in the class hierarchy
363     if (class_addMethod(cls, releaseSEL, class_getMethodImplementation(cls, releaseSEL), method_getTypeEncoding(releaseMethod)))
364         releaseMethod = class_getInstanceMethod(cls, releaseSEL);
365
366     // swizzle the old release for the new implementation
367     method_exchangeImplementations(releaseMethod, class_getInstanceMethod(cls, webThreadLockReleaseSEL));
368 }
369
370 void WebCoreObjCDeallocOnWebThreadImpl(id self, SEL _cmd)
371 {
372     UNUSED_PARAM(_cmd);
373     if (!WebThreadIsEnabled())
374         [self _webcore_releaseOnWebThread];
375     else {
376         lockWebCoreReleaseLock();
377         if ([self retainCount] == 1) {
378             // This is the only reference retaining the object, so we can
379             // safely release the WebCoreReleaseLock now.
380             unlockWebCoreReleaseLock();
381             if (WebThreadIsCurrent())
382                 [self _webcore_releaseOnWebThread];
383             else
384                 WebThreadAdoptAndRelease(self);
385         } else {
386             // This is not the only reference retaining the object, so another
387             // thread could also call release - hold the lock whilst calling
388             // release to avoid a race condition.
389             [self _webcore_releaseOnWebThread];
390             unlockWebCoreReleaseLock();
391         }
392     }
393 }
394
395 void WebCoreObjCDeallocWithWebThreadLockImpl(id self, SEL _cmd)
396 {
397     UNUSED_PARAM(_cmd);
398     lockWebCoreReleaseLock();
399     if (WebThreadIsLockedOrDisabled() || 1 != [self retainCount])
400         [self _webcore_releaseWithWebThreadLock];
401     else
402         WebThreadAdoptAndRelease(self);
403     unlockWebCoreReleaseLock();
404 }
405
406 static void HandleWebThreadReleaseSource(void *info)
407 {
408     UNUSED_PARAM(info);
409     ASSERT(WebThreadIsCurrent());
410
411     int result = pthread_mutex_lock(&WebThreadReleaseLock);
412     ASSERT_WITH_MESSAGE(result == 0, "Release lock failed with code:%d", result);
413
414     CFMutableArrayRef objects = NULL;
415     if (CFArrayGetCount(WebThreadReleaseObjArray)) {
416         objects = CFArrayCreateMutableCopy(NULL, 0, WebThreadReleaseObjArray);
417         CFArrayRemoveAllValues(WebThreadReleaseObjArray);
418     }
419
420     result = pthread_mutex_unlock(&WebThreadReleaseLock);
421     ASSERT_WITH_MESSAGE(result == 0, "Release unlock failed with code:%d", result);
422
423     if (!objects)
424         return;
425
426     unsigned count = CFArrayGetCount(objects);
427     unsigned i;
428     for (i = 0; i < count; i++) {
429         id obj = (id)CFArrayGetValueAtIndex(objects, i);
430 #if LOG_RELEASES
431         NSLog(@"Release recv [web thread] : %@", obj);
432 #endif
433         [obj release];
434     }
435
436     CFRelease(objects);
437 }
438
439 void WebThreadCallDelegate(NSInvocation *invocation)
440 {
441     // NSInvocation released in SendMessage()
442     SendDelegateMessage([invocation retain]);
443 }
444
445 void WebThreadPostNotification(NSString *name, id object, id userInfo)
446 {
447     if (pthread_main_np())
448         [[NSNotificationCenter defaultCenter] postNotificationName:name object:object userInfo:userInfo];
449     else {
450         dispatch_async(dispatch_get_main_queue(), ^ {
451             [[NSNotificationCenter defaultCenter] postNotificationName:name object:object userInfo:userInfo];
452         });
453     }
454 }
455
456 void WebThreadCallDelegateAsync(NSInvocation *invocation)
457 {
458     ASSERT(invocation);
459     if (WebThreadIsCurrent())
460         [sAsyncDelegates addObject:invocation];
461     else
462         WebThreadCallDelegate(invocation);
463 }
464
465 // Note: despite the name, returns an autoreleased object.
466 NSInvocation *WebThreadMakeNSInvocation(id target, SEL selector)
467 {
468     NSMethodSignature *signature = [target methodSignatureForSelector:selector];
469     ASSERT(signature);
470     if (signature) {
471         NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
472         [invocation setSelector:selector];
473         [invocation setTarget:target];
474         [invocation retainArguments];
475         return invocation;
476     }
477     return nil;
478 }
479
480 static void MainRunLoopAutoUnlock(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context)
481 {
482     UNUSED_PARAM(observer);
483     UNUSED_PARAM(activity);
484     UNUSED_PARAM(context);
485     ASSERT(!WebThreadIsCurrent());
486
487     if (sMainThreadModalCount != 0)
488         return;
489     
490     CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes);
491
492     _WebThreadUnlock();
493 }
494
495 static void _WebThreadAutoLock(void)
496 {
497     ASSERT(!WebThreadIsCurrent());
498
499     if (mainThreadLockCount == 0) {
500         CFRunLoopAddObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes);    
501         _WebTryThreadLock(false);
502         CFRunLoopWakeUp(CFRunLoopGetMain());
503     }
504 }
505
506 static void WebRunLoopLockInternal(AutoreleasePoolOperation poolOperation)
507 {
508     _WebTryThreadLock(false);
509     if (poolOperation == PushOrPopAutoreleasePool)
510         autoreleasePoolMark = NSPushAutoreleasePool(0);
511     isWebThreadLocked = YES;
512 }
513
514 static void WebRunLoopUnlockInternal(AutoreleasePoolOperation poolOperation)
515 {
516     ASSERT(sAsyncDelegates);
517     if ([sAsyncDelegates count]) {
518         for (NSInvocation* invocation in sAsyncDelegates)
519             SendDelegateMessage([invocation retain]);
520         [sAsyncDelegates removeAllObjects];
521     }
522
523     if (poolOperation == PushOrPopAutoreleasePool)
524         NSPopAutoreleasePool(autoreleasePoolMark);
525
526     _WebThreadUnlock();
527     isWebThreadLocked = NO;
528 }
529
530 static void WebRunLoopLock(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context)
531 {
532     UNUSED_PARAM(observer);
533     UNUSED_PARAM(context);
534     ASSERT(WebThreadIsCurrent());
535     ASSERT_UNUSED(activity, activity == kCFRunLoopAfterWaiting || activity == kCFRunLoopBeforeTimers || activity == kCFRunLoopBeforeSources);
536
537     // If the WebThread is locked by the main thread then we want to
538     // grab the lock ourselves when the main thread releases the lock.
539     if (isWebThreadLocked && !mainThreadLockCount)
540         return;
541     WebRunLoopLockInternal(PushOrPopAutoreleasePool);
542 }
543
544 static void WebRunLoopUnlock(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context)
545 {
546     UNUSED_PARAM(observer);
547     UNUSED_PARAM(context);
548     ASSERT(WebThreadIsCurrent());
549     ASSERT_UNUSED(activity, activity == kCFRunLoopBeforeWaiting || activity == kCFRunLoopExit);
550     ASSERT(!mainThreadLockCount);
551
552     if (!isWebThreadLocked)
553         return;
554     WebRunLoopUnlockInternal(PushOrPopAutoreleasePool);
555 }
556
557 static void MainRunLoopUnlockGuard(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context)
558 {
559     UNUSED_PARAM(observer);
560     UNUSED_PARAM(activity);
561     UNUSED_PARAM(context);
562     ASSERT(!WebThreadIsCurrent());
563
564     // We shouldn't have the web lock at this point.  However, MobileMail sometimes
565     // get to a state where the main thread has web lock but it didn't release it on last
566     // runloop exit, and web thread gets stuck at waiting for the lock. If this happens,
567     // we need to help release the lock.  See <rdar://problem/8005192>.
568     if (mainThreadLockCount != 0 && sMainThreadModalCount == 0) {
569         NSLog(@"WARNING: Main thread didn't release the lock at last runloop exit!");
570
571         MainRunLoopAutoUnlock(observer, activity, context);
572         if (mainThreadLockCount != 0)
573             mainThreadLockCount = 0;
574     }
575 }
576
577 static void _WebRunLoopEnableNestedFromMainThread()
578 {
579     CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes);
580 }
581
582 static void _WebRunLoopDisableNestedFromMainThread()
583 {
584     CFRunLoopAddObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes); 
585 }
586
587 void WebRunLoopEnableNested()
588 {
589     if (!WebThreadIsEnabled())
590         return;
591
592     ASSERT(!isNestedWebThreadRunLoop);
593
594     if (!WebThreadIsCurrent())
595         _WebRunLoopEnableNestedFromMainThread();
596
597     savedAutoreleasePoolMark = autoreleasePoolMark;
598     autoreleasePoolMark = 0;
599     WebRunLoopUnlockInternal(IgnoreAutoreleasePool);
600     isNestedWebThreadRunLoop = YES;
601 }
602
603 void WebRunLoopDisableNested()
604 {
605     if (!WebThreadIsEnabled())
606         return;
607
608     ASSERT(isNestedWebThreadRunLoop);
609
610     if (!WebThreadIsCurrent())
611         _WebRunLoopDisableNestedFromMainThread();
612
613     autoreleasePoolMark = savedAutoreleasePoolMark;
614     savedAutoreleasePoolMark = 0;
615     WebRunLoopLockInternal(IgnoreAutoreleasePool);
616     isNestedWebThreadRunLoop = NO;
617 }
618
619 static void FreeThreadContext(void *threadContext)
620 {
621     if (threadContext != NULL)
622        free(threadContext);
623 }
624
625 static void InitThreadContextKey()
626 {
627     int error = pthread_key_create(&threadContextKey, FreeThreadContext);
628     if (error)
629         CRASH();
630 }
631
632 static WebThreadContext *CurrentThreadContext(void)
633 {
634     static pthread_once_t initControl = PTHREAD_ONCE_INIT; 
635     pthread_once(&initControl, InitThreadContextKey);    
636
637     WebThreadContext *threadContext = (WebThreadContext*)pthread_getspecific(threadContextKey);
638     if (threadContext == NULL) {
639         threadContext = (WebThreadContext *)calloc(sizeof(WebThreadContext), 1);
640         pthread_setspecific(threadContextKey, threadContext);
641     }        
642     return threadContext;
643 }
644
645 static
646 #ifndef __llvm__
647 NO_RETURN
648 #endif
649 void *RunWebThread(void *arg)
650 {
651     FloatingPointEnvironment::singleton().propagateMainThreadEnvironment();
652
653     UNUSED_PARAM(arg);
654     // WTF::initializeMainThread() needs to be called before JSC::initializeThreading() since the
655     // code invoked by the latter needs to know if it's running on the WebThread. See
656     // <rdar://problem/8502487>.
657     WTF::initializeMainThread();
658     WTF::initializeWebThread();
659     JSC::initializeThreading();
660     
661     // Make sure that the WebThread and the main thread share the same ThreadGlobalData objects.
662     WebCore::threadGlobalData().setWebCoreThreadData();
663     initializeWebThreadIdentifier();
664
665 #if HAVE(PTHREAD_SETNAME_NP)
666     pthread_setname_np("WebThread");
667 #endif
668
669     webThreadContext = CurrentThreadContext();
670     webThreadRunLoop = CFRunLoopGetCurrent();
671     webThreadNSRunLoop = [[NSRunLoop currentRunLoop] retain];
672
673     CFRunLoopObserverRef webRunLoopLockObserverRef = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeTimers|kCFRunLoopBeforeSources|kCFRunLoopAfterWaiting, YES, 0, WebRunLoopLock, NULL);
674     CFRunLoopAddObserver(webThreadRunLoop, webRunLoopLockObserverRef, kCFRunLoopCommonModes);
675     CFRelease(webRunLoopLockObserverRef);
676     
677     WebThreadInitRunQueue();
678
679     // We must have the lock when CA paints in the web thread. CA commits at 2000000 so we use larger order number than that to free the lock.
680     CFRunLoopObserverRef webRunLoopUnlockObserverRef = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting|kCFRunLoopExit, YES, 2500000, WebRunLoopUnlock, NULL);    
681     CFRunLoopAddObserver(webThreadRunLoop, webRunLoopUnlockObserverRef, kCFRunLoopCommonModes);
682     CFRelease(webRunLoopUnlockObserverRef);    
683
684     CFRunLoopSourceContext ReleaseSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleWebThreadReleaseSource};
685     WebThreadReleaseSource = CFRunLoopSourceCreate(NULL, -1, &ReleaseSourceContext);
686     CFRunLoopAddSource(webThreadRunLoop, WebThreadReleaseSource, kCFRunLoopDefaultMode);
687
688     int result = pthread_mutex_lock(&startupLock);
689     ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result);
690
691     result = pthread_cond_signal(&startupCondition);
692     ASSERT_WITH_MESSAGE(result == 0, "startup signal failed with code:%d", result);
693
694     result = pthread_mutex_unlock(&startupLock);
695     ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result);
696
697     while (1)
698         CFRunLoopRunInMode(kCFRunLoopDefaultMode, DistantFuture, true);
699
700 #ifdef __llvm__
701     return NULL;
702 #endif  
703 }
704
705 static void StartWebThread()
706 {
707     webThreadStarted = TRUE;
708
709     // Initialize ThreadGlobalData on the main UI thread so that the WebCore thread
710     // can later set it's thread-specific data to point to the same objects.
711     WebCore::ThreadGlobalData& unused = WebCore::threadGlobalData();
712     (void)unused;
713
714     // Initialize AtomicString on the main thread.
715     WTF::AtomicString::init();
716
717     RunLoop::initializeMainRunLoop();
718
719     // register class for WebThread deallocation
720     WebCoreObjCDeallocOnWebThread([WAKWindow class]);
721     WebCoreObjCDeallocWithWebThreadLock([WAKView class]);
722
723     pthread_mutexattr_t mattr;
724     pthread_mutexattr_init(&mattr);
725     pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
726     pthread_mutex_init(&webLock, &mattr);
727     pthread_mutexattr_destroy(&mattr);            
728
729     pthread_mutexattr_t mutex_attr;
730     pthread_mutexattr_init(&mutex_attr);
731     pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);    
732     pthread_mutex_init(&WebCoreReleaseLock, &mutex_attr);
733     pthread_mutexattr_destroy(&mutex_attr);
734
735     CFRunLoopRef runLoop = CFRunLoopGetCurrent();
736     CFRunLoopSourceContext delegateSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleDelegateSource};
737     delegateSource = CFRunLoopSourceCreate(NULL, 0, &delegateSourceContext);
738     // We shouldn't get delegate callbacks while scrolling, but there might be
739     // one outstanding when we start.  Add the source for all common run loop
740     // modes so we don't block the web thread while scrolling.
741     if (!delegateSourceRunLoopMode)
742         delegateSourceRunLoopMode = kCFRunLoopCommonModes;
743     CFRunLoopAddSource(runLoop, delegateSource, delegateSourceRunLoopMode);
744
745     sAsyncDelegates = [[NSMutableArray alloc] init];
746
747     mainRunLoopAutoUnlockObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting | kCFRunLoopExit, YES, 3000001, MainRunLoopAutoUnlock, NULL);
748
749     pthread_attr_t tattr;
750     pthread_attr_init(&tattr);
751     pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
752     // The web thread is a secondary thread, and secondary threads are usually given
753     // a 512 kb stack, but we need more space in order to have room for the JavaScriptCore
754     // reentrancy limit. This limit works on both the simulator and the device.
755     pthread_attr_setstacksize(&tattr, 200 * 4096);
756
757     struct sched_param param;
758     pthread_attr_getschedparam(&tattr, &param);
759     param.sched_priority--;
760     pthread_attr_setschedparam(&tattr, &param);
761
762     // Wait for the web thread to startup completely before we continue.
763     int result = pthread_mutex_lock(&startupLock);
764     ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result);
765
766     // Propagate the mainThread's fenv to workers & the web thread.
767     FloatingPointEnvironment::singleton().saveMainThreadEnvironment();
768
769     pthread_create(&webThread, &tattr, RunWebThread, NULL);
770     pthread_attr_destroy(&tattr);
771
772     result = pthread_cond_wait(&startupCondition, &startupLock);
773     ASSERT_WITH_MESSAGE(result == 0, "startup wait failed with code:%d", result);
774
775     result = pthread_mutex_unlock(&startupLock);
776     ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result);
777
778     initializeApplicationUIThreadIdentifier();
779 }
780
781 static int WebTimedConditionLock (pthread_cond_t *condition, pthread_mutex_t *lock, CFAbsoluteTime interval)
782 {
783     struct timespec time;
784     CFAbsoluteTime at = CFAbsoluteTimeGetCurrent() + interval;
785     time.tv_sec = (time_t)(floor(at) + kCFAbsoluteTimeIntervalSince1970);
786     time.tv_nsec = (int32_t)((at - floor(at)) * 1000000000.0);        
787     return pthread_cond_timedwait(condition, lock, &time);
788 }
789
790
791 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
792 static unsigned lockCount;
793 #endif
794
795 static bool _WebTryThreadLock(bool shouldTry)
796 {
797     // Suspend the web thread if the main thread is trying to lock.
798     bool onMainThread = pthread_main_np();
799     if (onMainThread)
800         webThreadShouldYield = true;
801     else if (!WebThreadIsCurrent()) {
802         NSLog(@"%s, %p: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...", __PRETTY_FUNCTION__, CurrentThreadContext());
803         CRASH();
804     }
805         
806             
807     int result;
808     bool busy = false;
809     if (shouldTry) {
810         result = pthread_mutex_trylock(&webLock);
811         if (result == EBUSY) {
812             busy = true;
813         } else
814             ASSERT_WITH_MESSAGE(result == 0, "try web lock failed with code:%d", result);
815     }
816     else {
817         result = pthread_mutex_lock(&webLock);
818         ASSERT_WITH_MESSAGE(result == 0, "web lock failed with code:%d", result);
819     }
820     
821     if (!busy) {
822 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
823         lockCount++;
824 #if LOG_WEB_LOCK
825         NSLog(@"lock   %d, web-thread: %d", lockCount, WebThreadIsCurrent());
826 #endif
827 #endif
828         if (onMainThread) {
829             ASSERT(CFRunLoopGetCurrent() == CFRunLoopGetMain());
830             webThreadShouldYield = false;
831             mainThreadLockCount++;
832 #if LOG_MAIN_THREAD_LOCKING
833             if (!sendingDelegateMessage && lockCount == 1)
834                 NSLog(@"Main thread locking outside of delegate messages.");
835 #endif
836         } else {
837             webThreadLockCount++;
838             if (webThreadLockCount > 1) {
839                 NSLog(@"%s, %p: Multiple locks on web thread not allowed! Please file a bug. Crashing now...", __PRETTY_FUNCTION__, CurrentThreadContext());
840                 CRASH();
841             }
842         }
843     }
844     
845     return !busy;
846 }
847
848 void WebThreadLock(void)
849 {
850     if (!webThreadStarted || pthread_equal(webThread, pthread_self()))
851         return;
852     _WebThreadAutoLock();
853 }
854
855 void WebThreadUnlock(void)
856 {
857     // This is a no-op, we unlock automatically on top of the runloop
858     ASSERT(!WebThreadIsCurrent());
859 }
860
861 void WebThreadLockFromAnyThread()
862 {
863     _WebThreadLockFromAnyThread(true);
864 }
865     
866 void WebThreadLockFromAnyThreadNoLog()
867 {
868     _WebThreadLockFromAnyThread(false);
869 }
870
871 static void _WebThreadLockFromAnyThread(bool shouldLog)
872 {
873     if (!webThreadStarted)
874         return;
875     ASSERT(!WebThreadIsCurrent());
876     if (pthread_main_np()) {
877         _WebThreadAutoLock();
878         return;
879     }
880     if (shouldLog)
881         NSLog(@"%s, %p: Obtaining the web lock from a thread other than the main thread or the web thread. UIKit should not be called from a secondary thread.", __PRETTY_FUNCTION__, CurrentThreadContext());
882
883     pthread_mutex_lock(&webLock);
884     
885     // This used for any thread other than the web thread.
886     otherThreadLockCount++;
887     webThreadShouldYield = false;
888 }
889
890 void WebThreadUnlockFromAnyThread()
891 {
892     if (!webThreadStarted)
893         return;
894     ASSERT(!WebThreadIsCurrent());
895     // No-op except from a secondary thread.
896     if (pthread_main_np())
897         return;
898     
899     ASSERT(otherThreadLockCount);
900     otherThreadLockCount--;
901     
902     int result;
903     result = pthread_mutex_unlock(&webLock); 
904     ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result);
905 }
906
907 void WebThreadUnlockGuardForMail()
908 {
909     ASSERT(!WebThreadIsCurrent());
910
911     CFRunLoopObserverRef mainRunLoopUnlockGuardObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopEntry, YES, 0, MainRunLoopUnlockGuard, NULL);
912     CFRunLoopAddObserver(CFRunLoopGetMain(), mainRunLoopUnlockGuardObserver, kCFRunLoopCommonModes);
913     CFRelease(mainRunLoopUnlockGuardObserver);
914 }
915
916 void _WebThreadUnlock(void)
917 {
918 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
919     lockCount--;
920 #if LOG_WEB_LOCK
921     NSLog(@"unlock %d, web-thread: %d", lockCount, WebThreadIsCurrent());
922 #endif
923 #endif
924     
925     if (!WebThreadIsCurrent()) {
926         ASSERT(mainThreadLockCount != 0);
927         mainThreadLockCount--;
928     } else {    
929         webThreadLockCount--;
930         webThreadShouldYield = false;
931     }
932     
933     int result;
934     result = pthread_mutex_unlock(&webLock); 
935     ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result);    
936 }
937
938 bool WebThreadIsLocked(void)
939 {
940     if (WebThreadIsCurrent())
941         return webThreadLockCount;
942     else if (pthread_main_np())
943         return mainThreadLockCount;
944     else
945         return otherThreadLockCount;
946 }
947
948 bool WebThreadIsLockedOrDisabled(void)
949 {
950     return !WebThreadIsEnabled() || WebThreadIsLocked();
951 }
952
953 void WebThreadLockPushModal(void)
954 {
955     if (WebThreadIsCurrent())
956         return;
957     
958     ASSERT(WebThreadIsLocked());
959     ++sMainThreadModalCount;
960 }
961
962 void WebThreadLockPopModal(void)
963 {
964     if (WebThreadIsCurrent())
965         return;
966     
967     ASSERT(WebThreadIsLocked());
968     ASSERT(sMainThreadModalCount != 0);
969     --sMainThreadModalCount;
970 }
971
972 CFRunLoopRef WebThreadRunLoop(void)
973 {
974     if (webThreadStarted) {
975         ASSERT(webThreadRunLoop);
976         return webThreadRunLoop;
977     }
978     
979     return CFRunLoopGetCurrent();
980 }
981
982 NSRunLoop* WebThreadNSRunLoop(void)
983 {
984     if (webThreadStarted) {
985         ASSERT(webThreadNSRunLoop);
986         return webThreadNSRunLoop;
987     }
988
989     return [NSRunLoop currentRunLoop];
990 }
991
992 WebThreadContext *WebThreadCurrentContext(void)
993 {
994     return CurrentThreadContext();
995 }
996
997 void WebThreadSetDelegateSourceRunLoopMode(CFStringRef mode)
998 {
999     ASSERT(!webThreadStarted);
1000     delegateSourceRunLoopMode = mode;
1001 }
1002
1003 void WebThreadEnable(void)
1004 {
1005     if (WebCore::IOSApplication::isMobileSafari()) {
1006         const mach_exception_data_type_t kExceptionCode = 0xbbadd0b1; // "Bad Dub 1" or Bad WK1.
1007         SimulateCrash(getpid(), kExceptionCode, @"MobileSafari should never run a WebThread");
1008     }
1009     RELEASE_ASSERT_WITH_MESSAGE(!WebCore::IOSApplication::isWebProcess(), "The WebProcess should never run a WebThread");
1010
1011     static pthread_once_t initControl = PTHREAD_ONCE_INIT;
1012     pthread_once(&initControl, StartWebThread);
1013 }
1014
1015 bool WebThreadIsEnabled(void)
1016 {
1017     return webThreadStarted;
1018 }
1019
1020 bool WebThreadIsCurrent(void)
1021 {
1022     return webThreadStarted && pthread_equal(webThread, pthread_self());
1023 }
1024
1025 bool WebThreadNotCurrent(void)
1026 {
1027     return webThreadStarted && !pthread_equal(webThread, pthread_self());
1028 }
1029
1030 #endif // PLATFORM(IOS)