• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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