c8f05a1a0aed602cbd8c094e0a97203af9c606bf
[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 pthread_mutex_t startupLock = PTHREAD_MUTEX_INITIALIZER;
131 static pthread_cond_t startupCondition = PTHREAD_COND_INITIALIZER;
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     int result = pthread_mutex_lock(&startupLock);
685     ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result);
686
687     result = pthread_cond_signal(&startupCondition);
688     ASSERT_WITH_MESSAGE(result == 0, "startup signal failed with code:%d", result);
689
690     result = pthread_mutex_unlock(&startupLock);
691     ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result);
692
693     while (1)
694         CFRunLoopRunInMode(kCFRunLoopDefaultMode, DistantFuture, true);
695
696 #ifdef __llvm__
697     return NULL;
698 #endif  
699 }
700
701 static void StartWebThread()
702 {
703     webThreadStarted = TRUE;
704
705     // ThreadGlobalData touches AtomicString, which requires Threading initialization.
706     WTF::initializeThreading();
707
708     // Initialize AtomicString on the main thread.
709     WTF::AtomicString::init();
710
711     // Initialize ThreadGlobalData on the main UI thread so that the WebCore thread
712     // can later set it's thread-specific data to point to the same objects.
713     WebCore::ThreadGlobalData& unused = WebCore::threadGlobalData();
714     (void)unused;
715
716     RunLoop::initializeMainRunLoop();
717
718     // register class for WebThread deallocation
719     WebCoreObjCDeallocOnWebThread([WAKWindow class]);
720     WebCoreObjCDeallocWithWebThreadLock([WAKView class]);
721
722     pthread_mutexattr_t mattr;
723     pthread_mutexattr_init(&mattr);
724     pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
725     pthread_mutex_init(&webLock, &mattr);
726     pthread_mutexattr_destroy(&mattr);            
727
728     pthread_mutexattr_t mutex_attr;
729     pthread_mutexattr_init(&mutex_attr);
730     pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);    
731     pthread_mutex_init(&WebCoreReleaseLock, &mutex_attr);
732     pthread_mutexattr_destroy(&mutex_attr);
733
734     CFRunLoopRef runLoop = CFRunLoopGetCurrent();
735     CFRunLoopSourceContext delegateSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleDelegateSource};
736     delegateSource = CFRunLoopSourceCreate(NULL, 0, &delegateSourceContext);
737
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     CFRunLoopAddSource(runLoop, delegateSource, kCFRunLoopCommonModes);
742
743     sAsyncDelegates = [[NSMutableArray alloc] init];
744
745     mainRunLoopAutoUnlockObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting | kCFRunLoopExit, YES, 3000001, MainRunLoopAutoUnlock, NULL);
746
747     pthread_attr_t tattr;
748     pthread_attr_init(&tattr);
749     pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
750     // The web thread is a secondary thread, and secondary threads are usually given
751     // a 512 kb stack, but we need more space in order to have room for the JavaScriptCore
752     // reentrancy limit. This limit works on both the simulator and the device.
753     pthread_attr_setstacksize(&tattr, 200 * 4096);
754
755     struct sched_param param;
756     pthread_attr_getschedparam(&tattr, &param);
757     param.sched_priority--;
758     pthread_attr_setschedparam(&tattr, &param);
759
760     // Wait for the web thread to startup completely before we continue.
761     int result = pthread_mutex_lock(&startupLock);
762     ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result);
763
764     // Propagate the mainThread's fenv to workers & the web thread.
765     FloatingPointEnvironment::singleton().saveMainThreadEnvironment();
766
767     pthread_create(&webThread, &tattr, RunWebThread, NULL);
768     pthread_attr_destroy(&tattr);
769
770     result = pthread_cond_wait(&startupCondition, &startupLock);
771     ASSERT_WITH_MESSAGE(result == 0, "startup wait failed with code:%d", result);
772
773     result = pthread_mutex_unlock(&startupLock);
774     ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result);
775
776     initializeApplicationUIThreadIdentifier();
777 }
778
779 static int WebTimedConditionLock (pthread_cond_t *condition, pthread_mutex_t *lock, CFAbsoluteTime interval)
780 {
781     struct timespec time;
782     CFAbsoluteTime at = CFAbsoluteTimeGetCurrent() + interval;
783     time.tv_sec = (time_t)(floor(at) + kCFAbsoluteTimeIntervalSince1970);
784     time.tv_nsec = (int32_t)((at - floor(at)) * 1000000000.0);        
785     return pthread_cond_timedwait(condition, lock, &time);
786 }
787
788
789 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
790 static unsigned lockCount;
791 #endif
792
793 static bool _WebTryThreadLock(bool shouldTry)
794 {
795     // Suspend the web thread if the main thread is trying to lock.
796     bool onMainThread = pthread_main_np();
797     if (onMainThread)
798         webThreadShouldYield = true;
799     else if (!WebThreadIsCurrent()) {
800         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());
801         CRASH();
802     }
803         
804             
805     int result;
806     bool busy = false;
807     if (shouldTry) {
808         result = pthread_mutex_trylock(&webLock);
809         if (result == EBUSY) {
810             busy = true;
811         } else
812             ASSERT_WITH_MESSAGE(result == 0, "try web lock failed with code:%d", result);
813     }
814     else {
815         result = pthread_mutex_lock(&webLock);
816         ASSERT_WITH_MESSAGE(result == 0, "web lock failed with code:%d", result);
817     }
818     
819     if (!busy) {
820 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
821         lockCount++;
822 #if LOG_WEB_LOCK
823         NSLog(@"lock   %d, web-thread: %d", lockCount, WebThreadIsCurrent());
824 #endif
825 #endif
826         if (onMainThread) {
827             ASSERT(CFRunLoopGetCurrent() == CFRunLoopGetMain());
828             webThreadShouldYield = false;
829             mainThreadLockCount++;
830 #if LOG_MAIN_THREAD_LOCKING
831             if (!sendingDelegateMessage && lockCount == 1)
832                 NSLog(@"Main thread locking outside of delegate messages.");
833 #endif
834         } else {
835             webThreadLockCount++;
836             if (webThreadLockCount > 1) {
837                 NSLog(@"%s, %p: Multiple locks on web thread not allowed! Please file a bug. Crashing now...", __PRETTY_FUNCTION__, CurrentThreadContext());
838                 CRASH();
839             }
840         }
841     }
842     
843     return !busy;
844 }
845
846 void WebThreadLock(void)
847 {
848     if (!webThreadStarted || pthread_equal(webThread, pthread_self()))
849         return;
850     _WebThreadAutoLock();
851 }
852
853 void WebThreadUnlock(void)
854 {
855     // This is a no-op, we unlock automatically on top of the runloop
856     ASSERT(!WebThreadIsCurrent());
857 }
858
859 void WebThreadLockFromAnyThread()
860 {
861     _WebThreadLockFromAnyThread(true);
862 }
863     
864 void WebThreadLockFromAnyThreadNoLog()
865 {
866     _WebThreadLockFromAnyThread(false);
867 }
868
869 static void _WebThreadLockFromAnyThread(bool shouldLog)
870 {
871     if (!webThreadStarted)
872         return;
873     ASSERT(!WebThreadIsCurrent());
874     if (pthread_main_np()) {
875         _WebThreadAutoLock();
876         return;
877     }
878     if (shouldLog)
879         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());
880
881     pthread_mutex_lock(&webLock);
882     
883     // This used for any thread other than the web thread.
884     otherThreadLockCount++;
885     webThreadShouldYield = false;
886 }
887
888 void WebThreadUnlockFromAnyThread()
889 {
890     if (!webThreadStarted)
891         return;
892     ASSERT(!WebThreadIsCurrent());
893     // No-op except from a secondary thread.
894     if (pthread_main_np())
895         return;
896     
897     ASSERT(otherThreadLockCount);
898     otherThreadLockCount--;
899     
900     int result;
901     result = pthread_mutex_unlock(&webLock); 
902     ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result);
903 }
904
905 void WebThreadUnlockGuardForMail()
906 {
907     ASSERT(!WebThreadIsCurrent());
908
909     CFRunLoopObserverRef mainRunLoopUnlockGuardObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopEntry, YES, 0, MainRunLoopUnlockGuard, NULL);
910     CFRunLoopAddObserver(CFRunLoopGetMain(), mainRunLoopUnlockGuardObserver, kCFRunLoopCommonModes);
911     CFRelease(mainRunLoopUnlockGuardObserver);
912 }
913
914 void _WebThreadUnlock(void)
915 {
916 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
917     lockCount--;
918 #if LOG_WEB_LOCK
919     NSLog(@"unlock %d, web-thread: %d", lockCount, WebThreadIsCurrent());
920 #endif
921 #endif
922     
923     if (!WebThreadIsCurrent()) {
924         ASSERT(mainThreadLockCount != 0);
925         mainThreadLockCount--;
926     } else {    
927         webThreadLockCount--;
928         webThreadShouldYield = false;
929     }
930     
931     int result;
932     result = pthread_mutex_unlock(&webLock); 
933     ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result);    
934 }
935
936 bool WebThreadIsLocked(void)
937 {
938     if (WebThreadIsCurrent())
939         return webThreadLockCount;
940     else if (pthread_main_np())
941         return mainThreadLockCount;
942     else
943         return otherThreadLockCount;
944 }
945
946 bool WebThreadIsLockedOrDisabled(void)
947 {
948     return !WebThreadIsEnabled() || WebThreadIsLocked();
949 }
950
951 void WebThreadLockPushModal(void)
952 {
953     if (WebThreadIsCurrent())
954         return;
955     
956     ASSERT(WebThreadIsLocked());
957     ++sMainThreadModalCount;
958 }
959
960 void WebThreadLockPopModal(void)
961 {
962     if (WebThreadIsCurrent())
963         return;
964     
965     ASSERT(WebThreadIsLocked());
966     ASSERT(sMainThreadModalCount != 0);
967     --sMainThreadModalCount;
968 }
969
970 CFRunLoopRef WebThreadRunLoop(void)
971 {
972     if (webThreadStarted) {
973         ASSERT(webThreadRunLoop);
974         return webThreadRunLoop;
975     }
976     
977     return CFRunLoopGetCurrent();
978 }
979
980 NSRunLoop* WebThreadNSRunLoop(void)
981 {
982     if (webThreadStarted) {
983         ASSERT(webThreadNSRunLoop);
984         return webThreadNSRunLoop;
985     }
986
987     return [NSRunLoop currentRunLoop];
988 }
989
990 WebThreadContext *WebThreadCurrentContext(void)
991 {
992     return CurrentThreadContext();
993 }
994
995 void WebThreadEnable(void)
996 {
997     RELEASE_ASSERT_WITH_MESSAGE(!WebCore::IOSApplication::isWebProcess(), "The WebProcess should never run a Web Thread");
998
999     static pthread_once_t initControl = PTHREAD_ONCE_INIT;
1000     pthread_once(&initControl, StartWebThread);
1001 }
1002
1003 bool WebThreadIsEnabled(void)
1004 {
1005     return webThreadStarted;
1006 }
1007
1008 bool WebThreadIsCurrent(void)
1009 {
1010     return webThreadStarted && pthread_equal(webThread, pthread_self());
1011 }
1012
1013 bool WebThreadNotCurrent(void)
1014 {
1015     return webThreadStarted && !pthread_equal(webThread, pthread_self());
1016 }
1017
1018 #endif // PLATFORM(IOS)