]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/JSLock.cpp
JavaScriptCore-1097.13.tar.gz
[apple/javascriptcore.git] / runtime / JSLock.cpp
CommitLineData
9dae56ea
A
1/*
2 * Copyright (C) 2005, 2008 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
14957cd0 24#include "Heap.h"
9dae56ea 25#include "CallFrame.h"
6fe7ccc8 26#include "JSGlobalObject.h"
14957cd0
A
27#include "JSObject.h"
28#include "ScopeChain.h"
9dae56ea 29
14957cd0 30#if USE(PTHREADS)
9dae56ea
A
31#include <pthread.h>
32#endif
33
34namespace JSC {
35
14957cd0
A
36// JSLock is only needed to support an obsolete execution model where JavaScriptCore
37// automatically protected against concurrent access from multiple threads.
38// So it's safe to disable it on non-mac platforms where we don't have native pthreads.
6fe7ccc8 39#if (OS(DARWIN) || USE(PTHREADS))
9dae56ea 40
6fe7ccc8 41static pthread_mutex_t sharedInstanceLock = PTHREAD_MUTEX_INITIALIZER;
9dae56ea 42
6fe7ccc8
A
43GlobalJSLock::GlobalJSLock()
44{
45 pthread_mutex_lock(&sharedInstanceLock);
46}
9dae56ea 47
6fe7ccc8 48GlobalJSLock::~GlobalJSLock()
9dae56ea 49{
6fe7ccc8 50 pthread_mutex_unlock(&sharedInstanceLock);
9dae56ea
A
51}
52
6fe7ccc8
A
53JSLockHolder::JSLockHolder(ExecState* exec)
54 : m_globalData(&exec->globalData())
55{
56 m_globalData->apiLock().lock();
57}
9dae56ea 58
6fe7ccc8
A
59JSLockHolder::JSLockHolder(JSGlobalData* globalData)
60 : m_globalData(globalData)
9dae56ea 61{
6fe7ccc8
A
62 m_globalData->apiLock().lock();
63}
9dae56ea 64
6fe7ccc8
A
65JSLockHolder::JSLockHolder(JSGlobalData& globalData)
66 : m_globalData(&globalData)
67{
68 m_globalData->apiLock().lock();
9dae56ea
A
69}
70
6fe7ccc8 71JSLockHolder::~JSLockHolder()
9dae56ea 72{
6fe7ccc8 73 m_globalData->apiLock().unlock();
9dae56ea
A
74}
75
6fe7ccc8
A
76JSLock::JSLock()
77 : m_lockCount(0)
9dae56ea 78{
6fe7ccc8 79 m_spinLock.Init();
9dae56ea
A
80}
81
6fe7ccc8 82JSLock::~JSLock()
14957cd0 83{
14957cd0
A
84}
85
6fe7ccc8 86void JSLock::lock()
9dae56ea 87{
6fe7ccc8
A
88 ThreadIdentifier currentThread = WTF::currentThread();
89 {
90 SpinLockHolder holder(&m_spinLock);
91 if (m_ownerThread == currentThread && m_lockCount) {
92 m_lockCount++;
93 return;
94 }
95 }
9dae56ea 96
6fe7ccc8 97 m_lock.lock();
9dae56ea 98
6fe7ccc8
A
99 {
100 SpinLockHolder holder(&m_spinLock);
101 m_ownerThread = currentThread;
102 ASSERT(!m_lockCount);
103 m_lockCount = 1;
9dae56ea 104 }
9dae56ea
A
105}
106
6fe7ccc8 107void JSLock::unlock()
9dae56ea 108{
6fe7ccc8 109 ASSERT(currentThreadIsHoldingLock());
9dae56ea 110
6fe7ccc8
A
111 SpinLockHolder holder(&m_spinLock);
112 m_lockCount--;
9dae56ea 113
6fe7ccc8
A
114 if (!m_lockCount)
115 m_lock.unlock();
9dae56ea
A
116}
117
118void JSLock::lock(ExecState* exec)
119{
6fe7ccc8 120 exec->globalData().apiLock().lock();
9dae56ea
A
121}
122
123void JSLock::unlock(ExecState* exec)
124{
6fe7ccc8 125 exec->globalData().apiLock().unlock();
9dae56ea
A
126}
127
128bool JSLock::currentThreadIsHoldingLock()
129{
6fe7ccc8 130 return m_lockCount && m_ownerThread == WTF::currentThread();
9dae56ea
A
131}
132
133// This is fairly nasty. We allow multiple threads to run on the same
134// context, and we do not require any locking semantics in doing so -
135// clients of the API may simply use the context from multiple threads
136// concurently, and assume this will work. In order to make this work,
137// We lock the context when a thread enters, and unlock it when it leaves.
138// However we do not only unlock when the thread returns from its
139// entry point (evaluate script or call function), we also unlock the
140// context if the thread leaves JSC by making a call out to an external
141// function through a callback.
142//
143// All threads using the context share the same JS stack (the RegisterFile).
144// Whenever a thread calls into JSC it starts using the RegisterFile from the
145// previous 'high water mark' - the maximum point the stack has ever grown to
146// (returned by RegisterFile::end()). So if a first thread calls out to a
147// callback, and a second thread enters JSC, then also exits by calling out
148// to a callback, we can be left with stackframes from both threads in the
149// RegisterFile. As such, a problem may occur should the first thread's
150// callback complete first, and attempt to return to JSC. Were we to allow
151// this to happen, and were its stack to grow further, then it may potentially
152// write over the second thread's call frames.
153//
6fe7ccc8 154// To avoid JS stack corruption we enforce a policy of only ever allowing two
9dae56ea
A
155// threads to use a JS context concurrently, and only allowing the second of
156// these threads to execute until it has completed and fully returned from its
157// outermost call into JSC. We enforce this policy using 'lockDropDepth'. The
158// first time a thread exits it will call DropAllLocks - which will do as expected
159// and drop locks allowing another thread to enter. Should another thread, or the
160// same thread again, enter JSC (through evaluate script or call function), and exit
161// again through a callback, then the locks will not be dropped when DropAllLocks
162// is called (since lockDropDepth is non-zero). Since this thread is still holding
6fe7ccc8 163// the locks, only it will be able to re-enter JSC (either be returning from the
9dae56ea
A
164// callback, or by re-entering through another call to evaulate script or call
165// function).
166//
167// This policy is slightly more restricive than it needs to be for correctness -
168// we could validly allow futher entries into JSC from other threads, we only
169// need ensure that callbacks return in the reverse chronological order of the
170// order in which they were made - though implementing the less restrictive policy
171// would likely increase complexity and overhead.
172//
9dae56ea 173
6fe7ccc8
A
174// This function returns the number of locks that were dropped.
175unsigned JSLock::dropAllLocks()
9dae56ea 176{
6fe7ccc8
A
177 unsigned lockCount;
178 {
179 // Check if this thread is currently holding the lock.
180 // FIXME: Maybe we want to require this, guard with an ASSERT?
181 SpinLockHolder holder(&m_spinLock);
182 lockCount = m_lockCount;
183 if (!lockCount || m_ownerThread != WTF::currentThread())
184 return 0;
9dae56ea
A
185 }
186
6fe7ccc8
A
187 // Don't drop the locks if they've already been dropped once.
188 // (If the prior drop came from another thread, and it resumed first,
189 // it could trash our register file).
190 if (m_lockDropDepth)
191 return 0;
192
193 // m_lockDropDepth is only incremented if any locks were dropped.
194 m_lockDropDepth++;
195 m_lockCount = 0;
196 m_lock.unlock();
197 return lockCount;
9dae56ea
A
198}
199
6fe7ccc8 200unsigned JSLock::dropAllLocksUnconditionally()
9dae56ea 201{
6fe7ccc8
A
202 unsigned lockCount;
203 {
204 // Check if this thread is currently holding the lock.
205 // FIXME: Maybe we want to require this, guard with an ASSERT?
206 SpinLockHolder holder(&m_spinLock);
207 lockCount = m_lockCount;
208 if (!lockCount || m_ownerThread != WTF::currentThread())
209 return 0;
210 }
9dae56ea 211
6fe7ccc8
A
212 // m_lockDropDepth is only incremented if any locks were dropped.
213 m_lockDropDepth++;
214 m_lockCount = 0;
215 m_lock.unlock();
216 return lockCount;
217}
218
219void JSLock::grabAllLocks(unsigned lockCount)
220{
221 // If no locks were dropped, nothing to do!
222 if (!lockCount)
9dae56ea 223 return;
6fe7ccc8
A
224
225 ThreadIdentifier currentThread = WTF::currentThread();
226 {
227 // Check if this thread is currently holding the lock.
228 // FIXME: Maybe we want to prohibit this, guard against with an ASSERT?
229 SpinLockHolder holder(&m_spinLock);
230 if (m_ownerThread == currentThread && m_lockCount) {
231 m_lockCount += lockCount;
232 m_lockDropDepth--;
233 return;
234 }
235 }
236
237 m_lock.lock();
238
239 {
240 SpinLockHolder holder(&m_spinLock);
241 m_ownerThread = currentThread;
242 ASSERT(!m_lockCount);
243 m_lockCount = lockCount;
244 m_lockDropDepth--;
9dae56ea 245 }
6fe7ccc8 246}
9dae56ea 247
6fe7ccc8
A
248JSLock::DropAllLocks::DropAllLocks(ExecState* exec, AlwaysDropLocksTag alwaysDropLocks)
249 : m_lockCount(0)
250 , m_globalData(&exec->globalData())
251{
252 if (alwaysDropLocks)
253 m_lockCount = m_globalData->apiLock().dropAllLocksUnconditionally();
254 else
255 m_lockCount = m_globalData->apiLock().dropAllLocks();
256}
9dae56ea 257
6fe7ccc8
A
258JSLock::DropAllLocks::DropAllLocks(JSGlobalData* globalData, AlwaysDropLocksTag alwaysDropLocks)
259 : m_lockCount(0)
260 , m_globalData(globalData)
261{
262 if (alwaysDropLocks)
263 m_lockCount = m_globalData->apiLock().dropAllLocksUnconditionally();
264 else
265 m_lockCount = m_globalData->apiLock().dropAllLocks();
9dae56ea
A
266}
267
268JSLock::DropAllLocks::~DropAllLocks()
269{
6fe7ccc8
A
270 m_globalData->apiLock().grabAllLocks(m_lockCount);
271}
272
273#else // (OS(DARWIN) || USE(PTHREADS))
274
275GlobalJSLock::GlobalJSLock()
276{
277}
278
279GlobalJSLock::~GlobalJSLock()
280{
281}
9dae56ea 282
6fe7ccc8
A
283JSLockHolder::JSLockHolder(JSGlobalData*)
284{
9dae56ea
A
285}
286
6fe7ccc8
A
287JSLockHolder::JSLockHolder(JSGlobalData&)
288{
289}
9dae56ea 290
6fe7ccc8 291JSLockHolder::JSLockHolder(ExecState*)
9dae56ea
A
292{
293}
294
6fe7ccc8
A
295JSLockHolder::~JSLockHolder()
296{
297}
298
299JSLock::JSLock()
300{
301}
302
303JSLock::~JSLock()
9dae56ea 304{
9dae56ea
A
305}
306
307bool JSLock::currentThreadIsHoldingLock()
308{
309 return true;
310}
311
6fe7ccc8 312void JSLock::lock()
9dae56ea
A
313{
314}
315
6fe7ccc8 316void JSLock::unlock()
9dae56ea
A
317{
318}
319
320void JSLock::lock(ExecState*)
321{
322}
323
324void JSLock::unlock(ExecState*)
325{
326}
327
6fe7ccc8
A
328void JSLock::lock(JSGlobalData&)
329{
330}
331
332void JSLock::unlock(JSGlobalData&)
333{
334}
335
336unsigned JSLock::dropAllLocks()
337{
338 return 0;
339}
340
341unsigned JSLock::dropAllLocksUnconditionally()
342{
343 return 0;
344}
345
346void JSLock::grabAllLocks(unsigned)
347{
348}
349
9dae56ea
A
350JSLock::DropAllLocks::DropAllLocks(ExecState*)
351{
352}
353
6fe7ccc8 354JSLock::DropAllLocks::DropAllLocks(JSGlobalData*)
9dae56ea
A
355{
356}
357
358JSLock::DropAllLocks::~DropAllLocks()
359{
360}
361
6fe7ccc8 362#endif // (OS(DARWIN) || USE(PTHREADS))
9dae56ea
A
363
364} // namespace JSC