X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/1df5f87f1309a8daa30dabdee855f48ae40d14ab..6fe7ccc865dc7d7541b93c5bcaf6368d2c98a174:/runtime/JSLock.cpp diff --git a/runtime/JSLock.cpp b/runtime/JSLock.cpp index fc543f0..087c81d 100644 --- a/runtime/JSLock.cpp +++ b/runtime/JSLock.cpp @@ -23,6 +23,7 @@ #include "Heap.h" #include "CallFrame.h" +#include "JSGlobalObject.h" #include "JSObject.h" #include "ScopeChain.h" @@ -35,99 +36,98 @@ namespace JSC { // JSLock is only needed to support an obsolete execution model where JavaScriptCore // automatically protected against concurrent access from multiple threads. // So it's safe to disable it on non-mac platforms where we don't have native pthreads. -#if ENABLE(JSC_MULTIPLE_THREADS) && (OS(DARWIN) || USE(PTHREADS)) +#if (OS(DARWIN) || USE(PTHREADS)) -// Acquire this mutex before accessing lock-related data. -static pthread_mutex_t JSMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t sharedInstanceLock = PTHREAD_MUTEX_INITIALIZER; -// Thread-specific key that tells whether a thread holds the JSMutex, and how many times it was taken recursively. -pthread_key_t JSLockCount; +GlobalJSLock::GlobalJSLock() +{ + pthread_mutex_lock(&sharedInstanceLock); +} -static void createJSLockCount() +GlobalJSLock::~GlobalJSLock() { - pthread_key_create(&JSLockCount, 0); + pthread_mutex_unlock(&sharedInstanceLock); } -pthread_once_t createJSLockCountOnce = PTHREAD_ONCE_INIT; +JSLockHolder::JSLockHolder(ExecState* exec) + : m_globalData(&exec->globalData()) +{ + m_globalData->apiLock().lock(); +} -// Lock nesting count. -intptr_t JSLock::lockCount() +JSLockHolder::JSLockHolder(JSGlobalData* globalData) + : m_globalData(globalData) { - pthread_once(&createJSLockCountOnce, createJSLockCount); + m_globalData->apiLock().lock(); +} - return reinterpret_cast(pthread_getspecific(JSLockCount)); +JSLockHolder::JSLockHolder(JSGlobalData& globalData) + : m_globalData(&globalData) +{ + m_globalData->apiLock().lock(); } -static void setLockCount(intptr_t count) +JSLockHolder::~JSLockHolder() { - ASSERT(count >= 0); - pthread_setspecific(JSLockCount, reinterpret_cast(count)); + m_globalData->apiLock().unlock(); } -JSLock::JSLock(ExecState* exec) - : m_lockBehavior(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly) +JSLock::JSLock() + : m_lockCount(0) { - lock(m_lockBehavior); + m_spinLock.Init(); } -JSLock::JSLock(JSGlobalData* globalData) - : m_lockBehavior(globalData->isSharedInstance() ? LockForReal : SilenceAssertionsOnly) +JSLock::~JSLock() { - lock(m_lockBehavior); } -void JSLock::lock(JSLockBehavior lockBehavior) +void JSLock::lock() { -#ifdef NDEBUG - // Locking "not for real" is a debug-only feature. - if (lockBehavior == SilenceAssertionsOnly) - return; -#endif + ThreadIdentifier currentThread = WTF::currentThread(); + { + SpinLockHolder holder(&m_spinLock); + if (m_ownerThread == currentThread && m_lockCount) { + m_lockCount++; + return; + } + } - pthread_once(&createJSLockCountOnce, createJSLockCount); + m_lock.lock(); - intptr_t currentLockCount = lockCount(); - if (!currentLockCount && lockBehavior == LockForReal) { - int result; - result = pthread_mutex_lock(&JSMutex); - ASSERT(!result); + { + SpinLockHolder holder(&m_spinLock); + m_ownerThread = currentThread; + ASSERT(!m_lockCount); + m_lockCount = 1; } - setLockCount(currentLockCount + 1); } -void JSLock::unlock(JSLockBehavior lockBehavior) +void JSLock::unlock() { - ASSERT(lockCount()); + ASSERT(currentThreadIsHoldingLock()); -#ifdef NDEBUG - // Locking "not for real" is a debug-only feature. - if (lockBehavior == SilenceAssertionsOnly) - return; -#endif + SpinLockHolder holder(&m_spinLock); + m_lockCount--; - intptr_t newLockCount = lockCount() - 1; - setLockCount(newLockCount); - if (!newLockCount && lockBehavior == LockForReal) { - int result; - result = pthread_mutex_unlock(&JSMutex); - ASSERT(!result); - } + if (!m_lockCount) + m_lock.unlock(); } void JSLock::lock(ExecState* exec) { - lock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly); + exec->globalData().apiLock().lock(); } void JSLock::unlock(ExecState* exec) { - unlock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly); + exec->globalData().apiLock().unlock(); } bool JSLock::currentThreadIsHoldingLock() { - pthread_once(&createJSLockCountOnce, createJSLockCount); - return !!pthread_getspecific(JSLockCount); + return m_lockCount && m_ownerThread == WTF::currentThread(); } // This is fairly nasty. We allow multiple threads to run on the same @@ -151,7 +151,7 @@ bool JSLock::currentThreadIsHoldingLock() // this to happen, and were its stack to grow further, then it may potentially // write over the second thread's call frames. // -// In avoid JS stack corruption we enforce a policy of only ever allowing two +// To avoid JS stack corruption we enforce a policy of only ever allowing two // threads to use a JS context concurrently, and only allowing the second of // these threads to execute until it has completed and fully returned from its // outermost call into JSC. We enforce this policy using 'lockDropDepth'. The @@ -160,7 +160,7 @@ bool JSLock::currentThreadIsHoldingLock() // same thread again, enter JSC (through evaluate script or call function), and exit // again through a callback, then the locks will not be dropped when DropAllLocks // is called (since lockDropDepth is non-zero). Since this thread is still holding -// the locks, only it will re able to re-enter JSC (either be returning from the +// the locks, only it will be able to re-enter JSC (either be returning from the // callback, or by re-entering through another call to evaulate script or call // function). // @@ -170,61 +170,138 @@ bool JSLock::currentThreadIsHoldingLock() // order in which they were made - though implementing the less restrictive policy // would likely increase complexity and overhead. // -static unsigned lockDropDepth = 0; -JSLock::DropAllLocks::DropAllLocks(ExecState* exec) - : m_lockBehavior(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly) +// This function returns the number of locks that were dropped. +unsigned JSLock::dropAllLocks() { - pthread_once(&createJSLockCountOnce, createJSLockCount); - - if (lockDropDepth++) { - m_lockCount = 0; - return; + unsigned lockCount; + { + // Check if this thread is currently holding the lock. + // FIXME: Maybe we want to require this, guard with an ASSERT? + SpinLockHolder holder(&m_spinLock); + lockCount = m_lockCount; + if (!lockCount || m_ownerThread != WTF::currentThread()) + return 0; } - m_lockCount = JSLock::lockCount(); - for (intptr_t i = 0; i < m_lockCount; i++) - JSLock::unlock(m_lockBehavior); + // Don't drop the locks if they've already been dropped once. + // (If the prior drop came from another thread, and it resumed first, + // it could trash our register file). + if (m_lockDropDepth) + return 0; + + // m_lockDropDepth is only incremented if any locks were dropped. + m_lockDropDepth++; + m_lockCount = 0; + m_lock.unlock(); + return lockCount; } -JSLock::DropAllLocks::DropAllLocks(JSLockBehavior JSLockBehavior) - : m_lockBehavior(JSLockBehavior) +unsigned JSLock::dropAllLocksUnconditionally() { - pthread_once(&createJSLockCountOnce, createJSLockCount); + unsigned lockCount; + { + // Check if this thread is currently holding the lock. + // FIXME: Maybe we want to require this, guard with an ASSERT? + SpinLockHolder holder(&m_spinLock); + lockCount = m_lockCount; + if (!lockCount || m_ownerThread != WTF::currentThread()) + return 0; + } - if (lockDropDepth++) { - m_lockCount = 0; + // m_lockDropDepth is only incremented if any locks were dropped. + m_lockDropDepth++; + m_lockCount = 0; + m_lock.unlock(); + return lockCount; +} + +void JSLock::grabAllLocks(unsigned lockCount) +{ + // If no locks were dropped, nothing to do! + if (!lockCount) return; + + ThreadIdentifier currentThread = WTF::currentThread(); + { + // Check if this thread is currently holding the lock. + // FIXME: Maybe we want to prohibit this, guard against with an ASSERT? + SpinLockHolder holder(&m_spinLock); + if (m_ownerThread == currentThread && m_lockCount) { + m_lockCount += lockCount; + m_lockDropDepth--; + return; + } + } + + m_lock.lock(); + + { + SpinLockHolder holder(&m_spinLock); + m_ownerThread = currentThread; + ASSERT(!m_lockCount); + m_lockCount = lockCount; + m_lockDropDepth--; } +} - // It is necessary to drop even "unreal" locks, because having a non-zero lock count - // will prevent a real lock from being taken. +JSLock::DropAllLocks::DropAllLocks(ExecState* exec, AlwaysDropLocksTag alwaysDropLocks) + : m_lockCount(0) + , m_globalData(&exec->globalData()) +{ + if (alwaysDropLocks) + m_lockCount = m_globalData->apiLock().dropAllLocksUnconditionally(); + else + m_lockCount = m_globalData->apiLock().dropAllLocks(); +} - m_lockCount = JSLock::lockCount(); - for (intptr_t i = 0; i < m_lockCount; i++) - JSLock::unlock(m_lockBehavior); +JSLock::DropAllLocks::DropAllLocks(JSGlobalData* globalData, AlwaysDropLocksTag alwaysDropLocks) + : m_lockCount(0) + , m_globalData(globalData) +{ + if (alwaysDropLocks) + m_lockCount = m_globalData->apiLock().dropAllLocksUnconditionally(); + else + m_lockCount = m_globalData->apiLock().dropAllLocks(); } JSLock::DropAllLocks::~DropAllLocks() { - for (intptr_t i = 0; i < m_lockCount; i++) - JSLock::lock(m_lockBehavior); + m_globalData->apiLock().grabAllLocks(m_lockCount); +} + +#else // (OS(DARWIN) || USE(PTHREADS)) + +GlobalJSLock::GlobalJSLock() +{ +} + +GlobalJSLock::~GlobalJSLock() +{ +} - --lockDropDepth; +JSLockHolder::JSLockHolder(JSGlobalData*) +{ } -#else // ENABLE(JSC_MULTIPLE_THREADS) && (OS(DARWIN) || USE(PTHREADS)) +JSLockHolder::JSLockHolder(JSGlobalData&) +{ +} -JSLock::JSLock(ExecState*) - : m_lockBehavior(SilenceAssertionsOnly) +JSLockHolder::JSLockHolder(ExecState*) { } -// If threading support is off, set the lock count to a constant value of 1 so ssertions -// that the lock is held don't fail -intptr_t JSLock::lockCount() +JSLockHolder::~JSLockHolder() +{ +} + +JSLock::JSLock() +{ +} + +JSLock::~JSLock() { - return 1; } bool JSLock::currentThreadIsHoldingLock() @@ -232,11 +309,11 @@ bool JSLock::currentThreadIsHoldingLock() return true; } -void JSLock::lock(JSLockBehavior) +void JSLock::lock() { } -void JSLock::unlock(JSLockBehavior) +void JSLock::unlock() { } @@ -248,11 +325,33 @@ void JSLock::unlock(ExecState*) { } +void JSLock::lock(JSGlobalData&) +{ +} + +void JSLock::unlock(JSGlobalData&) +{ +} + +unsigned JSLock::dropAllLocks() +{ + return 0; +} + +unsigned JSLock::dropAllLocksUnconditionally() +{ + return 0; +} + +void JSLock::grabAllLocks(unsigned) +{ +} + JSLock::DropAllLocks::DropAllLocks(ExecState*) { } -JSLock::DropAllLocks::DropAllLocks(JSLockBehavior) +JSLock::DropAllLocks::DropAllLocks(JSGlobalData*) { } @@ -260,6 +359,6 @@ JSLock::DropAllLocks::~DropAllLocks() { } -#endif // ENABLE(JSC_MULTIPLE_THREADS) && (OS(DARWIN) || USE(PTHREADS)) +#endif // (OS(DARWIN) || USE(PTHREADS)) } // namespace JSC