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();
73
74private:
75 RefPtr<VM> m_vm;
76};
77
78class JSLock : public ThreadSafeRefCounted<JSLock> {
79 WTF_MAKE_NONCOPYABLE(JSLock);
80public:
81 JSLock(VM*);
82 JS_EXPORT_PRIVATE ~JSLock();
83
84 JS_EXPORT_PRIVATE void lock();
85 JS_EXPORT_PRIVATE void unlock();
86
87 static void lock(ExecState*);
88 static void unlock(ExecState*);
89 static void lock(VM&);
90 static void unlock(VM&);
91
92 VM* vm() { return m_vm; }
93
94 Optional<RefPtr<Thread>> ownerThread() const
95 {
96 if (m_hasOwnerThread)
97 return m_ownerThread;
98 return WTF::nullopt;
99 }
100 bool currentThreadIsHoldingLock() { return m_hasOwnerThread && m_ownerThread.get() == &Thread::current(); }
101
102 void willDestroyVM(VM*);
103
104 class DropAllLocks {
105 WTF_MAKE_NONCOPYABLE(DropAllLocks);
106 public:
107 JS_EXPORT_PRIVATE DropAllLocks(ExecState*);
108 JS_EXPORT_PRIVATE DropAllLocks(VM*);
109 JS_EXPORT_PRIVATE DropAllLocks(VM&);
110 JS_EXPORT_PRIVATE ~DropAllLocks();
111
112 void setDropDepth(unsigned depth) { m_dropDepth = depth; }
113 unsigned dropDepth() const { return m_dropDepth; }
114
115 private:
116 intptr_t m_droppedLockCount;
117 RefPtr<VM> m_vm;
118 unsigned m_dropDepth;
119 };
120
121 void makeWebThreadAware()
122 {
123 m_isWebThreadAware = true;
124 }
125
126 bool isWebThreadAware() const { return m_isWebThreadAware; }
127
128private:
129 void lock(intptr_t lockCount);
130 void unlock(intptr_t unlockCount);
131
132 void didAcquireLock();
133 void willReleaseLock();
134
135 unsigned dropAllLocks(DropAllLocks*);
136 void grabAllLocks(DropAllLocks*, unsigned lockCount);
137
138 Lock m_lock;
139 bool m_isWebThreadAware { false };
140 // We cannot make m_ownerThread an optional (instead of pairing it with an explicit
141 // m_hasOwnerThread) because currentThreadIsHoldingLock() may be called from a
142 // different thread, and an optional is vulnerable to races.
143 // See https://bugs.webkit.org/show_bug.cgi?id=169042#c6
144 bool m_hasOwnerThread { false };
145 RefPtr<Thread> m_ownerThread;
146 intptr_t m_lockCount;
147 unsigned m_lockDropDepth;
148 bool m_shouldReleaseHeapAccess;
149 VM* m_vm;
150 AtomStringTable* m_entryAtomStringTable;
151};
152
153} // namespace
154