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