1/* 2 * Copyright (C) 2005, 2008, 2012 Apple Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the NU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA 18 * 19 */ 20 21#include "config.h" 22#include "JSLock.h" 23 24#include "Heap.h" 25#include "CallFrame.h" 26#include "JSGlobalObject.h" 27#include "JSObject.h" 28#include "Operations.h" 29 30#if USE(PTHREADS) 31#include <pthread.h> 32#endif 33 34namespace JSC { 35 36Mutex* GlobalJSLock::s_sharedInstanceLock = 0; 37 38GlobalJSLock::GlobalJSLock() 39{ 40 s_sharedInstanceLock->lock(); 41} 42 43GlobalJSLock::~GlobalJSLock() 44{ 45 s_sharedInstanceLock->unlock(); 46} 47 48void GlobalJSLock::initialize() 49{ 50 s_sharedInstanceLock = new Mutex(); 51} 52 53JSLockHolder::JSLockHolder(ExecState* exec) 54 : m_vm(&exec->vm()) 55{ 56 init(); 57} 58 59JSLockHolder::JSLockHolder(VM* vm) 60 : m_vm(vm) 61{ 62 init(); 63} 64 65JSLockHolder::JSLockHolder(VM& vm) 66 : m_vm(&vm) 67{ 68 init(); 69} 70 71void JSLockHolder::init() 72{ 73 m_vm->apiLock().lock(); 74} 75 76JSLockHolder::~JSLockHolder() 77{ 78 RefPtr<JSLock> apiLock(&m_vm->apiLock()); 79 m_vm.clear(); 80 apiLock->unlock(); 81} 82 83JSLock::JSLock(VM* vm) 84 : m_ownerThread(0) 85 , m_lockCount(0) 86 , m_lockDropDepth(0) 87 , m_vm(vm) 88{ 89 m_spinLock.Init(); 90} 91 92JSLock::~JSLock() 93{ 94} 95 96void JSLock::willDestroyVM(VM* vm) 97{ 98 ASSERT_UNUSED(vm, m_vm == vm); 99 m_vm = 0; 100} 101 102void JSLock::lock() 103{ 104 ThreadIdentifier currentThread = WTF::currentThread(); 105 { 106 SpinLockHolder holder(&m_spinLock); 107 if (m_ownerThread == currentThread && m_lockCount) { 108 m_lockCount++; 109 return; 110 } 111 } 112 113 m_lock.lock(); 114 115 { 116 SpinLockHolder holder(&m_spinLock); 117 m_ownerThread = currentThread; 118 ASSERT(!m_lockCount); 119 m_lockCount = 1; 120 } 121} 122 123void JSLock::unlock() 124{ 125 ASSERT(currentThreadIsHoldingLock()); 126 127 SpinLockHolder holder(&m_spinLock); 128 m_lockCount--; 129 130 if (!m_lockCount) 131 m_lock.unlock(); 132} 133 134void JSLock::lock(ExecState* exec) 135{ 136 exec->vm().apiLock().lock(); 137} 138 139void JSLock::unlock(ExecState* exec) 140{ 141 exec->vm().apiLock().unlock(); 142} 143 144bool JSLock::currentThreadIsHoldingLock() 145{ 146 return m_lockCount && m_ownerThread == WTF::currentThread(); 147} 148 149// This is fairly nasty. We allow multiple threads to run on the same 150// context, and we do not require any locking semantics in doing so - 151// clients of the API may simply use the context from multiple threads 152// concurently, and assume this will work. In order to make this work, 153// We lock the context when a thread enters, and unlock it when it leaves. 154// However we do not only unlock when the thread returns from its 155// entry point (evaluate script or call function), we also unlock the 156// context if the thread leaves JSC by making a call out to an external 157// function through a callback. 158// 159// All threads using the context share the same JS stack (the JSStack). 160// Whenever a thread calls into JSC it starts using the JSStack from the 161// previous 'high water mark' - the maximum point the stack has ever grown to 162// (returned by JSStack::end()). So if a first thread calls out to a 163// callback, and a second thread enters JSC, then also exits by calling out 164// to a callback, we can be left with stackframes from both threads in the 165// JSStack. As such, a problem may occur should the first thread's 166// callback complete first, and attempt to return to JSC. Were we to allow 167// this to happen, and were its stack to grow further, then it may potentially 168// write over the second thread's call frames. 169// 170// To avoid JS stack corruption we enforce a policy of only ever allowing two 171// threads to use a JS context concurrently, and only allowing the second of 172// these threads to execute until it has completed and fully returned from its 173// outermost call into JSC. We enforce this policy using 'lockDropDepth'. The 174// first time a thread exits it will call DropAllLocks - which will do as expected 175// and drop locks allowing another thread to enter. Should another thread, or the 176// same thread again, enter JSC (through evaluate script or call function), and exit 177// again through a callback, then the locks will not be dropped when DropAllLocks 178// is called (since lockDropDepth is non-zero). Since this thread is still holding 179// the locks, only it will be able to re-enter JSC (either be returning from the 180// callback, or by re-entering through another call to evaulate script or call 181// function). 182// 183// This policy is slightly more restricive than it needs to be for correctness - 184// we could validly allow futher entries into JSC from other threads, we only 185// need ensure that callbacks return in the reverse chronological order of the 186// order in which they were made - though implementing the less restrictive policy 187// would likely increase complexity and overhead. 188// 189 190// This function returns the number of locks that were dropped. 191unsigned JSLock::dropAllLocks() 192{ 193 if (m_lockDropDepth++) 194 return 0; 195 196 return dropAllLocksUnconditionally(); 197} 198 199unsigned JSLock::dropAllLocksUnconditionally() 200{ 201 unsigned lockCount = m_lockCount; 202 for (unsigned i = 0; i < lockCount; i++) 203 unlock(); 204 205 return lockCount; 206} 207 208void JSLock::grabAllLocks(unsigned lockCount) 209{ 210 for (unsigned i = 0; i < lockCount; i++) 211 lock(); 212 213 m_lockDropDepth--; 214} 215 216JSLock::DropAllLocks::DropAllLocks(ExecState* exec) 217 : m_lockCount(0) 218 , m_vm(&exec->vm()) 219{ 220 m_lockCount = m_vm->apiLock().dropAllLocks(); 221} 222 223JSLock::DropAllLocks::DropAllLocks(VM* vm) 224 : m_lockCount(0) 225 , m_vm(vm) 226{ 227 m_lockCount = m_vm->apiLock().dropAllLocks(); 228} 229 230JSLock::DropAllLocks::~DropAllLocks() 231{ 232 m_vm->apiLock().grabAllLocks(m_lockCount); 233} 234 235} // namespace JSC 236