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