2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #import "WebCoreThread.h"
31 #import "FloatingPointEnvironment.h"
32 #import "JSDOMWindowBase.h"
33 #import "ThreadGlobalData.h"
34 #import "RuntimeApplicationChecksIOS.h"
35 #import "WebCoreThreadInternal.h"
36 #import "WebCoreThreadMessage.h"
37 #import "WebCoreThreadRun.h"
38 #import "WebCoreThreadSafe.h"
39 #import "WKUtilities.h"
41 #import <runtime/InitializeThreading.h>
42 #import <runtime/JSLock.h>
43 #import <wtf/Assertions.h>
44 #import <wtf/MainThread.h>
45 #import <wtf/Threading.h>
46 #import <wtf/text/AtomicString.h>
48 #import <Foundation/NSInvocation.h>
49 #import <libkern/OSAtomic.h>
50 #import <objc/runtime.h>
52 #define LOG_MESSAGES 0
53 #define LOG_WEB_LOCK 0
54 #define LOG_MAIN_THREAD_LOCKING 0
55 #define LOG_RELEASES 0
57 #define DistantFuture (86400.0 * 2000 * 365.2425 + 86400.0) // same as +[NSDate distantFuture]
59 #define DrawWaitInterval 10
60 #define DelegateWaitInterval 10
62 static void _WebThreadAutoLock(void);
63 static bool _WebTryThreadLock(bool shouldTry);
64 static void _WebThreadLockFromAnyThread(bool shouldLog);
65 static void _WebThreadUnlock(void);
67 @interface NSObject(ForwardDeclarations)
68 -(void)_webcore_releaseOnWebThread;
69 -(void)_webcore_releaseWithWebThreadLock;
72 @implementation NSObject(WebCoreThreadAdditions)
74 - (void)releaseOnMainThread {
75 if ([NSThread isMainThread]) {
78 [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO];
84 typedef void *NSAutoreleasePoolMark;
88 extern NSAutoreleasePoolMark NSPushAutoreleasePool(unsigned ignored);
89 extern void NSPopAutoreleasePool(NSAutoreleasePoolMark token);
94 static int WebTimedConditionLock (pthread_cond_t *condition, pthread_mutex_t *lock, CFAbsoluteTime interval);
96 static pthread_mutex_t webLock;
97 static NSAutoreleasePoolMark autoreleasePoolMark;
98 static CFRunLoopRef webThreadRunLoop;
99 static NSRunLoop* webThreadNSRunLoop;
100 static pthread_t webThread;
101 static BOOL isWebThreadLocked;
102 static BOOL webThreadStarted;
103 static unsigned webThreadLockCount;
105 static NSAutoreleasePoolMark savedAutoreleasePoolMark;
106 static BOOL isNestedWebThreadRunLoop;
108 PushOrPopAutoreleasePool,
109 IgnoreAutoreleasePool
110 } AutoreleasePoolOperation;
112 static pthread_mutex_t WebThreadReleaseLock = PTHREAD_MUTEX_INITIALIZER;
113 static CFRunLoopSourceRef WebThreadReleaseSource;
114 static CFMutableArrayRef WebThreadReleaseObjArray;
116 static void MainThreadAdoptAndRelease(id obj);
118 static pthread_mutex_t delegateLock = PTHREAD_MUTEX_INITIALIZER;
119 static pthread_cond_t delegateCondition = PTHREAD_COND_INITIALIZER;
120 static NSInvocation *delegateInvocation;
121 static CFRunLoopSourceRef delegateSource = NULL;
122 static BOOL delegateHandled;
123 #if LOG_MAIN_THREAD_LOCKING
124 static BOOL sendingDelegateMessage;
127 static CFRunLoopObserverRef mainRunLoopAutoUnlockObserver;
129 static pthread_mutex_t startupLock = PTHREAD_MUTEX_INITIALIZER;
130 static pthread_cond_t startupCondition = PTHREAD_COND_INITIALIZER;
132 static WebThreadContext *webThreadContext;
133 static pthread_key_t threadContextKey;
134 static unsigned mainThreadLockCount;
135 static unsigned otherThreadLockCount;
136 static unsigned sMainThreadModalCount;
138 volatile bool webThreadShouldYield;
140 static pthread_mutex_t WebCoreReleaseLock;
141 static void WebCoreObjCDeallocOnWebThreadImpl(id self, SEL _cmd);
142 static void WebCoreObjCDeallocWithWebThreadLockImpl(id self, SEL _cmd);
144 static NSMutableArray *sAsyncDelegates = nil;
146 static CFStringRef delegateSourceRunLoopMode;
148 static inline void SendMessage(NSInvocation *invocation)
151 MainThreadAdoptAndRelease(invocation);
154 static void HandleDelegateSource(void *info)
157 ASSERT(!WebThreadIsCurrent());
159 #if LOG_MAIN_THREAD_LOCKING
160 sendingDelegateMessage = YES;
163 _WebThreadAutoLock();
165 int result = pthread_mutex_lock(&delegateLock);
166 ASSERT_WITH_MESSAGE(result == 0, "delegate lock failed with code:%d", result);
169 if ([[delegateInvocation target] isKindOfClass:[NSNotificationCenter class]]) {
171 [delegateInvocation getArgument:&argument0 atIndex:0];
172 NSLog(@"notification receive: %@", argument0);
174 NSLog(@"delegate receive: %@", NSStringFromSelector([delegateInvocation selector]));
178 SendMessage(delegateInvocation);
180 delegateHandled = YES;
181 pthread_cond_signal(&delegateCondition);
183 result = pthread_mutex_unlock(&delegateLock);
184 ASSERT_WITH_MESSAGE(result == 0, "delegate unlock failed with code:%d", result);
186 #if LOG_MAIN_THREAD_LOCKING
187 sendingDelegateMessage = NO;
191 static void SendDelegateMessage(NSInvocation *invocation)
193 if (WebThreadIsCurrent()) {
194 ASSERT(delegateSource);
195 int result = pthread_mutex_lock(&delegateLock);
196 ASSERT_WITH_MESSAGE(result == 0, "delegate lock failed with code:%d", result);
198 delegateInvocation = invocation;
199 delegateHandled = NO;
202 if ([[delegateInvocation target] isKindOfClass:[NSNotificationCenter class]]) {
204 [delegateInvocation getArgument:&argument0 atIndex:0];
205 NSLog(@"notification send: %@", argument0);
207 NSLog(@"delegate send: %@", NSStringFromSelector([delegateInvocation selector]));
212 // Code block created to scope JSC::JSLock::DropAllLocks outside of WebThreadLock()
213 JSC::JSLock::DropAllLocks dropAllLocks(WebCore::JSDOMWindowBase::commonVM());
216 CFRunLoopSourceSignal(delegateSource);
217 CFRunLoopWakeUp(CFRunLoopGetMain());
219 while (!delegateHandled) {
220 if (WebTimedConditionLock(&delegateCondition, &delegateLock, DelegateWaitInterval) != 0) {
221 id delegateInformation;
222 if ([[delegateInvocation target] isKindOfClass:[NSNotificationCenter class]])
223 [delegateInvocation getArgument:&delegateInformation atIndex:0];
225 delegateInformation = NSStringFromSelector([delegateInvocation selector]);
227 CFStringRef mode = CFRunLoopCopyCurrentMode(CFRunLoopGetMain());
228 NSLog(@"%s: delegate (%@) failed to return after waiting %d seconds. main run loop mode: %@", __PRETTY_FUNCTION__, delegateInformation, DelegateWaitInterval, mode);
233 result = pthread_mutex_unlock(&delegateLock);
235 ASSERT_WITH_MESSAGE(result == 0, "delegate unlock failed with code:%d", result);
236 _WebTryThreadLock(false);
239 SendMessage(invocation);
243 void WebThreadRunOnMainThread(void(^delegateBlock)())
245 if (!WebThreadIsCurrent()) {
246 ASSERT(pthread_main_np());
251 JSC::JSLock::DropAllLocks dropAllLocks(WebCore::JSDOMWindowBase::commonVM());
254 void (^delegateBlockCopy)() = Block_copy(delegateBlock);
255 dispatch_sync(dispatch_get_main_queue(), delegateBlockCopy);
256 Block_release(delegateBlockCopy);
258 _WebTryThreadLock(false);
261 static void MainThreadAdoptAndRelease(id obj)
263 if (!WebThreadIsEnabled() || CFRunLoopGetMain() == CFRunLoopGetCurrent()) {
268 NSLog(@"Release send [web thread] : %@", obj);
270 // We own obj at this point, so we don't need the block to implicitly
272 __block id objNotRetained = obj;
273 dispatch_async(dispatch_get_main_queue(), ^{
274 [objNotRetained release];
278 void WebThreadAdoptAndRelease(id obj)
280 ASSERT(!WebThreadIsCurrent());
281 ASSERT(WebThreadReleaseSource);
284 NSLog(@"Release send [main thread]: %@", obj);
287 int result = pthread_mutex_lock(&WebThreadReleaseLock);
288 ASSERT_WITH_MESSAGE(result == 0, "Release lock failed with code:%d", result);
290 if (WebThreadReleaseObjArray == nil)
291 WebThreadReleaseObjArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL);
292 CFArrayAppendValue(WebThreadReleaseObjArray, obj);
293 CFRunLoopSourceSignal(WebThreadReleaseSource);
294 CFRunLoopWakeUp(webThreadRunLoop);
296 result = pthread_mutex_unlock(&WebThreadReleaseLock);
297 ASSERT_WITH_MESSAGE(result == 0, "Release unlock failed with code:%d", result);
300 static inline void lockWebCoreReleaseLock()
302 int lockcode = pthread_mutex_lock(&WebCoreReleaseLock);
303 #pragma unused (lockcode)
304 ASSERT_WITH_MESSAGE(lockcode == 0, "WebCoreReleaseLock lock failed with code:%d", lockcode);
307 static inline void unlockWebCoreReleaseLock()
309 int lockcode = pthread_mutex_unlock(&WebCoreReleaseLock);
310 #pragma unused (lockcode)
311 ASSERT_WITH_MESSAGE(lockcode == 0, "WebCoreReleaseLock unlock failed with code:%d", lockcode);
314 void WebCoreObjCDeallocOnWebThread(Class cls)
316 SEL releaseSEL = @selector(release);
317 SEL webThreadReleaseSEL = @selector(_webcore_releaseOnWebThread);
319 // get the existing release method
320 Method releaseMethod = class_getInstanceMethod(cls, releaseSEL);
321 if (!releaseMethod) {
322 ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocOnWebThread() failed to find %s for %@", releaseSEL, NSStringFromClass(cls));
326 // add the implementation that ensures release WebThread release/deallocation
327 if (!class_addMethod(cls, webThreadReleaseSEL, (IMP)WebCoreObjCDeallocOnWebThreadImpl, method_getTypeEncoding(releaseMethod))) {
328 ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocOnWebThread() failed to add %s for %@", webThreadReleaseSEL, NSStringFromClass(cls));
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);
336 // swizzle the old release for the new implementation
337 method_exchangeImplementations(releaseMethod, class_getInstanceMethod(cls, webThreadReleaseSEL));
340 void WebCoreObjCDeallocWithWebThreadLock(Class cls)
342 SEL releaseSEL = @selector(release);
343 SEL webThreadLockReleaseSEL = @selector(_webcore_releaseWithWebThreadLock);
345 // get the existing release method
346 Method releaseMethod = class_getInstanceMethod(cls, releaseSEL);
347 if (!releaseMethod) {
348 ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocWithWebThreadLock() failed to find %s for %@", releaseSEL, NSStringFromClass(cls));
352 // add the implementation that ensures release WebThreadLock release/deallocation
353 if (!class_addMethod(cls, webThreadLockReleaseSEL, (IMP)WebCoreObjCDeallocWithWebThreadLockImpl, method_getTypeEncoding(releaseMethod))) {
354 ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocWithWebThreadLock() failed to add %s for %@", webThreadLockReleaseSEL, NSStringFromClass(cls));
358 // ensure the implementation exists at cls in the class hierarchy
359 if (class_addMethod(cls, releaseSEL, class_getMethodImplementation(cls, releaseSEL), method_getTypeEncoding(releaseMethod)))
360 releaseMethod = class_getInstanceMethod(cls, releaseSEL);
362 // swizzle the old release for the new implementation
363 method_exchangeImplementations(releaseMethod, class_getInstanceMethod(cls, webThreadLockReleaseSEL));
366 void WebCoreObjCDeallocOnWebThreadImpl(id self, SEL _cmd)
369 if (!WebThreadIsEnabled())
370 [self _webcore_releaseOnWebThread];
372 lockWebCoreReleaseLock();
373 if ([self retainCount] == 1) {
374 // This is the only reference retaining the object, so we can
375 // safely release the WebCoreReleaseLock now.
376 unlockWebCoreReleaseLock();
377 if (WebThreadIsCurrent())
378 [self _webcore_releaseOnWebThread];
380 WebThreadAdoptAndRelease(self);
382 // This is not the only reference retaining the object, so another
383 // thread could also call release - hold the lock whilst calling
384 // release to avoid a race condition.
385 [self _webcore_releaseOnWebThread];
386 unlockWebCoreReleaseLock();
391 void WebCoreObjCDeallocWithWebThreadLockImpl(id self, SEL _cmd)
394 lockWebCoreReleaseLock();
395 if (WebThreadIsLockedOrDisabled() || 1 != [self retainCount])
396 [self _webcore_releaseWithWebThreadLock];
398 WebThreadAdoptAndRelease(self);
399 unlockWebCoreReleaseLock();
402 static void HandleWebThreadReleaseSource(void *info)
405 ASSERT(WebThreadIsCurrent());
407 int result = pthread_mutex_lock(&WebThreadReleaseLock);
408 ASSERT_WITH_MESSAGE(result == 0, "Release lock failed with code:%d", result);
410 CFMutableArrayRef objects = NULL;
411 if (CFArrayGetCount(WebThreadReleaseObjArray)) {
412 objects = CFArrayCreateMutableCopy(NULL, 0, WebThreadReleaseObjArray);
413 CFArrayRemoveAllValues(WebThreadReleaseObjArray);
416 result = pthread_mutex_unlock(&WebThreadReleaseLock);
417 ASSERT_WITH_MESSAGE(result == 0, "Release unlock failed with code:%d", result);
422 unsigned count = CFArrayGetCount(objects);
424 for (i = 0; i < count; i++) {
425 id obj = (id)CFArrayGetValueAtIndex(objects, i);
427 NSLog(@"Release recv [web thread] : %@", obj);
435 void WebThreadCallDelegate(NSInvocation *invocation)
437 // NSInvocation released in SendMessage()
438 SendDelegateMessage([invocation retain]);
441 void WebThreadPostNotification(NSString *name, id object, id userInfo)
443 if (pthread_main_np())
444 [[NSNotificationCenter defaultCenter] postNotificationName:name object:object userInfo:userInfo];
446 dispatch_async(dispatch_get_main_queue(), ^ {
447 [[NSNotificationCenter defaultCenter] postNotificationName:name object:object userInfo:userInfo];
452 void WebThreadCallDelegateAsync(NSInvocation *invocation)
455 if (WebThreadIsCurrent())
456 [sAsyncDelegates addObject:invocation];
458 WebThreadCallDelegate(invocation);
461 // Note: despite the name, returns an autoreleased object.
462 NSInvocation *WebThreadMakeNSInvocation(id target, SEL selector)
464 NSMethodSignature *signature = [target methodSignatureForSelector:selector];
467 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
468 [invocation setSelector:selector];
469 [invocation setTarget:target];
470 [invocation retainArguments];
476 static void MainRunLoopAutoUnlock(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context)
478 UNUSED_PARAM(observer);
479 UNUSED_PARAM(activity);
480 UNUSED_PARAM(context);
481 ASSERT(!WebThreadIsCurrent());
483 if (sMainThreadModalCount != 0)
486 CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes);
491 static void _WebThreadAutoLock(void)
493 ASSERT(!WebThreadIsCurrent());
495 if (mainThreadLockCount == 0) {
496 CFRunLoopAddObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes);
497 _WebTryThreadLock(false);
498 CFRunLoopWakeUp(CFRunLoopGetMain());
502 static void WebRunLoopLockInternal(AutoreleasePoolOperation poolOperation)
504 _WebTryThreadLock(false);
505 if (poolOperation == PushOrPopAutoreleasePool)
506 autoreleasePoolMark = NSPushAutoreleasePool(0);
507 isWebThreadLocked = YES;
510 static void WebRunLoopUnlockInternal(AutoreleasePoolOperation poolOperation)
512 ASSERT(sAsyncDelegates);
513 if ([sAsyncDelegates count]) {
514 for (NSInvocation* invocation in sAsyncDelegates)
515 SendDelegateMessage([invocation retain]);
516 [sAsyncDelegates removeAllObjects];
519 if (poolOperation == PushOrPopAutoreleasePool)
520 NSPopAutoreleasePool(autoreleasePoolMark);
523 isWebThreadLocked = NO;
526 static void WebRunLoopLock(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context)
528 UNUSED_PARAM(observer);
529 UNUSED_PARAM(context);
530 ASSERT(WebThreadIsCurrent());
531 ASSERT_UNUSED(activity, activity == kCFRunLoopAfterWaiting || activity == kCFRunLoopBeforeTimers || activity == kCFRunLoopBeforeSources);
533 // If the WebThread is locked by the main thread then we want to
534 // grab the lock ourselves when the main thread releases the lock.
535 if (isWebThreadLocked && !mainThreadLockCount)
537 WebRunLoopLockInternal(PushOrPopAutoreleasePool);
540 static void WebRunLoopUnlock(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context)
542 UNUSED_PARAM(observer);
543 UNUSED_PARAM(context);
544 ASSERT(WebThreadIsCurrent());
545 ASSERT_UNUSED(activity, activity == kCFRunLoopBeforeWaiting || activity == kCFRunLoopExit);
546 ASSERT(!mainThreadLockCount);
548 if (!isWebThreadLocked)
550 WebRunLoopUnlockInternal(PushOrPopAutoreleasePool);
553 static void MainRunLoopUnlockGuard(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context)
555 UNUSED_PARAM(observer);
556 UNUSED_PARAM(activity);
557 UNUSED_PARAM(context);
558 ASSERT(!WebThreadIsCurrent());
560 // We shouldn't have the web lock at this point. However, MobileMail sometimes
561 // get to a state where the main thread has web lock but it didn't release it on last
562 // runloop exit, and web thread gets stuck at waiting for the lock. If this happens,
563 // we need to help release the lock. See <rdar://problem/8005192>.
564 if (mainThreadLockCount != 0 && sMainThreadModalCount == 0) {
565 NSLog(@"WARNING: Main thread didn't release the lock at last runloop exit!");
567 MainRunLoopAutoUnlock(observer, activity, context);
568 if (mainThreadLockCount != 0)
569 mainThreadLockCount = 0;
573 static void _WebRunLoopEnableNestedFromMainThread()
575 CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes);
578 static void _WebRunLoopDisableNestedFromMainThread()
580 CFRunLoopAddObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes);
583 void WebRunLoopEnableNested()
585 if (!WebThreadIsEnabled())
588 ASSERT(!isNestedWebThreadRunLoop);
590 if (!WebThreadIsCurrent())
591 _WebRunLoopEnableNestedFromMainThread();
593 savedAutoreleasePoolMark = autoreleasePoolMark;
594 autoreleasePoolMark = 0;
595 WebRunLoopUnlockInternal(IgnoreAutoreleasePool);
596 isNestedWebThreadRunLoop = YES;
599 void WebRunLoopDisableNested()
601 if (!WebThreadIsEnabled())
604 ASSERT(isNestedWebThreadRunLoop);
606 if (!WebThreadIsCurrent())
607 _WebRunLoopDisableNestedFromMainThread();
609 autoreleasePoolMark = savedAutoreleasePoolMark;
610 savedAutoreleasePoolMark = 0;
611 WebRunLoopLockInternal(IgnoreAutoreleasePool);
612 isNestedWebThreadRunLoop = NO;
615 static void FreeThreadContext(void *threadContext)
617 if (threadContext != NULL)
621 static void InitThreadContextKey()
623 pthread_key_create(&threadContextKey, FreeThreadContext);
626 static WebThreadContext *CurrentThreadContext(void)
628 static pthread_once_t initControl = PTHREAD_ONCE_INIT;
629 pthread_once(&initControl, InitThreadContextKey);
631 WebThreadContext *threadContext = (WebThreadContext*)pthread_getspecific(threadContextKey);
632 if (threadContext == NULL) {
633 threadContext = (WebThreadContext *)calloc(sizeof(WebThreadContext), 1);
634 pthread_setspecific(threadContextKey, threadContext);
636 return threadContext;
643 void *RunWebThread(void *arg)
645 FloatingPointEnvironment::shared().propagateMainThreadEnvironment();
648 // WTF::initializeMainThread() needs to be called before JSC::initializeThreading() since the
649 // code invoked by the latter needs to know if it's running on the WebThread. See
650 // <rdar://problem/8502487>.
651 WTF::initializeMainThread();
652 WTF::initializeWebThread();
653 JSC::initializeThreading();
655 // Make sure that the WebThread and the main thread share the same ThreadGlobalData objects.
656 WebCore::threadGlobalData().setWebCoreThreadData();
657 initializeWebThreadIdentifier();
659 #if HAVE(PTHREAD_SETNAME_NP)
660 pthread_setname_np("WebThread");
663 webThreadContext = CurrentThreadContext();
664 webThreadRunLoop = CFRunLoopGetCurrent();
665 webThreadNSRunLoop = [[NSRunLoop currentRunLoop] retain];
667 CFRunLoopObserverRef webRunLoopLockObserverRef = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeTimers|kCFRunLoopBeforeSources|kCFRunLoopAfterWaiting, YES, 0, WebRunLoopLock, NULL);
668 CFRunLoopAddObserver(webThreadRunLoop, webRunLoopLockObserverRef, kCFRunLoopCommonModes);
669 CFRelease(webRunLoopLockObserverRef);
671 WebThreadInitRunQueue();
673 // We must have the lock when CA paints in the web thread. CA commits at 2000000 so we use larger order number than that to free the lock.
674 CFRunLoopObserverRef webRunLoopUnlockObserverRef = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting|kCFRunLoopExit, YES, 2500000, WebRunLoopUnlock, NULL);
675 CFRunLoopAddObserver(webThreadRunLoop, webRunLoopUnlockObserverRef, kCFRunLoopCommonModes);
676 CFRelease(webRunLoopUnlockObserverRef);
678 CFRunLoopSourceContext ReleaseSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleWebThreadReleaseSource};
679 WebThreadReleaseSource = CFRunLoopSourceCreate(NULL, -1, &ReleaseSourceContext);
680 CFRunLoopAddSource(webThreadRunLoop, WebThreadReleaseSource, kCFRunLoopDefaultMode);
682 int result = pthread_mutex_lock(&startupLock);
683 ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result);
685 result = pthread_cond_signal(&startupCondition);
686 ASSERT_WITH_MESSAGE(result == 0, "startup signal failed with code:%d", result);
688 result = pthread_mutex_unlock(&startupLock);
689 ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result);
692 CFRunLoopRunInMode(kCFRunLoopDefaultMode, DistantFuture, true);
699 static void StartWebThread()
701 webThreadStarted = TRUE;
703 // Initialize ThreadGlobalData on the main UI thread so that the WebCore thread
704 // can later set it's thread-specific data to point to the same objects.
705 WebCore::ThreadGlobalData& unused = WebCore::threadGlobalData();
708 // Initialize AtomicString on the main thread.
709 WTF::AtomicString::init();
711 // register class for WebThread deallocation
712 WebCoreObjCDeallocOnWebThread([DOMObject class]);
713 WebCoreObjCDeallocOnWebThread([WAKWindow class]);
714 WebCoreObjCDeallocWithWebThreadLock([WAKView class]);
716 pthread_mutexattr_t mattr;
717 pthread_mutexattr_init(&mattr);
718 pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
719 pthread_mutex_init(&webLock, &mattr);
720 pthread_mutexattr_destroy(&mattr);
722 pthread_mutexattr_t mutex_attr;
723 pthread_mutexattr_init(&mutex_attr);
724 pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE);
725 pthread_mutex_init(&WebCoreReleaseLock, &mutex_attr);
726 pthread_mutexattr_destroy(&mutex_attr);
728 CFRunLoopRef runLoop = CFRunLoopGetCurrent();
729 CFRunLoopSourceContext delegateSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleDelegateSource};
730 delegateSource = CFRunLoopSourceCreate(NULL, 0, &delegateSourceContext);
731 // We shouldn't get delegate callbacks while scrolling, but there might be
732 // one outstanding when we start. Add the source for all common run loop
733 // modes so we don't block the web thread while scrolling.
734 if (!delegateSourceRunLoopMode)
735 delegateSourceRunLoopMode = kCFRunLoopCommonModes;
736 CFRunLoopAddSource(runLoop, delegateSource, delegateSourceRunLoopMode);
738 sAsyncDelegates = [[NSMutableArray alloc] init];
740 mainRunLoopAutoUnlockObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting | kCFRunLoopExit, YES, 3000001, MainRunLoopAutoUnlock, NULL);
742 pthread_attr_t tattr;
743 pthread_attr_init(&tattr);
744 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
745 // The web thread is a secondary thread, and secondary threads are usually given
746 // a 512 kb stack, but we need more space in order to have room for the JavaScriptCore
747 // reentrancy limit. This limit works on both the simulator and the device.
748 pthread_attr_setstacksize(&tattr, 200 * 4096);
750 struct sched_param param;
751 pthread_attr_getschedparam(&tattr, ¶m);
752 param.sched_priority--;
753 pthread_attr_setschedparam(&tattr, ¶m);
755 // Wait for the web thread to startup completely before we continue.
756 int result = pthread_mutex_lock(&startupLock);
757 ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result);
759 // Propagate the mainThread's fenv to workers & the web thread.
760 FloatingPointEnvironment::shared().saveMainThreadEnvironment();
762 pthread_create(&webThread, &tattr, RunWebThread, NULL);
763 pthread_attr_destroy(&tattr);
765 result = pthread_cond_wait(&startupCondition, &startupLock);
766 ASSERT_WITH_MESSAGE(result == 0, "startup wait failed with code:%d", result);
768 result = pthread_mutex_unlock(&startupLock);
769 ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result);
771 initializeApplicationUIThreadIdentifier();
774 static int WebTimedConditionLock (pthread_cond_t *condition, pthread_mutex_t *lock, CFAbsoluteTime interval)
776 struct timespec time;
777 CFAbsoluteTime at = CFAbsoluteTimeGetCurrent() + interval;
778 time.tv_sec = (time_t)(floor(at) + kCFAbsoluteTimeIntervalSince1970);
779 time.tv_nsec = (int32_t)((at - floor(at)) * 1000000000.0);
780 return pthread_cond_timedwait(condition, lock, &time);
784 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
785 static unsigned lockCount;
788 static bool _WebTryThreadLock(bool shouldTry)
790 // Suspend the web thread if the main thread is trying to lock.
791 bool onMainThread = pthread_main_np();
793 webThreadShouldYield = true;
794 else if (!WebThreadIsCurrent()) {
795 NSLog(@"%s, %p: Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...", __PRETTY_FUNCTION__, CurrentThreadContext());
803 result = pthread_mutex_trylock(&webLock);
804 if (result == EBUSY) {
807 ASSERT_WITH_MESSAGE(result == 0, "try web lock failed with code:%d", result);
810 result = pthread_mutex_lock(&webLock);
811 ASSERT_WITH_MESSAGE(result == 0, "web lock failed with code:%d", result);
815 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
818 NSLog(@"lock %d, web-thread: %d", lockCount, WebThreadIsCurrent());
822 ASSERT(CFRunLoopGetCurrent() == CFRunLoopGetMain());
823 webThreadShouldYield = false;
824 mainThreadLockCount++;
825 #if LOG_MAIN_THREAD_LOCKING
826 if (!sendingDelegateMessage && lockCount == 1)
827 NSLog(@"Main thread locking outside of delegate messages.");
830 webThreadLockCount++;
831 if (webThreadLockCount > 1) {
832 NSLog(@"%s, %p: Multiple locks on web thread not allowed! Please file a bug. Crashing now...", __PRETTY_FUNCTION__, CurrentThreadContext());
841 void WebThreadLock(void)
843 if (!webThreadStarted || pthread_equal(webThread, pthread_self()))
845 _WebThreadAutoLock();
848 void WebThreadUnlock(void)
850 // This is a no-op, we unlock automatically on top of the runloop
851 ASSERT(!WebThreadIsCurrent());
854 void WebThreadLockFromAnyThread()
856 _WebThreadLockFromAnyThread(true);
859 void WebThreadLockFromAnyThreadNoLog()
861 _WebThreadLockFromAnyThread(false);
864 static void _WebThreadLockFromAnyThread(bool shouldLog)
866 if (!webThreadStarted)
868 ASSERT(!WebThreadIsCurrent());
869 if (pthread_main_np()) {
870 _WebThreadAutoLock();
874 NSLog(@"%s, %p: Obtaining the web lock from a thread other than the main thread or the web thread. UIKit should not be called from a secondary thread.", __PRETTY_FUNCTION__, CurrentThreadContext());
876 pthread_mutex_lock(&webLock);
878 // This used for any thread other than the web thread.
879 otherThreadLockCount++;
880 webThreadShouldYield = false;
883 void WebThreadUnlockFromAnyThread()
885 if (!webThreadStarted)
887 ASSERT(!WebThreadIsCurrent());
888 // No-op except from a secondary thread.
889 if (pthread_main_np())
892 ASSERT(otherThreadLockCount);
893 otherThreadLockCount--;
896 result = pthread_mutex_unlock(&webLock);
897 ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result);
900 void WebThreadUnlockGuardForMail()
902 ASSERT(!WebThreadIsCurrent());
904 CFRunLoopObserverRef mainRunLoopUnlockGuardObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopEntry, YES, 0, MainRunLoopUnlockGuard, NULL);
905 CFRunLoopAddObserver(CFRunLoopGetMain(), mainRunLoopUnlockGuardObserver, kCFRunLoopCommonModes);
906 CFRelease(mainRunLoopUnlockGuardObserver);
909 void _WebThreadUnlock(void)
911 #if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING
914 NSLog(@"unlock %d, web-thread: %d", lockCount, WebThreadIsCurrent());
918 if (!WebThreadIsCurrent()) {
919 ASSERT(mainThreadLockCount != 0);
920 mainThreadLockCount--;
922 webThreadLockCount--;
923 webThreadShouldYield = false;
927 result = pthread_mutex_unlock(&webLock);
928 ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result);
931 bool WebThreadIsLocked(void)
933 if (WebThreadIsCurrent())
934 return webThreadLockCount;
935 else if (pthread_main_np())
936 return mainThreadLockCount;
938 return otherThreadLockCount;
941 bool WebThreadIsLockedOrDisabled(void)
943 return !WebThreadIsEnabled() || WebThreadIsLocked();
946 void WebThreadLockPushModal(void)
948 if (WebThreadIsCurrent())
951 ASSERT(WebThreadIsLocked());
952 ++sMainThreadModalCount;
955 void WebThreadLockPopModal(void)
957 if (WebThreadIsCurrent())
960 ASSERT(WebThreadIsLocked());
961 ASSERT(sMainThreadModalCount != 0);
962 --sMainThreadModalCount;
965 CFRunLoopRef WebThreadRunLoop(void)
967 if (webThreadStarted) {
968 ASSERT(webThreadRunLoop);
969 return webThreadRunLoop;
972 return CFRunLoopGetCurrent();
975 NSRunLoop* WebThreadNSRunLoop(void)
977 if (webThreadStarted) {
978 ASSERT(webThreadNSRunLoop);
979 return webThreadNSRunLoop;
982 return [NSRunLoop currentRunLoop];
985 WebThreadContext *WebThreadCurrentContext(void)
987 return CurrentThreadContext();
990 bool WebThreadContextIsCurrent(void)
992 return WebThreadCurrentContext() == webThreadContext;
995 void WebThreadSetDelegateSourceRunLoopMode(CFStringRef mode)
997 ASSERT(!webThreadStarted);
998 delegateSourceRunLoopMode = mode;
1001 void WebThreadEnable(void)
1003 RELEASE_ASSERT_WITH_MESSAGE(!WebCore::applicationIsWebProcess(), "The WebProcess should never run a Web Thread");
1005 static pthread_once_t initControl = PTHREAD_ONCE_INIT;
1006 pthread_once(&initControl, StartWebThread);
1009 bool WebThreadIsEnabled(void)
1011 return webThreadStarted;
1014 bool WebThreadIsCurrent(void)
1016 return webThreadStarted && pthread_equal(webThread, pthread_self());
1019 bool WebThreadNotCurrent(void)
1021 return webThreadStarted && !pthread_equal(webThread, pthread_self());
1024 #endif // PLATFORM(IOS)