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 #ifndef PANDA_RUNTIME_THREAD_PROXY_STATIC_H 16 #define PANDA_RUNTIME_THREAD_PROXY_STATIC_H 17 18 #include "runtime/include/thread_interface.h" 19 #include "libpandabase/macros.h" 20 21 #include <atomic> 22 23 namespace ark { 24 25 class MutatorLock; 26 27 class ThreadProxyStatic : public ThreadInterface { 28 public: ThreadProxyStatic(MutatorLock * mutatorLock)29 explicit ThreadProxyStatic(MutatorLock *mutatorLock) : ThreadInterface(mutatorLock) {} 30 31 // NO_THREAD_SANITIZE for invalid TSAN data race report 32 NO_THREAD_SANITIZE bool TestAllFlags() const; 33 34 PANDA_PUBLIC_API enum ThreadStatus GetStatus() const; 35 36 void InitializeThreadFlag(); 37 38 void CleanUpThreadStatus(); 39 40 PANDA_PUBLIC_API void UpdateStatus(enum ThreadStatus status); 41 IsSuspended()42 inline bool IsSuspended() const 43 { 44 return ReadFlag(SUSPEND_REQUEST); 45 } 46 IsRuntimeTerminated()47 inline bool IsRuntimeTerminated() const 48 { 49 return ReadFlag(RUNTIME_TERMINATION_REQUEST); 50 } 51 SetRuntimeTerminated()52 inline void SetRuntimeTerminated() 53 { 54 SetFlag(RUNTIME_TERMINATION_REQUEST); 55 } 56 IsThreadAlive()57 bool IsThreadAlive() const 58 { 59 return GetStatus() != ThreadStatus::FINISHED; 60 } 61 GetFlagOffset()62 static constexpr uint32_t GetFlagOffset() 63 { 64 return MEMBER_OFFSET(ThreadProxyStatic, fts_); 65 } 66 67 static void InitializeInitThreadFlag(); 68 69 // NO_THREAD_SAFETY_ANALYSIS due to TSAN not being able to determine lock status 70 /// Transition to suspended and back to runnable, re-acquire share on mutator_lock_ 71 PANDA_PUBLIC_API void SuspendCheck() NO_THREAD_SAFETY_ANALYSIS; 72 73 PANDA_PUBLIC_API void SuspendImpl(bool internalSuspend = false); 74 75 PANDA_PUBLIC_API void ResumeImpl(bool internalResume = false); 76 77 PANDA_PUBLIC_API void SafepointPoll(); 78 79 PANDA_PUBLIC_API bool IsUserSuspended() const; 80 81 PANDA_PUBLIC_API void WaitSuspension(); 82 83 void MakeTSANHappyForThreadState(); 84 85 /* @sync 1 86 * @description This synchronization point can be used to insert a new attribute or method 87 * into ManagedThread class. 88 */ 89 private: 90 // NO_THREAD_SAFETY_ANALYSIS due to TSAN not being able to determine lock status 91 void TransitionFromRunningToSuspended(enum ThreadStatus status) NO_THREAD_SAFETY_ANALYSIS; 92 93 // Separate functions for NO_THREAD_SANITIZE to suppress TSAN data race report 94 NO_THREAD_SANITIZE uint32_t ReadFlagsAndThreadStatusUnsafe(); 95 96 // NO_THREAD_SANITIZE for invalid TSAN data race report ReadFlag(ThreadFlag flag)97 NO_THREAD_SANITIZE bool ReadFlag(ThreadFlag flag) const 98 { 99 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) 100 return (fts_.asStruct.flags & static_cast<uint16_t>(flag)) != 0; 101 } 102 103 // Separate functions for NO_THREAD_SANITIZE to suppress TSAN data race report 104 NO_THREAD_SANITIZE uint32_t ReadFlagsUnsafe() const; 105 106 void SetFlag(ThreadFlag flag); 107 108 void ClearFlag(ThreadFlag flag); 109 110 enum SafepointFlag : bool { DONT_CHECK_SAFEPOINT = false, CHECK_SAFEPOINT = true }; 111 enum ReadlockFlag : bool { NO_READLOCK = false, READLOCK = true }; 112 113 // NO_THREAD_SAFETY_ANALYSIS due to TSAN not being able to determine lock status 114 template <SafepointFlag SAFEPOINT = DONT_CHECK_SAFEPOINT, ReadlockFlag READLOCK_FLAG = NO_READLOCK> StoreStatus(ThreadStatus status)115 void StoreStatus(ThreadStatus status) NO_THREAD_SAFETY_ANALYSIS 116 { 117 // NOLINTBEGIN(cppcoreguidelines-pro-type-union-access) 118 while (true) { 119 union FlagsAndThreadStatus oldFts { 120 }; 121 union FlagsAndThreadStatus newFts { 122 }; 123 oldFts.asInt = ReadFlagsAndThreadStatusUnsafe(); 124 125 // NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements) 126 if constexpr (SAFEPOINT == CHECK_SAFEPOINT) { // NOLINT(bugprone-suspicious-semicolon) 127 if (oldFts.asStructNonvolatile.flags != initialThreadFlag_) { 128 // someone requires a safepoint 129 SafepointPoll(); 130 continue; 131 } 132 } 133 134 // mutator lock should be acquired before change status 135 // to avoid blocking in running state 136 // NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements) 137 if constexpr (READLOCK_FLAG == READLOCK) { // NOLINT(bugprone-suspicious-semicolon) 138 GetMutatorLock()->ReadLock(); 139 } 140 141 // clang-format conflicts with CodeCheckAgent, so disable it here 142 // clang-format off 143 // If it's not required to check safepoint, CAS would behave the same as 144 // regular STORE, just will do extra attempts, in this case it makes sense 145 // to use STORE for just thread status 16-bit word. 146 if constexpr (SAFEPOINT == DONT_CHECK_SAFEPOINT) { 147 auto newStatus = static_cast<uint16_t>(status); 148 // Atomic with release order reason: data race with other mutators 149 fts_.asAtomic.status.store(newStatus, std::memory_order_release); 150 break; 151 } 152 153 // if READLOCK, there's chance, someone changed the flags 154 // in parallel, let's check before the CAS. 155 if constexpr (READLOCK_FLAG == READLOCK) { 156 if (ReadFlagsUnsafe() != oldFts.asStructNonvolatile.flags) { 157 GetMutatorLock()->Unlock(); 158 continue; 159 } 160 } 161 162 newFts.asStructNonvolatile.flags = oldFts.asStructNonvolatile.flags; 163 newFts.asStructNonvolatile.status = status; 164 // Atomic with release order reason: data race with other mutators 165 if (fts_.asAtomicInt.compare_exchange_weak( 166 oldFts.asNonvolatileInt, newFts.asNonvolatileInt, std::memory_order_release)) { 167 // If CAS succeeded, we set new status and no request occurred here, safe to proceed. 168 break; 169 } 170 // Release mutator lock to acquire it on the next loop iteration 171 // clang-format on 172 // NOLINTNEXTLINE(readability-braces-around-statements, hicpp-braces-around-statements) 173 if constexpr (READLOCK_FLAG == READLOCK) { // NOLINT(bugprone-suspicious-semicolon) 174 GetMutatorLock()->Unlock(); 175 } 176 } 177 // NOLINTEND(cppcoreguidelines-pro-type-union-access) 178 } 179 180 union __attribute__((__aligned__(4))) FlagsAndThreadStatus { 181 FlagsAndThreadStatus() = default; 182 ~FlagsAndThreadStatus() = default; 183 184 volatile uint32_t asInt; 185 uint32_t asNonvolatileInt; 186 std::atomic_uint32_t asAtomicInt; 187 188 struct __attribute__((packed)) { 189 volatile uint16_t flags; 190 volatile enum ThreadStatus status; 191 } asStruct; 192 193 struct __attribute__((packed)) { 194 uint16_t flags; 195 enum ThreadStatus status; 196 } asStructNonvolatile; 197 198 struct { 199 std::atomic_uint16_t flags; 200 std::atomic_uint16_t status; 201 } asAtomic; 202 203 NO_COPY_SEMANTIC(FlagsAndThreadStatus); 204 NO_MOVE_SEMANTIC(FlagsAndThreadStatus); 205 }; 206 207 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes) 208 FlagsAndThreadStatus fts_ {}; 209 PANDA_PUBLIC_API static ThreadFlag initialThreadFlag_; 210 // NOLINTEND(misc-non-private-member-variables-in-classes) 211 212 static constexpr uint32_t THREAD_STATUS_OFFSET = 16; 213 static_assert(sizeof(fts_) == sizeof(uint32_t), "Wrong fts_ size"); 214 215 os::memory::ConditionVariable suspendVar_ GUARDED_BY(suspendLock_); 216 os::memory::Mutex suspendLock_; 217 uint32_t suspendCount_ GUARDED_BY(suspendLock_) = 0; 218 std::atomic_uint32_t userCodeSuspendCount_ {0}; 219 }; 220 221 } // namespace ark 222 223 #endif // PANDA_RUNTIME_THREAD_PROXY_STATIC_H 224