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