2  * Copyright (C) 2005, 2008 Apple Inc. All rights reserved. 
   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. 
   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. 
  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  
  24 #include "Collector.h" 
  25 #include "CallFrame.h" 
  27 #if ENABLE(JSC_MULTIPLE_THREADS) 
  33 #if ENABLE(JSC_MULTIPLE_THREADS) 
  35 // Acquire this mutex before accessing lock-related data. 
  36 static pthread_mutex_t JSMutex 
= PTHREAD_MUTEX_INITIALIZER
; 
  38 // Thread-specific key that tells whether a thread holds the JSMutex, and how many times it was taken recursively. 
  39 pthread_key_t JSLockCount
; 
  41 static void createJSLockCount() 
  43     pthread_key_create(&JSLockCount
, 0); 
  46 pthread_once_t createJSLockCountOnce 
= PTHREAD_ONCE_INIT
; 
  48 // Lock nesting count. 
  49 intptr_t JSLock::lockCount() 
  51     pthread_once(&createJSLockCountOnce
, createJSLockCount
); 
  53     return reinterpret_cast<intptr_t>(pthread_getspecific(JSLockCount
)); 
  56 static void setLockCount(intptr_t count
) 
  59     pthread_setspecific(JSLockCount
, reinterpret_cast<void*>(count
)); 
  62 JSLock::JSLock(ExecState
* exec
) 
  63     : m_lockBehavior(exec
->globalData().isSharedInstance 
? LockForReal 
: SilenceAssertionsOnly
) 
  68 void JSLock::lock(JSLockBehavior lockBehavior
) 
  71     // Locking "not for real" is a debug-only feature. 
  72     if (lockBehavior 
== SilenceAssertionsOnly
) 
  76     pthread_once(&createJSLockCountOnce
, createJSLockCount
); 
  78     intptr_t currentLockCount 
= lockCount(); 
  79     if (!currentLockCount 
&& lockBehavior 
== LockForReal
) { 
  81         result 
= pthread_mutex_lock(&JSMutex
); 
  84     setLockCount(currentLockCount 
+ 1); 
  87 void JSLock::unlock(JSLockBehavior lockBehavior
) 
  92     // Locking "not for real" is a debug-only feature. 
  93     if (lockBehavior 
== SilenceAssertionsOnly
) 
  97     intptr_t newLockCount 
= lockCount() - 1; 
  98     setLockCount(newLockCount
); 
  99     if (!newLockCount 
&& lockBehavior 
== LockForReal
) { 
 101         result 
= pthread_mutex_unlock(&JSMutex
); 
 106 void JSLock::lock(ExecState
* exec
) 
 108     lock(exec
->globalData().isSharedInstance 
? LockForReal 
: SilenceAssertionsOnly
); 
 111 void JSLock::unlock(ExecState
* exec
) 
 113     unlock(exec
->globalData().isSharedInstance 
? LockForReal 
: SilenceAssertionsOnly
); 
 116 bool JSLock::currentThreadIsHoldingLock() 
 118     pthread_once(&createJSLockCountOnce
, createJSLockCount
); 
 119     return !!pthread_getspecific(JSLockCount
); 
 122 // This is fairly nasty.  We allow multiple threads to run on the same 
 123 // context, and we do not require any locking semantics in doing so - 
 124 // clients of the API may simply use the context from multiple threads 
 125 // concurently, and assume this will work.  In order to make this work, 
 126 // We lock the context when a thread enters, and unlock it when it leaves. 
 127 // However we do not only unlock when the thread returns from its 
 128 // entry point (evaluate script or call function), we also unlock the 
 129 // context if the thread leaves JSC by making a call out to an external 
 130 // function through a callback. 
 132 // All threads using the context share the same JS stack (the RegisterFile). 
 133 // Whenever a thread calls into JSC it starts using the RegisterFile from the 
 134 // previous 'high water mark' - the maximum point the stack has ever grown to 
 135 // (returned by RegisterFile::end()).  So if a first thread calls out to a 
 136 // callback, and a second thread enters JSC, then also exits by calling out 
 137 // to a callback, we can be left with stackframes from both threads in the 
 138 // RegisterFile.  As such, a problem may occur should the first thread's 
 139 // callback complete first, and attempt to return to JSC.  Were we to allow 
 140 // this to happen, and were its stack to grow further, then it may potentially 
 141 // write over the second thread's call frames. 
 143 // In avoid JS stack corruption we enforce a policy of only ever allowing two 
 144 // threads to use a JS context concurrently, and only allowing the second of 
 145 // these threads to execute until it has completed and fully returned from its 
 146 // outermost call into JSC.  We enforce this policy using 'lockDropDepth'.  The 
 147 // first time a thread exits it will call DropAllLocks - which will do as expected 
 148 // and drop locks allowing another thread to enter.  Should another thread, or the 
 149 // same thread again, enter JSC (through evaluate script or call function), and exit 
 150 // again through a callback, then the locks will not be dropped when DropAllLocks 
 151 // is called (since lockDropDepth is non-zero).  Since this thread is still holding 
 152 // the locks, only it will re able to re-enter JSC (either be returning from the 
 153 // callback, or by re-entering through another call to evaulate script or call 
 156 // This policy is slightly more restricive than it needs to be for correctness - 
 157 // we could validly allow futher entries into JSC from other threads, we only 
 158 // need ensure that callbacks return in the reverse chronological order of the 
 159 // order in which they were made - though implementing the less restrictive policy 
 160 // would likely increase complexity and overhead. 
 162 static unsigned lockDropDepth 
= 0; 
 164 JSLock::DropAllLocks::DropAllLocks(ExecState
* exec
) 
 165     : m_lockBehavior(exec
->globalData().isSharedInstance 
? LockForReal 
: SilenceAssertionsOnly
) 
 167     pthread_once(&createJSLockCountOnce
, createJSLockCount
); 
 169     if (lockDropDepth
++) { 
 174     m_lockCount 
= JSLock::lockCount(); 
 175     for (intptr_t i 
= 0; i 
< m_lockCount
; i
++) 
 176         JSLock::unlock(m_lockBehavior
); 
 179 JSLock::DropAllLocks::DropAllLocks(JSLockBehavior JSLockBehavior
) 
 180     : m_lockBehavior(JSLockBehavior
) 
 182     pthread_once(&createJSLockCountOnce
, createJSLockCount
); 
 184     if (lockDropDepth
++) { 
 189     // It is necessary to drop even "unreal" locks, because having a non-zero lock count 
 190     // will prevent a real lock from being taken. 
 192     m_lockCount 
= JSLock::lockCount(); 
 193     for (intptr_t i 
= 0; i 
< m_lockCount
; i
++) 
 194         JSLock::unlock(m_lockBehavior
); 
 197 JSLock::DropAllLocks::~DropAllLocks() 
 199     for (intptr_t i 
= 0; i 
< m_lockCount
; i
++) 
 200         JSLock::lock(m_lockBehavior
); 
 207 JSLock::JSLock(ExecState
*) 
 208     : m_lockBehavior(SilenceAssertionsOnly
) 
 212 // If threading support is off, set the lock count to a constant value of 1 so ssertions 
 213 // that the lock is held don't fail 
 214 intptr_t JSLock::lockCount() 
 219 bool JSLock::currentThreadIsHoldingLock() 
 224 void JSLock::lock(JSLockBehavior
) 
 228 void JSLock::unlock(JSLockBehavior
) 
 232 void JSLock::lock(ExecState
*) 
 236 void JSLock::unlock(ExecState
*) 
 240 JSLock::DropAllLocks::DropAllLocks(ExecState
*) 
 244 JSLock::DropAllLocks::DropAllLocks(JSLockBehavior
) 
 248 JSLock::DropAllLocks::~DropAllLocks() 
 252 #endif // USE(MULTIPLE_THREADS)