b6bb1300068da9ec487fe050c56ccdd66a7c4f43
[WebKit.git] / Source / JavaScriptCore / runtime / JSLock.cpp
1 /*
2  * Copyright (C) 2005-2018 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 "JSCInlines.h"
29 #include "MachineStackMarker.h"
30 #include "SamplingProfiler.h"
31 #include "ThreadLocalCacheInlines.h"
32 #include "WasmMachineThreads.h"
33 #include <thread>
34 #include <wtf/Threading.h>
35 #include <wtf/threads/Signals.h>
36
37 namespace JSC {
38
39 StaticLock GlobalJSLock::s_sharedInstanceMutex;
40
41 GlobalJSLock::GlobalJSLock()
42 {
43     s_sharedInstanceMutex.lock();
44 }
45
46 GlobalJSLock::~GlobalJSLock()
47 {
48     s_sharedInstanceMutex.unlock();
49 }
50
51 JSLockHolder::JSLockHolder(ExecState* exec)
52     : m_vm(&exec->vm())
53 {
54     init();
55 }
56
57 JSLockHolder::JSLockHolder(VM* vm)
58     : m_vm(vm)
59 {
60     init();
61 }
62
63 JSLockHolder::JSLockHolder(VM& vm)
64     : m_vm(&vm)
65 {
66     init();
67 }
68
69 void JSLockHolder::init()
70 {
71     m_vm->apiLock().lock();
72 }
73
74 JSLockHolder::~JSLockHolder()
75 {
76     RefPtr<JSLock> apiLock(&m_vm->apiLock());
77     m_vm = nullptr;
78     apiLock->unlock();
79 }
80
81 JSLock::JSLock(VM* vm)
82     : m_lockCount(0)
83     , m_lockDropDepth(0)
84     , m_vm(vm)
85     , m_entryAtomicStringTable(nullptr)
86 {
87 }
88
89 JSLock::~JSLock()
90 {
91 }
92
93 void JSLock::willDestroyVM(VM* vm)
94 {
95     ASSERT_UNUSED(vm, m_vm == vm);
96     m_vm = nullptr;
97 }
98
99 void JSLock::lock()
100 {
101     lock(1);
102 }
103
104 void JSLock::lock(intptr_t lockCount)
105 {
106     ASSERT(lockCount > 0);
107     bool success = m_lock.tryLock();
108     if (UNLIKELY(!success)) {
109         if (currentThreadIsHoldingLock()) {
110             m_lockCount += lockCount;
111             return;
112         }
113         m_lock.lock();
114     }
115
116     m_ownerThread = &Thread::current();
117     WTF::storeStoreFence();
118     m_hasOwnerThread = true;
119     ASSERT(!m_lockCount);
120     m_lockCount = lockCount;
121
122     didAcquireLock();
123 }
124
125 void JSLock::didAcquireLock()
126 {
127     WTF::speculationFence();
128     
129     // FIXME: What should happen to the per-thread identifier table if we don't have a VM?
130     if (!m_vm)
131         return;
132     
133     Thread& thread = Thread::current();
134     ASSERT(!m_entryAtomicStringTable);
135     m_entryAtomicStringTable = thread.setCurrentAtomicStringTable(m_vm->atomicStringTable());
136     ASSERT(m_entryAtomicStringTable);
137
138     if (m_vm->heap.hasAccess())
139         m_shouldReleaseHeapAccess = false;
140     else {
141         m_vm->heap.acquireAccess();
142         m_shouldReleaseHeapAccess = true;
143     }
144
145     RELEASE_ASSERT(!m_vm->stackPointerAtVMEntry());
146     void* p = &p; // A proxy for the current stack pointer.
147     m_vm->setStackPointerAtVMEntry(p);
148
149     m_vm->setLastStackTop(thread.savedLastStackTop());
150     ASSERT(thread.stack().contains(m_vm->lastStackTop()));
151     
152     m_vm->defaultThreadLocalCache->install(*m_vm);
153     
154     m_vm->heap.machineThreads().addCurrentThread();
155 #if ENABLE(WEBASSEMBLY)
156     Wasm::startTrackingCurrentThread();
157 #endif
158
159 #if HAVE(MACH_EXCEPTIONS)
160     registerThreadForMachExceptionHandling(Thread::current());
161 #endif
162
163     // Note: everything below must come after addCurrentThread().
164     m_vm->traps().notifyGrabAllLocks();
165     
166     m_vm->firePrimitiveGigacageEnabledIfNecessary();
167
168 #if ENABLE(SAMPLING_PROFILER)
169     if (SamplingProfiler* samplingProfiler = m_vm->samplingProfiler())
170         samplingProfiler->noticeJSLockAcquisition();
171 #endif
172 }
173
174 void JSLock::unlock()
175 {
176     unlock(1);
177 }
178
179 void JSLock::unlock(intptr_t unlockCount)
180 {
181     RELEASE_ASSERT(currentThreadIsHoldingLock());
182     ASSERT(m_lockCount >= unlockCount);
183
184     // Maintain m_lockCount while calling willReleaseLock() so that its callees know that
185     // they still have the lock.
186     if (unlockCount == m_lockCount)
187         willReleaseLock();
188
189     m_lockCount -= unlockCount;
190
191     if (!m_lockCount) {
192         m_hasOwnerThread = false;
193         m_lock.unlock();
194     }
195 }
196
197 void JSLock::willReleaseLock()
198 {
199     WTF::speculationFence();
200     
201     RefPtr<VM> vm = m_vm;
202     if (vm) {
203         vm->drainMicrotasks();
204
205         vm->heap.releaseDelayedReleasedObjects();
206         vm->setStackPointerAtVMEntry(nullptr);
207         
208         if (m_shouldReleaseHeapAccess)
209             vm->heap.releaseAccess();
210     }
211
212     if (m_entryAtomicStringTable) {
213         Thread::current().setCurrentAtomicStringTable(m_entryAtomicStringTable);
214         m_entryAtomicStringTable = nullptr;
215     }
216 }
217
218 void JSLock::lock(ExecState* exec)
219 {
220     exec->vm().apiLock().lock();
221 }
222
223 void JSLock::unlock(ExecState* exec)
224 {
225     exec->vm().apiLock().unlock();
226 }
227
228 // This function returns the number of locks that were dropped.
229 unsigned JSLock::dropAllLocks(DropAllLocks* dropper)
230 {
231     if (!currentThreadIsHoldingLock())
232         return 0;
233
234     ++m_lockDropDepth;
235
236     dropper->setDropDepth(m_lockDropDepth);
237
238     Thread& thread = Thread::current();
239     thread.setSavedStackPointerAtVMEntry(m_vm->stackPointerAtVMEntry());
240     thread.setSavedLastStackTop(m_vm->lastStackTop());
241
242     unsigned droppedLockCount = m_lockCount;
243     unlock(droppedLockCount);
244
245     return droppedLockCount;
246 }
247
248 void JSLock::grabAllLocks(DropAllLocks* dropper, unsigned droppedLockCount)
249 {
250     // If no locks were dropped, nothing to do!
251     if (!droppedLockCount)
252         return;
253
254     ASSERT(!currentThreadIsHoldingLock());
255     lock(droppedLockCount);
256
257     while (dropper->dropDepth() != m_lockDropDepth) {
258         unlock(droppedLockCount);
259         Thread::yield();
260         lock(droppedLockCount);
261     }
262
263     --m_lockDropDepth;
264
265     Thread& thread = Thread::current();
266     m_vm->setStackPointerAtVMEntry(thread.savedStackPointerAtVMEntry());
267     m_vm->setLastStackTop(thread.savedLastStackTop());
268 }
269
270 JSLock::DropAllLocks::DropAllLocks(VM* vm)
271     : m_droppedLockCount(0)
272     // If the VM is in the middle of being destroyed then we don't want to resurrect it
273     // by allowing DropAllLocks to ref it. By this point the JSLock has already been 
274     // released anyways, so it doesn't matter that DropAllLocks is a no-op.
275     , m_vm(vm->refCount() ? vm : nullptr)
276 {
277     if (!m_vm)
278         return;
279     RELEASE_ASSERT(!m_vm->apiLock().currentThreadIsHoldingLock() || !m_vm->isCollectorBusyOnCurrentThread());
280     m_droppedLockCount = m_vm->apiLock().dropAllLocks(this);
281 }
282
283 JSLock::DropAllLocks::DropAllLocks(ExecState* exec)
284     : DropAllLocks(exec ? &exec->vm() : nullptr)
285 {
286 }
287
288 JSLock::DropAllLocks::DropAllLocks(VM& vm)
289     : DropAllLocks(&vm)
290 {
291 }
292
293 JSLock::DropAllLocks::~DropAllLocks()
294 {
295     if (!m_vm)
296         return;
297     m_vm->apiLock().grabAllLocks(this, m_droppedLockCount);
298 }
299
300 } // namespace JSC