[iOS] Make WebCore build and link with public SDK
[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 "FloatingPointEnvironment.h"
32 #import "JSDOMWindowBase.h"
33 #import "ThreadGlobalData.h"
34 #import "RuntimeApplicationChecksIOS.h"
35 #import "WebCoreThreadInternal.h"
36 #import "WebCoreThreadMessage.h"
37 #import "WebCoreThreadRun.h"
38 #import "WebCoreThreadSafe.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/Threading.h>
46 #import <wtf/text/AtomicString.h>
47
48 #import <Foundation/NSInvocation.h>
49 #import <libkern/OSAtomic.h>
50 #import <objc/runtime.h>
51
52 #define LOG_MESSAGES 0
53 #define LOG_WEB_LOCK 0
54 #define LOG_MAIN_THREAD_LOCKING 0
55 #define LOG_RELEASES 0
56
57 #define DistantFuture   (86400.0 * 2000 * 365.2425 + 86400.0)   // same as +[NSDate distantFuture]
58 #define MaxArgCount         5
59 #define DrawWaitInterval     10
60 #define DelegateWaitInterval 10
61
62 static void _WebThreadAutoLock(void);
63 static bool _WebTryThreadLock(bool shouldTry);
64 static void _WebThreadLockFromAnyThread(bool shouldLog);
65 static void _WebThreadUnlock(void);
66
67 @interface NSObject(ForwardDeclarations)
68 -(void)_webcore_releaseOnWebThread;
69 -(void)_webcore_releaseWithWebThreadLock;
70 @end
71
72 @implementation NSObject(WebCoreThreadAdditions)
73
74 - (void)releaseOnMainThread {
75     if ([NSThread isMainThread]) {
76         [self release];
77     } else {
78         [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
79     }
80 }
81
82 @end
83
84 typedef void *NSAutoreleasePoolMark;
85 #ifdef __cplusplus
86 extern "C" {
87 #endif
88 extern NSAutoreleasePoolMark NSPushAutoreleasePool(unsigned ignored);
89 extern void NSPopAutoreleasePool(NSAutoreleasePoolMark token);
90 #ifdef __cplusplus
91 }
92 #endif
93
94 static int WebTimedConditionLock (pthread_cond_t *condition, pthread_mutex_t *lock, CFAbsoluteTime interval);
95
96 static pthread_mutex_t webLock;
97 static NSAutoreleasePoolMark autoreleasePoolMark;
98 static CFRunLoopRef webThreadRunLoop;
99 static NSRunLoop* webThreadNSRunLoop;
100 static pthread_t webThread;
101 static BOOL isWebThreadLocked;
102 static BOOL webThreadStarted;
103 static unsigned webThreadLockCount;
104
105 static NSAutoreleasePoolMark savedAutoreleasePoolMark;
106 static BOOL isNestedWebThreadRunLoop;
107 typedef enum {
108     PushOrPopAutoreleasePool,
109     IgnoreAutoreleasePool
110 } AutoreleasePoolOperation;
111
112 static pthread_mutex_t WebThreadReleaseLock = PTHREAD_MUTEX_INITIALIZER;
113 static CFRunLoopSourceRef WebThreadReleaseSource;
114 static CFMutableArrayRef WebThreadReleaseObjArray;
115
116 static void MainThreadAdoptAndRelease(id obj);
117
118 static pthread_mutex_t delegateLock = PTHREAD_MUTEX_INITIALIZER;
119 static pthread_cond_t delegateCondition = PTHREAD_COND_INITIALIZER;
120 static NSInvocation *delegateInvocation;
121 static CFRunLoopSourceRef delegateSource = NULL;
122 static BOOL delegateHandled;
123 #if LOG_MAIN_THREAD_LOCKING
124 static BOOL sendingDelegateMessage;
125 #endif
126
127 static CFRunLoopObserverRef mainRunLoopAutoUnlockObserver;
128
129 static pthread_mutex_t startupLock = PTHREAD_MUTEX_INITIALIZER;
130 static pthread_cond_t startupCondition = PTHREAD_COND_INITIALIZER;
131
132 static WebThreadContext *webThreadContext;
133 static pthread_key_t threadContextKey;
134 static unsigned mainThreadLockCount;
135 static unsigned otherThreadLockCount;
136 static unsigned sMainThreadModalCount;
137
138 volatile bool webThreadShouldYield;
139
140 static pthread_mutex_t WebCoreReleaseLock;
141 static void WebCoreObjCDeallocOnWebThreadImpl(id self, SEL _cmd);
142 static void WebCoreObjCDeallocWithWebThreadLockImpl(id self, SEL _cmd);
143
144 static NSMutableArray *sAsyncDelegates = nil;
145
146 static CFStringRef delegateSourceRunLoopMode;
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::JSDOMWindowBase::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::JSDOMWindowBase::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     pthread_key_create(&threadContextKey, FreeThreadContext);
624 }
625
626 static WebThreadContext *CurrentThreadContext(void)
627 {
628     static pthread_once_t initControl = PTHREAD_ONCE_INIT; 
629     pthread_once(&initControl, InitThreadContextKey);    
630
631     WebThreadContext *threadContext = (WebThreadContext*)pthread_getspecific(threadContextKey);
632     if (threadContext == NULL) {
633         threadContext = (WebThreadContext *)calloc(sizeof(WebThreadContext), 1);
634         pthread_setspecific(threadContextKey, threadContext);
635     }        
636     return threadContext;
637 }
638
639 static
640 #ifndef __llvm__
641 NO_RETURN
642 #endif
643 void *RunWebThread(void *arg)
644 {
645     FloatingPointEnvironment::shared().propagateMainThreadEnvironment();
646
647     UNUSED_PARAM(arg);
648     // WTF::initializeMainThread() needs to be called before JSC::initializeThreading() since the
649     // code invoked by the latter needs to know if it's running on the WebThread. See
650     // <rdar://problem/8502487>.
651     WTF::initializeMainThread();
652     WTF::initializeWebThread();
653     JSC::initializeThreading();
654     
655     // Make sure that the WebThread and the main thread share the same ThreadGlobalData objects.
656     WebCore::threadGlobalData().setWebCoreThreadData();
657     initializeWebThreadIdentifier();
658
659 #if HAVE(PTHREAD_SETNAME_NP)
660     pthread_setname_np("WebThread");
661 #endif
662
663     webThreadContext = CurrentThreadContext();
664     webThreadRunLoop = CFRunLoopGetCurrent();
665     webThreadNSRunLoop = [[NSRunLoop currentRunLoop] retain];
666
667     CFRunLoopObserverRef webRunLoopLockObserverRef = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeTimers|kCFRunLoopBeforeSources|kCFRunLoopAfterWaiting, YES, 0, WebRunLoopLock, NULL);
668     CFRunLoopAddObserver(webThreadRunLoop, webRunLoopLockObserverRef, kCFRunLoopCommonModes);
669     CFRelease(webRunLoopLockObserverRef);
670     
671     WebThreadInitRunQueue();
672
673     // 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.
674     CFRunLoopObserverRef webRunLoopUnlockObserverRef = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting|kCFRunLoopExit, YES, 2500000, WebRunLoopUnlock, NULL);    
675     CFRunLoopAddObserver(webThreadRunLoop, webRunLoopUnlockObserverRef, kCFRunLoopCommonModes);
676     CFRelease(webRunLoopUnlockObserverRef);    
677
678     CFRunLoopSourceContext ReleaseSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleWebThreadReleaseSource};
679     WebThreadReleaseSource = CFRunLoopSourceCreate(NULL, -1, &ReleaseSourceContext);
680     CFRunLoopAddSource(webThreadRunLoop, WebThreadReleaseSource, kCFRunLoopDefaultMode);
681
682     int result = pthread_mutex_lock(&startupLock);
683     ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result);
684
685     result = pthread_cond_signal(&startupCondition);
686     ASSERT_WITH_MESSAGE(result == 0, "startup signal failed with code:%d", result);
687
688     result = pthread_mutex_unlock(&startupLock);
689     ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result);
690
691     while (1)
692         CFRunLoopRunInMode(kCFRunLoopDefaultMode, DistantFuture, true);
693
694 #ifdef __llvm__
695     return NULL;
696 #endif  
697 }
698
699 static void StartWebThread()
700 {
701     webThreadStarted = TRUE;
702
703     // Initialize ThreadGlobalData on the main UI thread so that the WebCore thread
704     // can later set it's thread-specific data to point to the same objects.
705     WebCore::ThreadGlobalData& unused = WebCore::threadGlobalData();
706     (void)unused;
707
708     // Initialize AtomicString on the main thread.
709     WTF::AtomicString::init();
710
711     // register class for WebThread deallocation
712     WebCoreObjCDeallocOnWebThread([DOMObject class]);
713     WebCoreObjCDeallocOnWebThread([WAKWindow class]);
714     WebCoreObjCDeallocWithWebThreadLock([WAKView class]);
715
716     pthread_mutexattr_t mattr;
717     pthread_mutexattr_init(&mattr);
718     pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
719     pthread_mutex_init(&webLock, &mattr);
720     pthread_mutexattr_destroy(&mattr);            
721
722     pthread_mutexattr_t mutex_attr;
723     pthread_mutexattr_init(&mutex_attr);
724     pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);    
725     pthread_mutex_init(&WebCoreReleaseLock, &mutex_attr);
726     pthread_mutexattr_destroy(&mutex_attr);
727
728     CFRunLoopRef runLoop = CFRunLoopGetCurrent();
729     CFRunLoopSourceContext delegateSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleDelegateSource};
730     delegateSource = CFRunLoopSourceCreate(NULL, 0, &delegateSourceContext);
731     // We shouldn't get delegate callbacks while scrolling, but there might be
732     // one outstanding when we start.  Add the source for all common run loop
733     // modes so we don't block the web thread while scrolling.
734     if (!delegateSourceRunLoopMode)
735         delegateSourceRunLoopMode = kCFRunLoopCommonModes;
736     CFRunLoopAddSource(runLoop, delegateSource, delegateSourceRunLoopMode);
737
738     sAsyncDelegates = [[NSMutableArray alloc] init];
739
740     mainRunLoopAutoUnlockObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting | kCFRunLoopExit, YES, 3000001, MainRunLoopAutoUnlock, NULL);
741
742     pthread_attr_t tattr;
743     pthread_attr_init(&tattr);
744     pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
745     // The web thread is a secondary thread, and secondary threads are usually given
746     // a 512 kb stack, but we need more space in order to have room for the JavaScriptCore
747     // reentrancy limit. This limit works on both the simulator and the device.
748     pthread_attr_setstacksize(&tattr, 200 * 4096);
749
750     struct sched_param param;
751     pthread_attr_getschedparam(&tattr, &param);
752     param.sched_priority--;
753     pthread_attr_setschedparam(&tattr, &param);
754
755     // Wait for the web thread to startup completely before we continue.
756     int result = pthread_mutex_lock(&startupLock);
757     ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result);
758
759     // Propagate the mainThread's fenv to workers & the web thread.
760     FloatingPointEnvironment::shared().saveMainThreadEnvironment();
761
762     pthread_create(&webThread, &tattr, RunWebThread, NULL);
763     pthread_attr_destroy(&tattr);
764
765     result = pthread_cond_wait(&startupCondition, &startupLock);
766     ASSERT_WITH_MESSAGE(result == 0, "startup wait failed with code:%d", result);
767
768     result = pthread_mutex_unlock(&startupLock);
769     ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result);
770
771     initializeApplicationUIThreadIdentifier();
772 }
773
774 static int WebTimedConditionLock (pthread_cond_t *condition, pthread_mutex_t *lock, CFAbsoluteTime interval)
775 {
776     struct timespec time;
777     CFAbsoluteTime at = CFAbsoluteTimeGetCurrent() + interval;
778     time.tv_sec = (time_t)(floor(at) + kCFAbsoluteTimeIntervalSince1970);
779     time.tv_nsec = (int32_t)((at - floor(at)) * 1000000000.0);        
780     return pthread_cond_timedwait(condition, lock, &time);
781 }
782
783
784 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
785 static unsigned lockCount;
786 #endif
787
788 static bool _WebTryThreadLock(bool shouldTry)
789 {
790     // Suspend the web thread if the main thread is trying to lock.
791     bool onMainThread = pthread_main_np();
792     if (onMainThread)
793         webThreadShouldYield = true;
794     else if (!WebThreadIsCurrent()) {
795         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());
796         CRASH();
797     }
798         
799             
800     int result;
801     bool busy = false;
802     if (shouldTry) {
803         result = pthread_mutex_trylock(&webLock);
804         if (result == EBUSY) {
805             busy = true;
806         } else
807             ASSERT_WITH_MESSAGE(result == 0, "try web lock failed with code:%d", result);
808     }
809     else {
810         result = pthread_mutex_lock(&webLock);
811         ASSERT_WITH_MESSAGE(result == 0, "web lock failed with code:%d", result);
812     }
813     
814     if (!busy) {
815 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
816         lockCount++;
817 #if LOG_WEB_LOCK
818         NSLog(@"lock   %d, web-thread: %d", lockCount, WebThreadIsCurrent());
819 #endif
820 #endif
821         if (onMainThread) {
822             ASSERT(CFRunLoopGetCurrent() == CFRunLoopGetMain());
823             webThreadShouldYield = false;
824             mainThreadLockCount++;
825 #if LOG_MAIN_THREAD_LOCKING
826             if (!sendingDelegateMessage && lockCount == 1)
827                 NSLog(@"Main thread locking outside of delegate messages.");
828 #endif
829         } else {
830             webThreadLockCount++;
831             if (webThreadLockCount > 1) {
832                 NSLog(@"%s, %p: Multiple locks on web thread not allowed! Please file a bug. Crashing now...", __PRETTY_FUNCTION__, CurrentThreadContext());
833                 CRASH();
834             }
835         }
836     }
837     
838     return !busy;
839 }
840
841 void WebThreadLock(void)
842 {
843     if (!webThreadStarted || pthread_equal(webThread, pthread_self()))
844         return;
845     _WebThreadAutoLock();
846 }
847
848 void WebThreadUnlock(void)
849 {
850     // This is a no-op, we unlock automatically on top of the runloop
851     ASSERT(!WebThreadIsCurrent());
852 }
853
854 void WebThreadLockFromAnyThread()
855 {
856     _WebThreadLockFromAnyThread(true);
857 }
858     
859 void WebThreadLockFromAnyThreadNoLog()
860 {
861     _WebThreadLockFromAnyThread(false);
862 }
863
864 static void _WebThreadLockFromAnyThread(bool shouldLog)
865 {
866     if (!webThreadStarted)
867         return;
868     ASSERT(!WebThreadIsCurrent());
869     if (pthread_main_np()) {
870         _WebThreadAutoLock();
871         return;
872     }
873     if (shouldLog)
874         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());
875
876     pthread_mutex_lock(&webLock);
877     
878     // This used for any thread other than the web thread.
879     otherThreadLockCount++;
880     webThreadShouldYield = false;
881 }
882
883 void WebThreadUnlockFromAnyThread()
884 {
885     if (!webThreadStarted)
886         return;
887     ASSERT(!WebThreadIsCurrent());
888     // No-op except from a secondary thread.
889     if (pthread_main_np())
890         return;
891     
892     ASSERT(otherThreadLockCount);
893     otherThreadLockCount--;
894     
895     int result;
896     result = pthread_mutex_unlock(&webLock); 
897     ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result);
898 }
899
900 void WebThreadUnlockGuardForMail()
901 {
902     ASSERT(!WebThreadIsCurrent());
903
904     CFRunLoopObserverRef mainRunLoopUnlockGuardObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopEntry, YES, 0, MainRunLoopUnlockGuard, NULL);
905     CFRunLoopAddObserver(CFRunLoopGetMain(), mainRunLoopUnlockGuardObserver, kCFRunLoopCommonModes);
906     CFRelease(mainRunLoopUnlockGuardObserver);
907 }
908
909 void _WebThreadUnlock(void)
910 {
911 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
912     lockCount--;
913 #if LOG_WEB_LOCK
914     NSLog(@"unlock %d, web-thread: %d", lockCount, WebThreadIsCurrent());
915 #endif
916 #endif
917     
918     if (!WebThreadIsCurrent()) {
919         ASSERT(mainThreadLockCount != 0);
920         mainThreadLockCount--;
921     } else {    
922         webThreadLockCount--;
923         webThreadShouldYield = false;
924     }
925     
926     int result;
927     result = pthread_mutex_unlock(&webLock); 
928     ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result);    
929 }
930
931 bool WebThreadIsLocked(void)
932 {
933     if (WebThreadIsCurrent())
934         return webThreadLockCount;
935     else if (pthread_main_np())
936         return mainThreadLockCount;
937     else
938         return otherThreadLockCount;
939 }
940
941 bool WebThreadIsLockedOrDisabled(void)
942 {
943     return !WebThreadIsEnabled() || WebThreadIsLocked();
944 }
945
946 void WebThreadLockPushModal(void)
947 {
948     if (WebThreadIsCurrent())
949         return;
950     
951     ASSERT(WebThreadIsLocked());
952     ++sMainThreadModalCount;
953 }
954
955 void WebThreadLockPopModal(void)
956 {
957     if (WebThreadIsCurrent())
958         return;
959     
960     ASSERT(WebThreadIsLocked());
961     ASSERT(sMainThreadModalCount != 0);
962     --sMainThreadModalCount;
963 }
964
965 CFRunLoopRef WebThreadRunLoop(void)
966 {
967     if (webThreadStarted) {
968         ASSERT(webThreadRunLoop);
969         return webThreadRunLoop;
970     }
971     
972     return CFRunLoopGetCurrent();
973 }
974
975 NSRunLoop* WebThreadNSRunLoop(void)
976 {
977     if (webThreadStarted) {
978         ASSERT(webThreadNSRunLoop);
979         return webThreadNSRunLoop;
980     }
981
982     return [NSRunLoop currentRunLoop];
983 }
984
985 WebThreadContext *WebThreadCurrentContext(void)
986 {
987     return CurrentThreadContext();
988 }
989
990 bool WebThreadContextIsCurrent(void)
991 {   
992     return WebThreadCurrentContext() == webThreadContext;
993 }
994
995 void WebThreadSetDelegateSourceRunLoopMode(CFStringRef mode)
996 {
997     ASSERT(!webThreadStarted);
998     delegateSourceRunLoopMode = mode;
999 }
1000
1001 void WebThreadEnable(void)
1002 {
1003     RELEASE_ASSERT_WITH_MESSAGE(!WebCore::applicationIsWebProcess(), "The WebProcess should never run a Web Thread");
1004
1005     static pthread_once_t initControl = PTHREAD_ONCE_INIT;
1006     pthread_once(&initControl, StartWebThread);
1007 }
1008
1009 bool WebThreadIsEnabled(void)
1010 {
1011     return webThreadStarted;
1012 }
1013
1014 bool WebThreadIsCurrent(void)
1015 {
1016     return webThreadStarted && pthread_equal(webThread, pthread_self());
1017 }
1018
1019 bool WebThreadNotCurrent(void)
1020 {
1021     return webThreadStarted && !pthread_equal(webThread, pthread_self());
1022 }
1023
1024 #endif // PLATFORM(IOS)