We should support CreateThis in the FTL
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSLock.h
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/AtomicStringTable.h>
30
31 namespace 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
52 class ExecState;
53 class VM;
54
55 // This class is used to protect the initialization of the legacy single 
56 // shared VM.
57 class GlobalJSLock {
58     WTF_MAKE_NONCOPYABLE(GlobalJSLock);
59 public:
60     JS_EXPORT_PRIVATE GlobalJSLock();
61     JS_EXPORT_PRIVATE ~GlobalJSLock();
62 private:
63     static Lock s_sharedInstanceMutex;
64 };
65
66 class JSLockHolder {
67 public:
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 private:
74     void init();
75
76     RefPtr<VM> m_vm;
77 };
78
79 class JSLock : public ThreadSafeRefCounted<JSLock> {
80     WTF_MAKE_NONCOPYABLE(JSLock);
81 public:
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     std::optional<RefPtr<Thread>> ownerThread() const
96     {
97         if (m_hasOwnerThread)
98             return m_ownerThread;
99         return std::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
122 private:
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     AtomicStringTable* m_entryAtomicStringTable; 
144 };
145
146 } // namespace