1 /*
2 * Copyright (c) 2021-2024 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 PANDA_LIBPANDABASE_PBASE_OS_MACROS_H_
17 #define PANDA_LIBPANDABASE_PBASE_OS_MACROS_H_
18
19 #if defined(PANDA_USE_FUTEX)
20 #include "platforms/unix/libpandabase/futex/mutex.h"
21 #elif !defined(PANDA_TARGET_UNIX) && !defined(PANDA_TARGET_WINDOWS)
22 #error "Unsupported platform"
23 #endif
24
25 #include "clang.h"
26 #include "macros.h"
27 #include "os/thread.h"
28
29 #include <atomic>
30 #include <pthread.h>
31 #include <tuple>
32
33 namespace ark::os::memory {
34
35 // Dummy lock which locks nothing
36 // but has the same methods as RWLock and Mutex.
37 // Can be used in Locks Holders.
38 class DummyLock {
39 public:
Lock()40 void Lock() const {}
TryLock()41 bool TryLock() const
42 {
43 return true;
44 }
TryReadLock()45 bool TryReadLock() const
46 {
47 return true;
48 }
TryWriteLock()49 bool TryWriteLock() const
50 {
51 return true;
52 }
Unlock()53 void Unlock() const {}
ReadLock()54 void ReadLock() const {}
WriteLock()55 void WriteLock() const {}
56 };
57
58 #if defined(PANDA_USE_FUTEX)
59 using Mutex = ark::os::unix::memory::futex::Mutex;
60 using RecursiveMutex = ark::os::unix::memory::futex::RecursiveMutex;
61 using RWLock = ark::os::unix::memory::futex::RWLock;
62 using ConditionVariable = ark::os::unix::memory::futex::ConditionVariable;
63 #else
64 class ConditionVariable;
65
66 class CAPABILITY("mutex") Mutex {
67 public:
68 PANDA_PUBLIC_API explicit Mutex(bool is_init = true);
69
70 PANDA_PUBLIC_API ~Mutex();
71
72 PANDA_PUBLIC_API void Lock() ACQUIRE();
73
74 PANDA_PUBLIC_API bool TryLock() TRY_ACQUIRE(true);
75
76 PANDA_PUBLIC_API void Unlock() RELEASE();
77
78 // NOTE(mwx851039): Extract common part as an interface.
DoNotCheckOnTerminationLoop()79 static bool DoNotCheckOnTerminationLoop()
80 {
81 return no_check_for_deadlock_;
82 }
83
IgnoreChecksOnTerminationLoop()84 static void IgnoreChecksOnTerminationLoop()
85 {
86 no_check_for_deadlock_ = true;
87 }
88
89 protected:
90 void Init(pthread_mutexattr_t *attrs);
91
92 private:
93 pthread_mutex_t mutex_;
94
95 // This field is set to false in case of deadlock with daemon threads (only daemon threads
96 // are not finished and they have state IS_BLOCKED). In this case we should terminate
97 // those threads ignoring failures on lock structures destructors.
98 PANDA_PUBLIC_API static std::atomic_bool no_check_for_deadlock_;
99
100 NO_COPY_SEMANTIC(Mutex);
101 NO_MOVE_SEMANTIC(Mutex);
102
103 friend ConditionVariable;
104 };
105
106 class CAPABILITY("mutex") RecursiveMutex : public Mutex {
107 public:
108 PANDA_PUBLIC_API RecursiveMutex();
109
110 ~RecursiveMutex() = default;
111
112 NO_COPY_SEMANTIC(RecursiveMutex);
113 NO_MOVE_SEMANTIC(RecursiveMutex);
114 };
115
116 class CAPABILITY("mutex") RWLock {
117 public:
118 PANDA_PUBLIC_API RWLock();
119
120 PANDA_PUBLIC_API ~RWLock();
121
122 PANDA_PUBLIC_API void ReadLock() ACQUIRE_SHARED();
123
124 PANDA_PUBLIC_API void WriteLock() ACQUIRE();
125
126 PANDA_PUBLIC_API bool TryReadLock() TRY_ACQUIRE_SHARED(true);
127
128 PANDA_PUBLIC_API bool TryWriteLock() TRY_ACQUIRE(true);
129
130 PANDA_PUBLIC_API void Unlock() RELEASE_GENERIC();
131
132 private:
133 pthread_rwlock_t rwlock_;
134
135 NO_COPY_SEMANTIC(RWLock);
136 NO_MOVE_SEMANTIC(RWLock);
137 };
138
139 // Some RTOS could not have support for condition variables, so this primitive should be used carefully
140 class ConditionVariable {
141 public:
142 PANDA_PUBLIC_API ConditionVariable();
143
144 PANDA_PUBLIC_API ~ConditionVariable();
145
146 PANDA_PUBLIC_API void Signal();
147
148 PANDA_PUBLIC_API void SignalAll();
149
150 PANDA_PUBLIC_API void Wait(Mutex *mutex);
151
152 PANDA_PUBLIC_API bool TimedWait(Mutex *mutex, uint64_t ms, uint64_t ns = 0, bool is_absolute = false);
153
154 private:
155 pthread_cond_t cond_;
156
157 NO_COPY_SEMANTIC(ConditionVariable);
158 NO_MOVE_SEMANTIC(ConditionVariable);
159 };
160 #endif // PANDA_USE_FUTEX
161
162 using PandaThreadKey = pthread_key_t;
163 const auto PandaGetspecific = pthread_getspecific; // NOLINT(readability-identifier-naming)
164 const auto PandaSetspecific = pthread_setspecific; // NOLINT(readability-identifier-naming)
165 const auto PandaThreadKeyCreate = pthread_key_create; // NOLINT(readability-identifier-naming)
166
167 template <class T, bool NEED_LOCK = true>
168 class SCOPED_CAPABILITY LockHolder {
169 public:
LockHolder(T & lock)170 explicit LockHolder(T &lock) ACQUIRE(lock) : lock_(lock)
171 {
172 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
173 lock_.Lock();
174 }
175 }
176
RELEASE()177 ~LockHolder() RELEASE()
178 {
179 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
180 lock_.Unlock();
181 }
182 }
183
184 private:
185 T &lock_;
186
187 NO_COPY_SEMANTIC(LockHolder);
188 NO_MOVE_SEMANTIC(LockHolder);
189 };
190
191 template <class T, bool NEED_LOCK = true>
192 class SCOPED_CAPABILITY ReadLockHolder {
193 public:
ReadLockHolder(T & lock)194 explicit ReadLockHolder(T &lock) ACQUIRE_SHARED(lock) : lock_(lock)
195 {
196 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
197 lock_.ReadLock();
198 }
199 }
200
RELEASE()201 ~ReadLockHolder() RELEASE()
202 {
203 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
204 lock_.Unlock();
205 }
206 }
207
208 private:
209 T &lock_;
210
211 NO_COPY_SEMANTIC(ReadLockHolder);
212 NO_MOVE_SEMANTIC(ReadLockHolder);
213 };
214
215 template <class T, bool NEED_LOCK = true>
216 class SCOPED_CAPABILITY WriteLockHolder {
217 public:
WriteLockHolder(T & lock)218 explicit WriteLockHolder(T &lock) ACQUIRE(lock) : lock_(lock)
219 {
220 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
221 lock_.WriteLock();
222 }
223 }
224
RELEASE()225 ~WriteLockHolder() RELEASE()
226 {
227 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
228 lock_.Unlock();
229 }
230 }
231
232 private:
233 T &lock_;
234
235 NO_COPY_SEMANTIC(WriteLockHolder);
236 NO_MOVE_SEMANTIC(WriteLockHolder);
237 };
238
239 enum class LockType : uint8_t {
240 LOCK,
241 READ_LOCK,
242 WRITE_LOCK,
243 };
244
245 namespace detail {
246
247 template <LockType TYPE, class L>
TryLockMutex(L & lock)248 inline bool TryLockMutex(L &lock) NO_THREAD_SAFETY_ANALYSIS
249 {
250 if constexpr (TYPE == LockType::LOCK) {
251 return lock.TryLock();
252 } else if constexpr (TYPE == LockType::READ_LOCK) {
253 return lock.TryReadLock();
254 } else if constexpr (TYPE == LockType::WRITE_LOCK) {
255 return lock.TryWriteLock();
256 }
257 return true;
258 }
259
260 template <LockType TYPE, class L>
TryLock(L & lock)261 inline int TryLock(L &lock)
262 {
263 if (TryLockMutex<TYPE>(lock)) {
264 return -1;
265 }
266 return 0;
267 }
268
269 template <LockType TYPE, class L0, class... L1>
TryLock(L0 & lock0,L1 &...rest)270 inline int TryLock(L0 &lock0, L1 &...rest) NO_THREAD_SAFETY_ANALYSIS
271 {
272 if (TryLockMutex<TYPE>(lock0)) {
273 int failedIndex = TryLock<TYPE>(rest...);
274 if (failedIndex == -1) {
275 return -1;
276 }
277 lock0.Unlock();
278 return failedIndex + 1;
279 }
280 return 0;
281 }
282
283 template <LockType TYPE, class L>
LockMutex(L & lock)284 inline void LockMutex(L &lock) NO_THREAD_SAFETY_ANALYSIS
285 {
286 if constexpr (TYPE == LockType::LOCK) {
287 lock.Lock();
288 } else if constexpr (TYPE == LockType::READ_LOCK) {
289 lock.ReadLock();
290 } else if constexpr (TYPE == LockType::WRITE_LOCK) {
291 lock.WriteLock();
292 }
293 }
294
295 } // namespace detail
296
297 template <LockType TYPE, class L0>
Lock(L0 & lock)298 inline void Lock(L0 &lock)
299 {
300 detail::LockMutex<TYPE>(lock);
301 }
302
303 template <LockType TYPE, class L0, class... L1>
Lock(L0 & lock0,L1 &...rest)304 void Lock(L0 &lock0, L1 &...rest) NO_THREAD_SAFETY_ANALYSIS
305 {
306 detail::LockMutex<TYPE>(lock0);
307 int failedIndex = detail::TryLock<TYPE>(rest...);
308 if (failedIndex == -1) {
309 return;
310 }
311
312 lock0.Unlock();
313 thread::Yield();
314 Lock<TYPE>(rest..., lock0);
315 }
316
317 /**
318 * @brief RAII handle for locking an arbitrary number of mutexes using a deadlock avoidance algorithm
319 * @warning: Does not support thread safety analysis
320 * @tparam locks - locks to be locked for the scope duration
321 * @tparam NEED_LOCK - specifies whether the locks are actually aquired
322 */
323 template <bool NEED_LOCK = true, class... T>
324 class ScopedLock {
325 public:
ScopedLock(T &...locks)326 explicit ScopedLock(T &...locks) : locks_(std::tie(locks...))
327 {
328 static_assert(sizeof...(T) > 0, "ScopedLock argument list can not be empty");
329
330 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
331 Lock<LockType::LOCK>(locks...);
332 }
333 }
334
~ScopedLock()335 ~ScopedLock()
336 {
337 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
338 auto unlock = [](auto &...lock) NO_THREAD_SAFETY_ANALYSIS { (lock.Unlock(), ...); };
339 std::apply(unlock, locks_);
340 }
341 }
342
343 private:
344 std::tuple<T &...> locks_;
345
346 NO_COPY_SEMANTIC(ScopedLock);
347 NO_MOVE_SEMANTIC(ScopedLock);
348 };
349
350 /**
351 * @brief RAII handle for read locking an arbitrary number of mutexes using a deadlock avoidance algorithm
352 * @warning: Does not support thread safety analysis
353 * @tparam locks - locks to be locked for the scope duration
354 * @tparam NEED_LOCK - specifies whether the locks are actually aquired
355 */
356 template <bool NEED_LOCK = true, class... T>
357 class ReadScopedLock {
358 public:
ReadScopedLock(T &...locks)359 explicit ReadScopedLock(T &...locks) : locks_(std::tie(locks...))
360 {
361 static_assert(sizeof...(T) > 0, "ReadScopedLock argument list can not be empty");
362
363 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
364 Lock<LockType::READ_LOCK>(locks...);
365 }
366 }
367
~ReadScopedLock()368 ~ReadScopedLock()
369 {
370 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
371 auto unlock = [](auto &...lock) NO_THREAD_SAFETY_ANALYSIS { (lock.Unlock(), ...); };
372 std::apply(unlock, locks_);
373 }
374 }
375
376 private:
377 std::tuple<T &...> locks_;
378
379 NO_COPY_SEMANTIC(ReadScopedLock);
380 NO_MOVE_SEMANTIC(ReadScopedLock);
381 };
382
383 /**
384 * @brief RAII handle for write locking an arbitrary number of mutexes using a deadlock avoidance algorithm
385 * @warning: Does not support thread safety analysis
386 * @tparam locks - locks to be locked for the scope duration
387 * @tparam NEED_LOCK - specifies whether the locks are actually aquired
388 */
389 template <bool NEED_LOCK = true, class... T>
390 class WriteScopedLock {
391 public:
WriteScopedLock(T &...locks)392 explicit WriteScopedLock(T &...locks) : locks_(std::tie(locks...))
393 {
394 static_assert(sizeof...(T) > 0, "WriteLockHolder argument list can not be empty");
395
396 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
397 Lock<LockType::WRITE_LOCK>(locks...);
398 }
399 }
400
~WriteScopedLock()401 ~WriteScopedLock()
402 {
403 if constexpr (NEED_LOCK) { // NOLINTNEXTLINE(readability-braces-around-statements)
404 auto unlock = [](auto &...lock) NO_THREAD_SAFETY_ANALYSIS { (lock.Unlock(), ...); };
405 std::apply(unlock, locks_);
406 }
407 }
408
409 private:
410 std::tuple<T &...> locks_;
411
412 NO_COPY_SEMANTIC(WriteScopedLock);
413 NO_MOVE_SEMANTIC(WriteScopedLock);
414 };
415
416 } // namespace ark::os::memory
417
418 #endif // PAND_LIBPANDABASE_PBASE_OS_MACROS_H_
419