1/* 2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#import "config.h" 27#import "WebCoreThread.h" 28 29#if PLATFORM(IOS) 30 31#import "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" 40 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> 47 48#import <CoreFoundation/CFPriv.h> 49#import <Foundation/NSInvocation.h> 50#import <libkern/OSAtomic.h> 51#import <objc/runtime.h> 52 53#define LOG_MESSAGES 0 54#define LOG_WEB_LOCK 0 55#define LOG_MAIN_THREAD_LOCKING 0 56#define LOG_RELEASES 0 57 58#define DistantFuture (86400.0 * 2000 * 365.2425 + 86400.0) // same as +[NSDate distantFuture] 59#define MaxArgCount 5 60#define DrawWaitInterval 10 61#define DelegateWaitInterval 10 62 63static void _WebThreadAutoLock(void); 64static bool _WebTryThreadLock(bool shouldTry); 65static void _WebThreadLockFromAnyThread(bool shouldLog); 66static void _WebThreadUnlock(void); 67 68@interface NSObject(ForwardDeclarations) 69-(void)_webcore_releaseOnWebThread; 70-(void)_webcore_releaseWithWebThreadLock; 71@end 72 73@implementation NSObject(WebCoreThreadAdditions) 74 75- (void)releaseOnMainThread { 76 if ([NSThread isMainThread]) { 77 [self release]; 78 } else { 79 [self performSelectorOnMainThread:@selector(release) withObject:nil waitUntilDone:NO]; 80 } 81} 82 83@end 84 85typedef void *NSAutoreleasePoolMark; 86#ifdef __cplusplus 87extern "C" { 88#endif 89extern NSAutoreleasePoolMark NSPushAutoreleasePool(unsigned ignored); 90extern void NSPopAutoreleasePool(NSAutoreleasePoolMark token); 91#ifdef __cplusplus 92} 93#endif 94 95static int WebTimedConditionLock (pthread_cond_t *condition, pthread_mutex_t *lock, CFAbsoluteTime interval); 96 97static pthread_mutex_t webLock; 98static NSAutoreleasePoolMark autoreleasePoolMark; 99static CFRunLoopRef webThreadRunLoop; 100static NSRunLoop* webThreadNSRunLoop; 101static pthread_t webThread; 102static BOOL isWebThreadLocked; 103static BOOL webThreadStarted; 104static unsigned webThreadLockCount; 105 106static NSAutoreleasePoolMark savedAutoreleasePoolMark; 107static BOOL isNestedWebThreadRunLoop; 108typedef enum { 109 PushOrPopAutoreleasePool, 110 IgnoreAutoreleasePool 111} AutoreleasePoolOperation; 112 113static pthread_mutex_t WebThreadReleaseLock = PTHREAD_MUTEX_INITIALIZER; 114static CFRunLoopSourceRef WebThreadReleaseSource; 115static CFMutableArrayRef WebThreadReleaseObjArray; 116 117static void MainThreadAdoptAndRelease(id obj); 118 119static pthread_mutex_t delegateLock = PTHREAD_MUTEX_INITIALIZER; 120static pthread_cond_t delegateCondition = PTHREAD_COND_INITIALIZER; 121static NSInvocation *delegateInvocation; 122static CFRunLoopSourceRef delegateSource = NULL; 123static BOOL delegateHandled; 124#if LOG_MAIN_THREAD_LOCKING 125static BOOL sendingDelegateMessage; 126#endif 127 128static CFRunLoopObserverRef mainRunLoopAutoUnlockObserver; 129 130static pthread_mutex_t startupLock = PTHREAD_MUTEX_INITIALIZER; 131static pthread_cond_t startupCondition = PTHREAD_COND_INITIALIZER; 132 133static WebThreadContext *webThreadContext; 134static pthread_key_t threadContextKey; 135static unsigned mainThreadLockCount; 136static unsigned otherThreadLockCount; 137static unsigned sMainThreadModalCount; 138 139volatile bool webThreadShouldYield; 140 141static pthread_mutex_t WebCoreReleaseLock; 142static void WebCoreObjCDeallocOnWebThreadImpl(id self, SEL _cmd); 143static void WebCoreObjCDeallocWithWebThreadLockImpl(id self, SEL _cmd); 144 145static NSMutableArray *sAsyncDelegates = nil; 146 147static CFStringRef delegateSourceRunLoopMode; 148 149static inline void SendMessage(NSInvocation *invocation) 150{ 151 [invocation invoke]; 152 MainThreadAdoptAndRelease(invocation); 153} 154 155static void HandleDelegateSource(void *info) 156{ 157 UNUSED_PARAM(info); 158 ASSERT(!WebThreadIsCurrent()); 159 160#if LOG_MAIN_THREAD_LOCKING 161 sendingDelegateMessage = YES; 162#endif 163 164 _WebThreadAutoLock(); 165 166 int result = pthread_mutex_lock(&delegateLock); 167 ASSERT_WITH_MESSAGE(result == 0, "delegate lock failed with code:%d", result); 168 169#if LOG_MESSAGES 170 if ([[delegateInvocation target] isKindOfClass:[NSNotificationCenter class]]) { 171 id argument0; 172 [delegateInvocation getArgument:&argument0 atIndex:0]; 173 NSLog(@"notification receive: %@", argument0); 174 } else { 175 NSLog(@"delegate receive: %@", NSStringFromSelector([delegateInvocation selector])); 176 } 177#endif 178 179 SendMessage(delegateInvocation); 180 181 delegateHandled = YES; 182 pthread_cond_signal(&delegateCondition); 183 184 result = pthread_mutex_unlock(&delegateLock); 185 ASSERT_WITH_MESSAGE(result == 0, "delegate unlock failed with code:%d", result); 186 187#if LOG_MAIN_THREAD_LOCKING 188 sendingDelegateMessage = NO; 189#endif 190} 191 192static void SendDelegateMessage(NSInvocation *invocation) 193{ 194 if (WebThreadIsCurrent()) { 195 ASSERT(delegateSource); 196 int result = pthread_mutex_lock(&delegateLock); 197 ASSERT_WITH_MESSAGE(result == 0, "delegate lock failed with code:%d", result); 198 199 delegateInvocation = invocation; 200 delegateHandled = NO; 201 202#if LOG_MESSAGES 203 if ([[delegateInvocation target] isKindOfClass:[NSNotificationCenter class]]) { 204 id argument0; 205 [delegateInvocation getArgument:&argument0 atIndex:0]; 206 NSLog(@"notification send: %@", argument0); 207 } else { 208 NSLog(@"delegate send: %@", NSStringFromSelector([delegateInvocation selector])); 209 } 210#endif 211 212 { 213 // Code block created to scope JSC::JSLock::DropAllLocks outside of WebThreadLock() 214 JSC::JSLock::DropAllLocks dropAllLocks(WebCore::JSDOMWindowBase::commonVM()); 215 _WebThreadUnlock(); 216 217 CFRunLoopSourceSignal(delegateSource); 218 CFRunLoopWakeUp(CFRunLoopGetMain()); 219 220 while (!delegateHandled) { 221 if (WebTimedConditionLock(&delegateCondition, &delegateLock, DelegateWaitInterval) != 0) { 222 id delegateInformation; 223 if ([[delegateInvocation target] isKindOfClass:[NSNotificationCenter class]]) 224 [delegateInvocation getArgument:&delegateInformation atIndex:0]; 225 else 226 delegateInformation = NSStringFromSelector([delegateInvocation selector]); 227 228 CFStringRef mode = CFRunLoopCopyCurrentMode(CFRunLoopGetMain()); 229 NSLog(@"%s: delegate (%@) failed to return after waiting %d seconds. main run loop mode: %@", __PRETTY_FUNCTION__, delegateInformation, DelegateWaitInterval, mode); 230 if (mode) 231 CFRelease(mode); 232 } 233 } 234 result = pthread_mutex_unlock(&delegateLock); 235 236 ASSERT_WITH_MESSAGE(result == 0, "delegate unlock failed with code:%d", result); 237 _WebTryThreadLock(false); 238 } 239 } else { 240 SendMessage(invocation); 241 } 242} 243 244void WebThreadRunOnMainThread(void(^delegateBlock)()) 245{ 246 if (!WebThreadIsCurrent()) { 247 ASSERT(pthread_main_np()); 248 delegateBlock(); 249 return; 250 } 251 252 JSC::JSLock::DropAllLocks dropAllLocks(WebCore::JSDOMWindowBase::commonVM()); 253 _WebThreadUnlock(); 254 255 void (^delegateBlockCopy)() = Block_copy(delegateBlock); 256 dispatch_sync(dispatch_get_main_queue(), delegateBlockCopy); 257 Block_release(delegateBlockCopy); 258 259 _WebTryThreadLock(false); 260} 261 262static void MainThreadAdoptAndRelease(id obj) 263{ 264 if (!WebThreadIsEnabled() || CFRunLoopGetMain() == CFRunLoopGetCurrent()) { 265 [obj release]; 266 return; 267 } 268#if LOG_RELEASES 269 NSLog(@"Release send [web thread] : %@", obj); 270#endif 271 // We own obj at this point, so we don't need the block to implicitly 272 // retain it. 273 __block id objNotRetained = obj; 274 dispatch_async(dispatch_get_main_queue(), ^{ 275 [objNotRetained release]; 276 }); 277} 278 279void WebThreadAdoptAndRelease(id obj) 280{ 281 ASSERT(!WebThreadIsCurrent()); 282 ASSERT(WebThreadReleaseSource); 283 284#if LOG_RELEASES 285 NSLog(@"Release send [main thread]: %@", obj); 286#endif 287 288 int result = pthread_mutex_lock(&WebThreadReleaseLock); 289 ASSERT_WITH_MESSAGE(result == 0, "Release lock failed with code:%d", result); 290 291 if (WebThreadReleaseObjArray == nil) 292 WebThreadReleaseObjArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, NULL); 293 CFArrayAppendValue(WebThreadReleaseObjArray, obj); 294 CFRunLoopSourceSignal(WebThreadReleaseSource); 295 CFRunLoopWakeUp(webThreadRunLoop); 296 297 result = pthread_mutex_unlock(&WebThreadReleaseLock); 298 ASSERT_WITH_MESSAGE(result == 0, "Release unlock failed with code:%d", result); 299} 300 301static inline void lockWebCoreReleaseLock() 302{ 303 int lockcode = pthread_mutex_lock(&WebCoreReleaseLock); 304#pragma unused (lockcode) 305 ASSERT_WITH_MESSAGE(lockcode == 0, "WebCoreReleaseLock lock failed with code:%d", lockcode); 306} 307 308static inline void unlockWebCoreReleaseLock() 309{ 310 int lockcode = pthread_mutex_unlock(&WebCoreReleaseLock); 311#pragma unused (lockcode) 312 ASSERT_WITH_MESSAGE(lockcode == 0, "WebCoreReleaseLock unlock failed with code:%d", lockcode); 313} 314 315void WebCoreObjCDeallocOnWebThread(Class cls) 316{ 317 SEL releaseSEL = @selector(release); 318 SEL webThreadReleaseSEL = @selector(_webcore_releaseOnWebThread); 319 320 // get the existing release method 321 Method releaseMethod = class_getInstanceMethod(cls, releaseSEL); 322 if (!releaseMethod) { 323 ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocOnWebThread() failed to find %s for %@", releaseSEL, NSStringFromClass(cls)); 324 return; 325 } 326 327 // add the implementation that ensures release WebThread release/deallocation 328 if (!class_addMethod(cls, webThreadReleaseSEL, (IMP)WebCoreObjCDeallocOnWebThreadImpl, method_getTypeEncoding(releaseMethod))) { 329 ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocOnWebThread() failed to add %s for %@", webThreadReleaseSEL, NSStringFromClass(cls)); 330 return; 331 } 332 333 // ensure the implementation exists at cls in the class hierarchy 334 if (class_addMethod(cls, releaseSEL, class_getMethodImplementation(cls, releaseSEL), method_getTypeEncoding(releaseMethod))) 335 releaseMethod = class_getInstanceMethod(cls, releaseSEL); 336 337 // swizzle the old release for the new implementation 338 method_exchangeImplementations(releaseMethod, class_getInstanceMethod(cls, webThreadReleaseSEL)); 339} 340 341void WebCoreObjCDeallocWithWebThreadLock(Class cls) 342{ 343 SEL releaseSEL = @selector(release); 344 SEL webThreadLockReleaseSEL = @selector(_webcore_releaseWithWebThreadLock); 345 346 // get the existing release method 347 Method releaseMethod = class_getInstanceMethod(cls, releaseSEL); 348 if (!releaseMethod) { 349 ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocWithWebThreadLock() failed to find %s for %@", releaseSEL, NSStringFromClass(cls)); 350 return; 351 } 352 353 // add the implementation that ensures release WebThreadLock release/deallocation 354 if (!class_addMethod(cls, webThreadLockReleaseSEL, (IMP)WebCoreObjCDeallocWithWebThreadLockImpl, method_getTypeEncoding(releaseMethod))) { 355 ASSERT_WITH_MESSAGE(releaseMethod, "WebCoreObjCDeallocWithWebThreadLock() failed to add %s for %@", webThreadLockReleaseSEL, NSStringFromClass(cls)); 356 return; 357 } 358 359 // ensure the implementation exists at cls in the class hierarchy 360 if (class_addMethod(cls, releaseSEL, class_getMethodImplementation(cls, releaseSEL), method_getTypeEncoding(releaseMethod))) 361 releaseMethod = class_getInstanceMethod(cls, releaseSEL); 362 363 // swizzle the old release for the new implementation 364 method_exchangeImplementations(releaseMethod, class_getInstanceMethod(cls, webThreadLockReleaseSEL)); 365} 366 367void WebCoreObjCDeallocOnWebThreadImpl(id self, SEL _cmd) 368{ 369 UNUSED_PARAM(_cmd); 370 if (!WebThreadIsEnabled()) 371 [self _webcore_releaseOnWebThread]; 372 else { 373 lockWebCoreReleaseLock(); 374 if ([self retainCount] == 1) { 375 // This is the only reference retaining the object, so we can 376 // safely release the WebCoreReleaseLock now. 377 unlockWebCoreReleaseLock(); 378 if (WebThreadIsCurrent()) 379 [self _webcore_releaseOnWebThread]; 380 else 381 WebThreadAdoptAndRelease(self); 382 } else { 383 // This is not the only reference retaining the object, so another 384 // thread could also call release - hold the lock whilst calling 385 // release to avoid a race condition. 386 [self _webcore_releaseOnWebThread]; 387 unlockWebCoreReleaseLock(); 388 } 389 } 390} 391 392void WebCoreObjCDeallocWithWebThreadLockImpl(id self, SEL _cmd) 393{ 394 UNUSED_PARAM(_cmd); 395 lockWebCoreReleaseLock(); 396 if (WebThreadIsLockedOrDisabled() || 1 != [self retainCount]) 397 [self _webcore_releaseWithWebThreadLock]; 398 else 399 WebThreadAdoptAndRelease(self); 400 unlockWebCoreReleaseLock(); 401} 402 403static void HandleWebThreadReleaseSource(void *info) 404{ 405 UNUSED_PARAM(info); 406 ASSERT(WebThreadIsCurrent()); 407 408 int result = pthread_mutex_lock(&WebThreadReleaseLock); 409 ASSERT_WITH_MESSAGE(result == 0, "Release lock failed with code:%d", result); 410 411 CFMutableArrayRef objects = NULL; 412 if (CFArrayGetCount(WebThreadReleaseObjArray)) { 413 objects = CFArrayCreateMutableCopy(NULL, 0, WebThreadReleaseObjArray); 414 CFArrayRemoveAllValues(WebThreadReleaseObjArray); 415 } 416 417 result = pthread_mutex_unlock(&WebThreadReleaseLock); 418 ASSERT_WITH_MESSAGE(result == 0, "Release unlock failed with code:%d", result); 419 420 if (!objects) 421 return; 422 423 unsigned count = CFArrayGetCount(objects); 424 unsigned i; 425 for (i = 0; i < count; i++) { 426 id obj = (id)CFArrayGetValueAtIndex(objects, i); 427#if LOG_RELEASES 428 NSLog(@"Release recv [web thread] : %@", obj); 429#endif 430 [obj release]; 431 } 432 433 CFRelease(objects); 434} 435 436void WebThreadCallDelegate(NSInvocation *invocation) 437{ 438 // NSInvocation released in SendMessage() 439 SendDelegateMessage([invocation retain]); 440} 441 442void WebThreadPostNotification(NSString *name, id object, id userInfo) 443{ 444 if (pthread_main_np()) 445 [[NSNotificationCenter defaultCenter] postNotificationName:name object:object userInfo:userInfo]; 446 else { 447 dispatch_async(dispatch_get_main_queue(), ^ { 448 [[NSNotificationCenter defaultCenter] postNotificationName:name object:object userInfo:userInfo]; 449 }); 450 } 451} 452 453void WebThreadCallDelegateAsync(NSInvocation *invocation) 454{ 455 ASSERT(invocation); 456 if (WebThreadIsCurrent()) 457 [sAsyncDelegates addObject:invocation]; 458 else 459 WebThreadCallDelegate(invocation); 460} 461 462// Note: despite the name, returns an autoreleased object. 463NSInvocation *WebThreadMakeNSInvocation(id target, SEL selector) 464{ 465 NSMethodSignature *signature = [target methodSignatureForSelector:selector]; 466 ASSERT(signature); 467 if (signature) { 468 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 469 [invocation setSelector:selector]; 470 [invocation setTarget:target]; 471 [invocation retainArguments]; 472 return invocation; 473 } 474 return nil; 475} 476 477static void MainRunLoopAutoUnlock(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context) 478{ 479 UNUSED_PARAM(observer); 480 UNUSED_PARAM(activity); 481 UNUSED_PARAM(context); 482 ASSERT(!WebThreadIsCurrent()); 483 484 if (sMainThreadModalCount != 0) 485 return; 486 487 CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes); 488 489 _WebThreadUnlock(); 490} 491 492static void _WebThreadAutoLock(void) 493{ 494 ASSERT(!WebThreadIsCurrent()); 495 496 if (mainThreadLockCount == 0) { 497 CFRunLoopAddObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes); 498 _WebTryThreadLock(false); 499 CFRunLoopWakeUp(CFRunLoopGetMain()); 500 } 501} 502 503static void WebRunLoopLockInternal(AutoreleasePoolOperation poolOperation) 504{ 505 _WebTryThreadLock(false); 506 if (poolOperation == PushOrPopAutoreleasePool) 507 autoreleasePoolMark = NSPushAutoreleasePool(0); 508 isWebThreadLocked = YES; 509} 510 511static void WebRunLoopUnlockInternal(AutoreleasePoolOperation poolOperation) 512{ 513 ASSERT(sAsyncDelegates); 514 if ([sAsyncDelegates count]) { 515 for (NSInvocation* invocation in sAsyncDelegates) 516 SendDelegateMessage([invocation retain]); 517 [sAsyncDelegates removeAllObjects]; 518 } 519 520 if (poolOperation == PushOrPopAutoreleasePool) 521 NSPopAutoreleasePool(autoreleasePoolMark); 522 523 _WebThreadUnlock(); 524 isWebThreadLocked = NO; 525} 526 527static void WebRunLoopLock(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context) 528{ 529 UNUSED_PARAM(observer); 530 UNUSED_PARAM(context); 531 ASSERT(WebThreadIsCurrent()); 532 ASSERT_UNUSED(activity, activity == kCFRunLoopAfterWaiting || activity == kCFRunLoopBeforeTimers || activity == kCFRunLoopBeforeSources); 533 534 // If the WebThread is locked by the main thread then we want to 535 // grab the lock ourselves when the main thread releases the lock. 536 if (isWebThreadLocked && !mainThreadLockCount) 537 return; 538 WebRunLoopLockInternal(PushOrPopAutoreleasePool); 539} 540 541static void WebRunLoopUnlock(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context) 542{ 543 UNUSED_PARAM(observer); 544 UNUSED_PARAM(context); 545 ASSERT(WebThreadIsCurrent()); 546 ASSERT_UNUSED(activity, activity == kCFRunLoopBeforeWaiting || activity == kCFRunLoopExit); 547 ASSERT(!mainThreadLockCount); 548 549 if (!isWebThreadLocked) 550 return; 551 WebRunLoopUnlockInternal(PushOrPopAutoreleasePool); 552} 553 554static void MainRunLoopUnlockGuard(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *context) 555{ 556 UNUSED_PARAM(observer); 557 UNUSED_PARAM(activity); 558 UNUSED_PARAM(context); 559 ASSERT(!WebThreadIsCurrent()); 560 561 // We shouldn't have the web lock at this point. However, MobileMail sometimes 562 // get to a state where the main thread has web lock but it didn't release it on last 563 // runloop exit, and web thread gets stuck at waiting for the lock. If this happens, 564 // we need to help release the lock. See <rdar://problem/8005192>. 565 if (mainThreadLockCount != 0 && sMainThreadModalCount == 0) { 566 NSLog(@"WARNING: Main thread didn't release the lock at last runloop exit!"); 567 568 MainRunLoopAutoUnlock(observer, activity, context); 569 if (mainThreadLockCount != 0) 570 mainThreadLockCount = 0; 571 } 572} 573 574static void _WebRunLoopEnableNestedFromMainThread() 575{ 576 CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes); 577} 578 579static void _WebRunLoopDisableNestedFromMainThread() 580{ 581 CFRunLoopAddObserver(CFRunLoopGetCurrent(), mainRunLoopAutoUnlockObserver, kCFRunLoopCommonModes); 582} 583 584void WebRunLoopEnableNested() 585{ 586 if (!WebThreadIsEnabled()) 587 return; 588 589 ASSERT(!isNestedWebThreadRunLoop); 590 591 if (!WebThreadIsCurrent()) 592 _WebRunLoopEnableNestedFromMainThread(); 593 594 savedAutoreleasePoolMark = autoreleasePoolMark; 595 autoreleasePoolMark = 0; 596 WebRunLoopUnlockInternal(IgnoreAutoreleasePool); 597 isNestedWebThreadRunLoop = YES; 598} 599 600void WebRunLoopDisableNested() 601{ 602 if (!WebThreadIsEnabled()) 603 return; 604 605 ASSERT(isNestedWebThreadRunLoop); 606 607 if (!WebThreadIsCurrent()) 608 _WebRunLoopDisableNestedFromMainThread(); 609 610 autoreleasePoolMark = savedAutoreleasePoolMark; 611 savedAutoreleasePoolMark = 0; 612 WebRunLoopLockInternal(IgnoreAutoreleasePool); 613 isNestedWebThreadRunLoop = NO; 614} 615 616static void FreeThreadContext(void *threadContext) 617{ 618 if (threadContext != NULL) 619 free(threadContext); 620} 621 622static void InitThreadContextKey() 623{ 624 pthread_key_create(&threadContextKey, FreeThreadContext); 625} 626 627static WebThreadContext *CurrentThreadContext(void) 628{ 629 static pthread_once_t initControl = PTHREAD_ONCE_INIT; 630 pthread_once(&initControl, InitThreadContextKey); 631 632 WebThreadContext *threadContext = (WebThreadContext*)pthread_getspecific(threadContextKey); 633 if (threadContext == NULL) { 634 threadContext = (WebThreadContext *)calloc(sizeof(WebThreadContext), 1); 635 pthread_setspecific(threadContextKey, threadContext); 636 } 637 return threadContext; 638} 639 640static 641#ifndef __llvm__ 642NO_RETURN 643#endif 644void *RunWebThread(void *arg) 645{ 646 FloatingPointEnvironment::shared().propagateMainThreadEnvironment(); 647 648 UNUSED_PARAM(arg); 649 // WTF::initializeMainThread() needs to be called before JSC::initializeThreading() since the 650 // code invoked by the latter needs to know if it's running on the WebThread. See 651 // <rdar://problem/8502487>. 652 WTF::initializeMainThread(); 653 WTF::initializeWebThread(); 654 JSC::initializeThreading(); 655 656 // Make sure that the WebThread and the main thread share the same ThreadGlobalData objects. 657 WebCore::threadGlobalData().setWebCoreThreadData(); 658 initializeWebThreadIdentifier(); 659 660#if HAVE(PTHREAD_SETNAME_NP) 661 pthread_setname_np("WebThread"); 662#endif 663 664 webThreadContext = CurrentThreadContext(); 665 webThreadRunLoop = CFRunLoopGetCurrent(); 666 webThreadNSRunLoop = [[NSRunLoop currentRunLoop] retain]; 667 668 CFRunLoopObserverRef webRunLoopLockObserverRef = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeTimers|kCFRunLoopBeforeSources|kCFRunLoopAfterWaiting, YES, 0, WebRunLoopLock, NULL); 669 CFRunLoopAddObserver(webThreadRunLoop, webRunLoopLockObserverRef, kCFRunLoopCommonModes); 670 CFRelease(webRunLoopLockObserverRef); 671 672 WebThreadInitRunQueue(); 673 674 // 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. 675 CFRunLoopObserverRef webRunLoopUnlockObserverRef = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting|kCFRunLoopExit, YES, 2500000, WebRunLoopUnlock, NULL); 676 CFRunLoopAddObserver(webThreadRunLoop, webRunLoopUnlockObserverRef, kCFRunLoopCommonModes); 677 CFRelease(webRunLoopUnlockObserverRef); 678 679 CFRunLoopSourceContext ReleaseSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleWebThreadReleaseSource}; 680 WebThreadReleaseSource = CFRunLoopSourceCreate(NULL, -1, &ReleaseSourceContext); 681 CFRunLoopAddSource(webThreadRunLoop, WebThreadReleaseSource, kCFRunLoopDefaultMode); 682 683 int result = pthread_mutex_lock(&startupLock); 684 ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result); 685 686 result = pthread_cond_signal(&startupCondition); 687 ASSERT_WITH_MESSAGE(result == 0, "startup signal failed with code:%d", result); 688 689 result = pthread_mutex_unlock(&startupLock); 690 ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result); 691 692 while (1) 693 CFRunLoopRunInMode(kCFRunLoopDefaultMode, DistantFuture, true); 694 695#ifdef __llvm__ 696 return NULL; 697#endif 698} 699 700static void StartWebThread() 701{ 702 webThreadStarted = TRUE; 703 704 // Initialize ThreadGlobalData on the main UI thread so that the WebCore thread 705 // can later set it's thread-specific data to point to the same objects. 706 WebCore::ThreadGlobalData& unused = WebCore::threadGlobalData(); 707 (void)unused; 708 709 // Initialize AtomicString on the main thread. 710 WTF::AtomicString::init(); 711 712 // register class for WebThread deallocation 713 WebCoreObjCDeallocOnWebThread([DOMObject class]); 714 WebCoreObjCDeallocOnWebThread([WAKWindow class]); 715 WebCoreObjCDeallocWithWebThreadLock([WAKView class]); 716 717 pthread_mutexattr_t mattr; 718 pthread_mutexattr_init(&mattr); 719 pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); 720 pthread_mutex_init(&webLock, &mattr); 721 pthread_mutexattr_destroy(&mattr); 722 723 pthread_mutexattr_t mutex_attr; 724 pthread_mutexattr_init(&mutex_attr); 725 pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); 726 pthread_mutex_init(&WebCoreReleaseLock, &mutex_attr); 727 pthread_mutexattr_destroy(&mutex_attr); 728 729 CFRunLoopRef runLoop = CFRunLoopGetCurrent(); 730 CFRunLoopSourceContext delegateSourceContext = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, HandleDelegateSource}; 731 delegateSource = CFRunLoopSourceCreate(NULL, 0, &delegateSourceContext); 732 // We shouldn't get delegate callbacks while scrolling, but there might be 733 // one outstanding when we start. Add the source for all common run loop 734 // modes so we don't block the web thread while scrolling. 735 if (!delegateSourceRunLoopMode) 736 delegateSourceRunLoopMode = kCFRunLoopCommonModes; 737 CFRunLoopAddSource(runLoop, delegateSource, delegateSourceRunLoopMode); 738 739 sAsyncDelegates = [[NSMutableArray alloc] init]; 740 741 mainRunLoopAutoUnlockObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopBeforeWaiting | kCFRunLoopExit, YES, 3000001, MainRunLoopAutoUnlock, NULL); 742 743 pthread_attr_t tattr; 744 pthread_attr_init(&tattr); 745 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM); 746 // The web thread is a secondary thread, and secondary threads are usually given 747 // a 512 kb stack, but we need more space in order to have room for the JavaScriptCore 748 // reentrancy limit. This limit works on both the simulator and the device. 749 pthread_attr_setstacksize(&tattr, 200 * 4096); 750 751 struct sched_param param; 752 pthread_attr_getschedparam(&tattr, ¶m); 753 param.sched_priority--; 754 pthread_attr_setschedparam(&tattr, ¶m); 755 756 // Wait for the web thread to startup completely before we continue. 757 int result = pthread_mutex_lock(&startupLock); 758 ASSERT_WITH_MESSAGE(result == 0, "startup lock failed with code:%d", result); 759 760 // Propagate the mainThread's fenv to workers & the web thread. 761 FloatingPointEnvironment::shared().saveMainThreadEnvironment(); 762 763 pthread_create(&webThread, &tattr, RunWebThread, NULL); 764 pthread_attr_destroy(&tattr); 765 766 result = pthread_cond_wait(&startupCondition, &startupLock); 767 ASSERT_WITH_MESSAGE(result == 0, "startup wait failed with code:%d", result); 768 769 result = pthread_mutex_unlock(&startupLock); 770 ASSERT_WITH_MESSAGE(result == 0, "startup unlock failed with code:%d", result); 771 772 initializeApplicationUIThreadIdentifier(); 773} 774 775static int WebTimedConditionLock (pthread_cond_t *condition, pthread_mutex_t *lock, CFAbsoluteTime interval) 776{ 777 struct timespec time; 778 CFAbsoluteTime at = CFAbsoluteTimeGetCurrent() + interval; 779 time.tv_sec = (time_t)(floor(at) + kCFAbsoluteTimeIntervalSince1970); 780 time.tv_nsec = (int32_t)((at - floor(at)) * 1000000000.0); 781 return pthread_cond_timedwait(condition, lock, &time); 782} 783 784 785#if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING 786static unsigned lockCount; 787#endif 788 789static bool _WebTryThreadLock(bool shouldTry) 790{ 791 // Suspend the web thread if the main thread is trying to lock. 792 bool onMainThread = pthread_main_np(); 793 if (onMainThread) 794 webThreadShouldYield = true; 795 else if (!WebThreadIsCurrent()) { 796 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()); 797 CRASH(); 798 } 799 800 801 int result; 802 bool busy = false; 803 if (shouldTry) { 804 result = pthread_mutex_trylock(&webLock); 805 if (result == EBUSY) { 806 busy = true; 807 } else 808 ASSERT_WITH_MESSAGE(result == 0, "try web lock failed with code:%d", result); 809 } 810 else { 811 result = pthread_mutex_lock(&webLock); 812 ASSERT_WITH_MESSAGE(result == 0, "web lock failed with code:%d", result); 813 } 814 815 if (!busy) { 816#if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING 817 lockCount++; 818#if LOG_WEB_LOCK 819 NSLog(@"lock %d, web-thread: %d", lockCount, WebThreadIsCurrent()); 820#endif 821#endif 822 if (onMainThread) { 823 ASSERT(CFRunLoopGetCurrent() == CFRunLoopGetMain()); 824 webThreadShouldYield = false; 825 mainThreadLockCount++; 826#if LOG_MAIN_THREAD_LOCKING 827 if (!sendingDelegateMessage && lockCount == 1) 828 NSLog(@"Main thread locking outside of delegate messages."); 829#endif 830 } else { 831 webThreadLockCount++; 832 if (webThreadLockCount > 1) { 833 NSLog(@"%s, %p: Multiple locks on web thread not allowed! Please file a bug. Crashing now...", __PRETTY_FUNCTION__, CurrentThreadContext()); 834 CRASH(); 835 } 836 } 837 } 838 839 return !busy; 840} 841 842void WebThreadLock(void) 843{ 844 if (!webThreadStarted || pthread_equal(webThread, pthread_self())) 845 return; 846 _WebThreadAutoLock(); 847} 848 849void WebThreadUnlock(void) 850{ 851 // This is a no-op, we unlock automatically on top of the runloop 852 ASSERT(!WebThreadIsCurrent()); 853} 854 855void WebThreadLockFromAnyThread() 856{ 857 _WebThreadLockFromAnyThread(true); 858} 859 860void WebThreadLockFromAnyThreadNoLog() 861{ 862 _WebThreadLockFromAnyThread(false); 863} 864 865static void _WebThreadLockFromAnyThread(bool shouldLog) 866{ 867 if (!webThreadStarted) 868 return; 869 ASSERT(!WebThreadIsCurrent()); 870 if (pthread_main_np()) { 871 _WebThreadAutoLock(); 872 return; 873 } 874 if (shouldLog) 875 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 877 pthread_mutex_lock(&webLock); 878 879 // This used for any thread other than the web thread. 880 otherThreadLockCount++; 881 webThreadShouldYield = false; 882} 883 884void WebThreadUnlockFromAnyThread() 885{ 886 if (!webThreadStarted) 887 return; 888 ASSERT(!WebThreadIsCurrent()); 889 // No-op except from a secondary thread. 890 if (pthread_main_np()) 891 return; 892 893 ASSERT(otherThreadLockCount); 894 otherThreadLockCount--; 895 896 int result; 897 result = pthread_mutex_unlock(&webLock); 898 ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result); 899} 900 901void WebThreadUnlockGuardForMail() 902{ 903 ASSERT(!WebThreadIsCurrent()); 904 905 CFRunLoopObserverRef mainRunLoopUnlockGuardObserver = CFRunLoopObserverCreate(NULL, kCFRunLoopEntry, YES, 0, MainRunLoopUnlockGuard, NULL); 906 CFRunLoopAddObserver(CFRunLoopGetMain(), mainRunLoopUnlockGuardObserver, kCFRunLoopCommonModes); 907 CFRelease(mainRunLoopUnlockGuardObserver); 908} 909 910void _WebThreadUnlock(void) 911{ 912#if LOG_WEB_LOCK || LOG_MAIN_THREAD_LOCKING 913 lockCount--; 914#if LOG_WEB_LOCK 915 NSLog(@"unlock %d, web-thread: %d", lockCount, WebThreadIsCurrent()); 916#endif 917#endif 918 919 if (!WebThreadIsCurrent()) { 920 ASSERT(mainThreadLockCount != 0); 921 mainThreadLockCount--; 922 } else { 923 webThreadLockCount--; 924 webThreadShouldYield = false; 925 } 926 927 int result; 928 result = pthread_mutex_unlock(&webLock); 929 ASSERT_WITH_MESSAGE(result == 0, "web unlock failed with code:%d", result); 930} 931 932bool WebThreadIsLocked(void) 933{ 934 if (WebThreadIsCurrent()) 935 return webThreadLockCount; 936 else if (pthread_main_np()) 937 return mainThreadLockCount; 938 else 939 return otherThreadLockCount; 940} 941 942bool WebThreadIsLockedOrDisabled(void) 943{ 944 return !WebThreadIsEnabled() || WebThreadIsLocked(); 945} 946 947void WebThreadLockPushModal(void) 948{ 949 if (WebThreadIsCurrent()) 950 return; 951 952 ASSERT(WebThreadIsLocked()); 953 ++sMainThreadModalCount; 954} 955 956void WebThreadLockPopModal(void) 957{ 958 if (WebThreadIsCurrent()) 959 return; 960 961 ASSERT(WebThreadIsLocked()); 962 ASSERT(sMainThreadModalCount != 0); 963 --sMainThreadModalCount; 964} 965 966CFRunLoopRef WebThreadRunLoop(void) 967{ 968 if (webThreadStarted) { 969 ASSERT(webThreadRunLoop); 970 return webThreadRunLoop; 971 } 972 973 return CFRunLoopGetCurrent(); 974} 975 976NSRunLoop* WebThreadNSRunLoop(void) 977{ 978 if (webThreadStarted) { 979 ASSERT(webThreadNSRunLoop); 980 return webThreadNSRunLoop; 981 } 982 983 return [NSRunLoop currentRunLoop]; 984} 985 986WebThreadContext *WebThreadCurrentContext(void) 987{ 988 return CurrentThreadContext(); 989} 990 991bool WebThreadContextIsCurrent(void) 992{ 993 return WebThreadCurrentContext() == webThreadContext; 994} 995 996void WebThreadSetDelegateSourceRunLoopMode(CFStringRef mode) 997{ 998 ASSERT(!webThreadStarted); 999 delegateSourceRunLoopMode = mode; 1000} 1001 1002void WebThreadEnable(void) 1003{ 1004 RELEASE_ASSERT_WITH_MESSAGE(!WebCore::applicationIsWebProcess(), "The WebProcess should never run a Web Thread"); 1005 1006 static pthread_once_t initControl = PTHREAD_ONCE_INIT; 1007 pthread_once(&initControl, StartWebThread); 1008} 1009 1010bool WebThreadIsEnabled(void) 1011{ 1012 return webThreadStarted; 1013} 1014 1015bool WebThreadIsCurrent(void) 1016{ 1017 return webThreadStarted && pthread_equal(webThread, pthread_self()); 1018} 1019 1020bool WebThreadNotCurrent(void) 1021{ 1022 return webThreadStarted && !pthread_equal(webThread, pthread_self()); 1023} 1024 1025#endif // PLATFORM(IOS) 1026