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