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