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