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 GNU
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#pragma once
22
23#include <mutex>
24#include <wtf/Assertions.h>
25#include <wtf/Lock.h>
26#include <wtf/Noncopyable.h>
27#include <wtf/RefPtr.h>
28#include <wtf/Threading.h>
29#include <wtf/text/AtomStringTable.h>
30
31namespace JSC {
32
33// To make it safe to use JavaScript on multiple threads, it is
34// important to lock before doing anything that allocates a
35// JavaScript data structure or that interacts with shared state
36// such as the protect count hash table. The simplest way to lock
37// is to create a local JSLockHolder object in the scope where the lock
38// must be held and pass it the context that requires protection.
39// The lock is recursive so nesting is ok. The JSLock
40// object also acts as a convenience short-hand for running important
41// initialization routines.
42
43// To avoid deadlock, sometimes it is necessary to temporarily
44// release the lock. Since it is recursive you actually have to
45// release all locks held by your thread. This is safe to do if
46// you are executing code that doesn't require the lock, and you
47// reacquire the right number of locks at the end. You can do this
48// by constructing a locally scoped JSLock::DropAllLocks object. The
49// DropAllLocks object takes care to release the JSLock only if your
50// thread acquired it to begin with.
51
52class ExecState;
53class VM;
54
55// This class is used to protect the initialization of the legacy single
56// shared VM.
57class GlobalJSLock {
58 WTF_MAKE_NONCOPYABLE(GlobalJSLock);
59public:
60 JS_EXPORT_PRIVATE GlobalJSLock();
61 JS_EXPORT_PRIVATE ~GlobalJSLock();
62private:
63 static Lock s_sharedInstanceMutex;
64};
65
66class JSLockHolder {
67public:
68 JS_EXPORT_PRIVATE JSLockHolder(VM*);
69 JS_EXPORT_PRIVATE JSLockHolder(VM&);
70 JS_EXPORT_PRIVATE JSLockHolder(ExecState*);
71
72 JS_EXPORT_PRIVATE ~JSLockHolder();
73private:
74 void init();
75
76 RefPtr<VM> m_vm;
77};
78
79class JSLock : public ThreadSafeRefCounted<JSLock> {
80 WTF_MAKE_NONCOPYABLE(JSLock);
81public:
82 JSLock(VM*);
83 JS_EXPORT_PRIVATE ~JSLock();
84
85 JS_EXPORT_PRIVATE void lock();
86 JS_EXPORT_PRIVATE void unlock();
87
88 static void lock(ExecState*);
89 static void unlock(ExecState*);
90 static void lock(VM&);
91 static void unlock(VM&);
92
93 VM* vm() { return m_vm; }
94
95 Optional<RefPtr<Thread>> ownerThread() const
96 {
97 if (m_hasOwnerThread)
98 return m_ownerThread;
99 return WTF::nullopt;
100 }
101 bool currentThreadIsHoldingLock() { return m_hasOwnerThread && m_ownerThread.get() == &Thread::current(); }
102
103 void willDestroyVM(VM*);
104
105 class DropAllLocks {
106 WTF_MAKE_NONCOPYABLE(DropAllLocks);
107 public:
108 JS_EXPORT_PRIVATE DropAllLocks(ExecState*);
109 JS_EXPORT_PRIVATE DropAllLocks(VM*);
110 JS_EXPORT_PRIVATE DropAllLocks(VM&);
111 JS_EXPORT_PRIVATE ~DropAllLocks();
112
113 void setDropDepth(unsigned depth) { m_dropDepth = depth; }
114 unsigned dropDepth() const { return m_dropDepth; }
115
116 private:
117 intptr_t m_droppedLockCount;
118 RefPtr<VM> m_vm;
119 unsigned m_dropDepth;
120 };
121
122private:
123 void lock(intptr_t lockCount);
124 void unlock(intptr_t unlockCount);
125
126 void didAcquireLock();
127 void willReleaseLock();
128
129 unsigned dropAllLocks(DropAllLocks*);
130 void grabAllLocks(DropAllLocks*, unsigned lockCount);
131
132 Lock m_lock;
133 // We cannot make m_ownerThread an optional (instead of pairing it with an explicit
134 // m_hasOwnerThread) because currentThreadIsHoldingLock() may be called from a
135 // different thread, and an optional is vulnerable to races.
136 // See https://bugs.webkit.org/show_bug.cgi?id=169042#c6
137 bool m_hasOwnerThread { false };
138 RefPtr<Thread> m_ownerThread;
139 intptr_t m_lockCount;
140 unsigned m_lockDropDepth;
141 bool m_shouldReleaseHeapAccess;
142 VM* m_vm;
143 AtomStringTable* m_entryAtomStringTable;
144};
145
146} // namespace
147