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