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