1 /* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef COMMON_INTERFACES_THREAD_THREAD_HOLDER_H 17 #define COMMON_INTERFACES_THREAD_THREAD_HOLDER_H 18 19 #include <condition_variable> 20 #include <mutex> 21 #include <unordered_set> 22 23 #include "base/common.h" 24 #include "heap/heap_visitor.h" 25 #include "thread/mutator_base.h" 26 #include "thread/thread_state.h" 27 28 namespace panda::ecmascript { 29 class JSThread; 30 } 31 32 namespace ark { 33 class Coroutine; 34 } 35 36 namespace common { 37 class BaseThread; 38 class ThreadHolderManager; 39 40 /** 41 * ThreadHolder does two things: 42 * 1. Represent the current ThreadState(RUNNING for VM, or NATIVE for NativeCode) for all the execution BaseThread 43 * registered to this ThreadHolder, they will share the same ThreadState. And be responsible for transfer 44 * ThreadState, and is the control unit for ThreadHolderManager::SuspendAll()/ResumeAll(). 45 * 2. Maintain all the execution BaseThread registered to the current ThreadHolder. 46 * 47 * ThreadHolder is a package of execution BaseThreads which must run in the same OS Thread and so could 48 * share ThreadState. 49 */ 50 class ThreadHolder { 51 public: 52 using JSThread = panda::ecmascript::JSThread; 53 using Coroutine = ark::Coroutine; 54 using MutatorBase = common::MutatorBase; 55 ThreadHolder(MutatorBase * mutatorBase)56 ThreadHolder(MutatorBase *mutatorBase) : mutatorBase_(mutatorBase) 57 { 58 SetCurrent(this); 59 } 60 ~ThreadHolder()61 ~ThreadHolder() 62 { 63 SetCurrent(nullptr); 64 } 65 66 static ThreadHolder *GetCurrent(); 67 static void SetCurrent(ThreadHolder *holder); 68 69 // This is a temporary impl so we need pass vm from JSThread, or nullptr otherwise 70 static ThreadHolder *CreateAndRegisterNewThreadHolder(void *vm); 71 static void DestroyThreadHolder(ThreadHolder *holder); 72 73 // Transfer to Running no matter in Running or Native. 74 inline void TransferToRunning(); 75 76 // Transfer to Native no matter in Running or Native. 77 inline void TransferToNative(); 78 79 // If current in Native, transfer to Running and return true; 80 // If current in Running, do nothing and return false. 81 inline bool TransferToRunningIfInNative(); 82 83 // If current in Running, transfer to Native and return true; 84 // If current in Native, do nothing and return false. 85 inline bool TransferToNativeIfInRunning(); 86 CheckSafepointIfSuspended()87 bool CheckSafepointIfSuspended() 88 { 89 if (UNLIKELY_CC(HasSuspendRequest())) { 90 WaitSuspension(); 91 return true; 92 } 93 return false; 94 } 95 96 void WaitSuspension(); 97 HasSuspendRequest()98 bool HasSuspendRequest() const 99 { 100 return mutatorBase_->HasAnySuspensionRequest(); 101 } 102 IsInRunningState()103 bool IsInRunningState() const 104 { 105 return !mutatorBase_->InSaferegion(); 106 } 107 108 // Thread must be binded mutator before to allocate. Otherwise it cannot allocate heap object in this thread. 109 // One thread only allow to bind one muatator. If try bind sencond mutator, will be fatal. 110 void BindMutator(); 111 // One thread only allow to bind one muatator. So it must be unbinded mutator before bind another one. 112 void UnbindMutator(); 113 // unify JSThread* and Coroutine* 114 // When register a thread, it must be initialized, i.e. it's safe to visit GC-Root. 115 void RegisterJSThread(JSThread *jsThread); 116 void UnregisterJSThread(JSThread *jsThread); 117 void RegisterCoroutine(Coroutine *coroutine); 118 void UnregisterCoroutine(Coroutine *coroutine); 119 void VisitAllThreads(CommonRootVisitor visitor); 120 121 // Get the thread-local alloction buffer, which is used for fast path of allocating heap objects. 122 // It should be used after binding mutator, and will be invalid after unbinding. GetAllocBuffer()123 void* GetAllocBuffer() const 124 { 125 DCHECK_CC(allocBuffer_ != nullptr); 126 return allocBuffer_; 127 } 128 129 void ReleaseAllocBuffer(); 130 GetJSThread()131 JSThread* GetJSThread() const 132 { 133 return jsThread_; 134 } 135 GetMutator()136 void *GetMutator() const 137 { 138 return mutatorBase_->mutator_; 139 } 140 GetMutatorPhase()141 GCPhase GetMutatorPhase() const 142 { 143 return mutatorBase_->GetMutatorPhase(); 144 } 145 146 // Return if thread has already binded mutator. 147 class TryBindMutatorScope { 148 public: 149 TryBindMutatorScope(ThreadHolder *holder); 150 ~TryBindMutatorScope(); 151 152 private: 153 ThreadHolder *holder_ {nullptr}; 154 }; 155 GetMutatorBaseOffset()156 static constexpr size_t GetMutatorBaseOffset() 157 { 158 return offsetof(ThreadHolder, mutatorBase_); 159 } 160 161 private: 162 // Return false if thread has already binded mutator. Otherwise bind a mutator. 163 bool TryBindMutator(); 164 165 MutatorBase *mutatorBase_ {nullptr}; 166 167 // Used for allocation fastpath, it is binded to thread local panda::AllocationBuffer. 168 void* allocBuffer_ {nullptr}; 169 170 // Access jsThreads/coroutines(iterate/insert/remove) must happen in RunningState from the currentThreadHolder, or 171 // in SuspendAll from others, because daemon thread may iterate if in NativeState. 172 // And if we use locks to make that thread safe, it would cause a AB-BA dead lock. 173 JSThread *jsThread_ {nullptr}; 174 std::unordered_set<Coroutine *> coroutines_ {}; 175 176 NO_COPY_SEMANTIC_CC(ThreadHolder); 177 NO_MOVE_SEMANTIC_CC(ThreadHolder); 178 179 friend JSThread; 180 friend ThreadHolderManager; 181 }; 182 } // namespace common 183 #endif // COMMON_INTERFACES_THREAD_THREAD_HOLDER_H 184