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