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