From cad810f21b803229eb11403f9209855525a25d57 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Fri, 6 May 2011 11:45:16 +0100 Subject: Merge WebKit at r75315: Initial merge by git. Change-Id: I570314b346ce101c935ed22a626b48c2af266b84 --- Source/JavaScriptCore/runtime/JSLock.cpp | 260 +++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 Source/JavaScriptCore/runtime/JSLock.cpp (limited to 'Source/JavaScriptCore/runtime/JSLock.cpp') diff --git a/Source/JavaScriptCore/runtime/JSLock.cpp b/Source/JavaScriptCore/runtime/JSLock.cpp new file mode 100644 index 0000000..10f4f3f --- /dev/null +++ b/Source/JavaScriptCore/runtime/JSLock.cpp @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2005, 2008 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the NU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + * + */ + +#include "config.h" +#include "JSLock.h" + +#include "Collector.h" +#include "CallFrame.h" + +#if ENABLE(JSC_MULTIPLE_THREADS) +#include +#endif + +namespace JSC { + +#if ENABLE(JSC_MULTIPLE_THREADS) + +// Acquire this mutex before accessing lock-related data. +static pthread_mutex_t JSMutex = 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; + +static void createJSLockCount() +{ + pthread_key_create(&JSLockCount, 0); +} + +pthread_once_t createJSLockCountOnce = PTHREAD_ONCE_INIT; + +// Lock nesting count. +intptr_t JSLock::lockCount() +{ + pthread_once(&createJSLockCountOnce, createJSLockCount); + + return reinterpret_cast(pthread_getspecific(JSLockCount)); +} + +static void setLockCount(intptr_t count) +{ + ASSERT(count >= 0); + pthread_setspecific(JSLockCount, reinterpret_cast(count)); +} + +JSLock::JSLock(ExecState* exec) + : m_lockBehavior(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly) +{ + lock(m_lockBehavior); +} + +JSLock::JSLock(JSGlobalData* globalData) + : m_lockBehavior(globalData->isSharedInstance() ? LockForReal : SilenceAssertionsOnly) +{ + lock(m_lockBehavior); +} + +void JSLock::lock(JSLockBehavior lockBehavior) +{ +#ifdef NDEBUG + // Locking "not for real" is a debug-only feature. + if (lockBehavior == SilenceAssertionsOnly) + return; +#endif + + pthread_once(&createJSLockCountOnce, createJSLockCount); + + intptr_t currentLockCount = lockCount(); + if (!currentLockCount && lockBehavior == LockForReal) { + int result; + result = pthread_mutex_lock(&JSMutex); + ASSERT(!result); + } + setLockCount(currentLockCount + 1); +} + +void JSLock::unlock(JSLockBehavior lockBehavior) +{ + ASSERT(lockCount()); + +#ifdef NDEBUG + // Locking "not for real" is a debug-only feature. + if (lockBehavior == SilenceAssertionsOnly) + return; +#endif + + intptr_t newLockCount = lockCount() - 1; + setLockCount(newLockCount); + if (!newLockCount && lockBehavior == LockForReal) { + int result; + result = pthread_mutex_unlock(&JSMutex); + ASSERT(!result); + } +} + +void JSLock::lock(ExecState* exec) +{ + lock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly); +} + +void JSLock::unlock(ExecState* exec) +{ + unlock(exec->globalData().isSharedInstance() ? LockForReal : SilenceAssertionsOnly); +} + +bool JSLock::currentThreadIsHoldingLock() +{ + pthread_once(&createJSLockCountOnce, createJSLockCount); + return !!pthread_getspecific(JSLockCount); +} + +// This is fairly nasty. We allow multiple threads to run on the same +// context, and we do not require any locking semantics in doing so - +// clients of the API may simply use the context from multiple threads +// concurently, and assume this will work. In order to make this work, +// We lock the context when a thread enters, and unlock it when it leaves. +// However we do not only unlock when the thread returns from its +// entry point (evaluate script or call function), we also unlock the +// context if the thread leaves JSC by making a call out to an external +// function through a callback. +// +// All threads using the context share the same JS stack (the RegisterFile). +// Whenever a thread calls into JSC it starts using the RegisterFile from the +// previous 'high water mark' - the maximum point the stack has ever grown to +// (returned by RegisterFile::end()). So if a first thread calls out to a +// callback, and a second thread enters JSC, then also exits by calling out +// to a callback, we can be left with stackframes from both threads in the +// RegisterFile. As such, a problem may occur should the first thread's +// callback complete first, and attempt to return to JSC. Were we to allow +// 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 +// 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 +// first time a thread exits it will call DropAllLocks - which will do as expected +// and drop locks allowing another thread to enter. Should another thread, or the +// 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 +// callback, or by re-entering through another call to evaulate script or call +// function). +// +// This policy is slightly more restricive than it needs to be for correctness - +// we could validly allow futher entries into JSC from other threads, we only +// need ensure that callbacks return in the reverse chronological order of the +// 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) +{ + pthread_once(&createJSLockCountOnce, createJSLockCount); + + if (lockDropDepth++) { + m_lockCount = 0; + return; + } + + m_lockCount = JSLock::lockCount(); + for (intptr_t i = 0; i < m_lockCount; i++) + JSLock::unlock(m_lockBehavior); +} + +JSLock::DropAllLocks::DropAllLocks(JSLockBehavior JSLockBehavior) + : m_lockBehavior(JSLockBehavior) +{ + pthread_once(&createJSLockCountOnce, createJSLockCount); + + if (lockDropDepth++) { + m_lockCount = 0; + return; + } + + // It is necessary to drop even "unreal" locks, because having a non-zero lock count + // will prevent a real lock from being taken. + + m_lockCount = JSLock::lockCount(); + for (intptr_t i = 0; i < m_lockCount; i++) + JSLock::unlock(m_lockBehavior); +} + +JSLock::DropAllLocks::~DropAllLocks() +{ + for (intptr_t i = 0; i < m_lockCount; i++) + JSLock::lock(m_lockBehavior); + + --lockDropDepth; +} + +#else + +JSLock::JSLock(ExecState*) + : m_lockBehavior(SilenceAssertionsOnly) +{ +} + +// 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() +{ + return 1; +} + +bool JSLock::currentThreadIsHoldingLock() +{ + return true; +} + +void JSLock::lock(JSLockBehavior) +{ +} + +void JSLock::unlock(JSLockBehavior) +{ +} + +void JSLock::lock(ExecState*) +{ +} + +void JSLock::unlock(ExecState*) +{ +} + +JSLock::DropAllLocks::DropAllLocks(ExecState*) +{ +} + +JSLock::DropAllLocks::DropAllLocks(JSLockBehavior) +{ +} + +JSLock::DropAllLocks::~DropAllLocks() +{ +} + +#endif // USE(MULTIPLE_THREADS) + +} // namespace JSC -- cgit v1.1