]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/JSLock.cpp
087c81dc59b042c47e8a0bd80cdaf127b7a79874
[apple/javascriptcore.git] / runtime / JSLock.cpp
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
24 #include "Heap.h"
25 #include "CallFrame.h"
26 #include "JSGlobalObject.h"
27 #include "JSObject.h"
28 #include "ScopeChain.h"
29
30 #if USE(PTHREADS)
31 #include <pthread.h>
32 #endif
33
34 namespace JSC {
35
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.
39 #if (OS(DARWIN) || USE(PTHREADS))
40
41 static pthread_mutex_t sharedInstanceLock = PTHREAD_MUTEX_INITIALIZER;
42
43 GlobalJSLock::GlobalJSLock()
44 {
45 pthread_mutex_lock(&sharedInstanceLock);
46 }
47
48 GlobalJSLock::~GlobalJSLock()
49 {
50 pthread_mutex_unlock(&sharedInstanceLock);
51 }
52
53 JSLockHolder::JSLockHolder(ExecState* exec)
54 : m_globalData(&exec->globalData())
55 {
56 m_globalData->apiLock().lock();
57 }
58
59 JSLockHolder::JSLockHolder(JSGlobalData* globalData)
60 : m_globalData(globalData)
61 {
62 m_globalData->apiLock().lock();
63 }
64
65 JSLockHolder::JSLockHolder(JSGlobalData& globalData)
66 : m_globalData(&globalData)
67 {
68 m_globalData->apiLock().lock();
69 }
70
71 JSLockHolder::~JSLockHolder()
72 {
73 m_globalData->apiLock().unlock();
74 }
75
76 JSLock::JSLock()
77 : m_lockCount(0)
78 {
79 m_spinLock.Init();
80 }
81
82 JSLock::~JSLock()
83 {
84 }
85
86 void JSLock::lock()
87 {
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 }
96
97 m_lock.lock();
98
99 {
100 SpinLockHolder holder(&m_spinLock);
101 m_ownerThread = currentThread;
102 ASSERT(!m_lockCount);
103 m_lockCount = 1;
104 }
105 }
106
107 void JSLock::unlock()
108 {
109 ASSERT(currentThreadIsHoldingLock());
110
111 SpinLockHolder holder(&m_spinLock);
112 m_lockCount--;
113
114 if (!m_lockCount)
115 m_lock.unlock();
116 }
117
118 void JSLock::lock(ExecState* exec)
119 {
120 exec->globalData().apiLock().lock();
121 }
122
123 void JSLock::unlock(ExecState* exec)
124 {
125 exec->globalData().apiLock().unlock();
126 }
127
128 bool JSLock::currentThreadIsHoldingLock()
129 {
130 return m_lockCount && m_ownerThread == WTF::currentThread();
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 //
154 // To avoid JS stack corruption we enforce a policy of only ever allowing two
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
163 // the locks, only it will be able to re-enter JSC (either be returning from the
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 //
173
174 // This function returns the number of locks that were dropped.
175 unsigned JSLock::dropAllLocks()
176 {
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;
185 }
186
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;
198 }
199
200 unsigned JSLock::dropAllLocksUnconditionally()
201 {
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 }
211
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
219 void JSLock::grabAllLocks(unsigned lockCount)
220 {
221 // If no locks were dropped, nothing to do!
222 if (!lockCount)
223 return;
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--;
245 }
246 }
247
248 JSLock::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 }
257
258 JSLock::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();
266 }
267
268 JSLock::DropAllLocks::~DropAllLocks()
269 {
270 m_globalData->apiLock().grabAllLocks(m_lockCount);
271 }
272
273 #else // (OS(DARWIN) || USE(PTHREADS))
274
275 GlobalJSLock::GlobalJSLock()
276 {
277 }
278
279 GlobalJSLock::~GlobalJSLock()
280 {
281 }
282
283 JSLockHolder::JSLockHolder(JSGlobalData*)
284 {
285 }
286
287 JSLockHolder::JSLockHolder(JSGlobalData&)
288 {
289 }
290
291 JSLockHolder::JSLockHolder(ExecState*)
292 {
293 }
294
295 JSLockHolder::~JSLockHolder()
296 {
297 }
298
299 JSLock::JSLock()
300 {
301 }
302
303 JSLock::~JSLock()
304 {
305 }
306
307 bool JSLock::currentThreadIsHoldingLock()
308 {
309 return true;
310 }
311
312 void JSLock::lock()
313 {
314 }
315
316 void JSLock::unlock()
317 {
318 }
319
320 void JSLock::lock(ExecState*)
321 {
322 }
323
324 void JSLock::unlock(ExecState*)
325 {
326 }
327
328 void JSLock::lock(JSGlobalData&)
329 {
330 }
331
332 void JSLock::unlock(JSGlobalData&)
333 {
334 }
335
336 unsigned JSLock::dropAllLocks()
337 {
338 return 0;
339 }
340
341 unsigned JSLock::dropAllLocksUnconditionally()
342 {
343 return 0;
344 }
345
346 void JSLock::grabAllLocks(unsigned)
347 {
348 }
349
350 JSLock::DropAllLocks::DropAllLocks(ExecState*)
351 {
352 }
353
354 JSLock::DropAllLocks::DropAllLocks(JSGlobalData*)
355 {
356 }
357
358 JSLock::DropAllLocks::~DropAllLocks()
359 {
360 }
361
362 #endif // (OS(DARWIN) || USE(PTHREADS))
363
364 } // namespace JSC