1 /**
2 * Copyright (c) 2021-2022 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 PLATFORMS_UNIX_LIBPANDABASE_FUTEX_FMUTEX_H
17 #define PLATFORMS_UNIX_LIBPANDABASE_FUTEX_FMUTEX_H
18
19 #include <unistd.h>
20
21 #ifdef MC_ON
22 #include <cassert>
23 #include <climits>
24 #include <pthread.h>
25 #include <stdatomic.h>
26 #define THREAD_ID pthread_t
27 #define GET_CURRENT_THREAD pthread_self()
28 #define ATOMIC(type) _Atomic type
29 #define ATOMIC_INT atomic_int
30 #define ATOMIC_STORE(addr, val, mem) atomic_store_explicit(addr, val, mem)
31 #define ATOMIC_LOAD(addr, mem) atomic_load_explicit(addr, mem)
32 #define ATOMIC_FETCH_ADD(addr, val, mem) atomic_fetch_add_explicit(addr, val, mem)
33 #define ATOMIC_FETCH_SUB(addr, val, mem) atomic_fetch_sub_explicit(addr, val, mem)
34 #define ATOMIC_CAS_WEAK(addr, old_val, new_val, mem1, mem2) \
35 atomic_compare_exchange_weak_explicit(addr, &old_val, new_val, mem1, mem2)
36 #define ASSERT(a) assert(a)
37 #define LIKELY(a) a
38 #define UNLIKELY(a) a
39 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
40 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
41 #else
42 #include <limits>
43 #include <sys/param.h>
44 #include <atomic>
45 #include <os/thread.h>
46 #include <linux/futex.h>
47 #include <sys/syscall.h>
48 namespace panda::os::unix::memory::futex {
49 #define THREAD_ID thread::ThreadId // NOLINT(cppcoreguidelines-macro-usage)
50 #define GET_CURRENT_THREAD os::thread::GetCurrentThreadId() // NOLINT(cppcoreguidelines-macro-usage)
51 #define ATOMIC(type) std::atomic<type> // NOLINT(cppcoreguidelines-macro-usage)
52 #define ATOMIC_INT ATOMIC(int) // NOLINT(cppcoreguidelines-macro-usage)
53 #define ATOMIC_STORE(addr, val, mem) (addr)->store(val, std::mem) // NOLINT(cppcoreguidelines-macro-usage)
54 #define ATOMIC_LOAD(addr, mem) (addr)->load(std::mem) // NOLINT(cppcoreguidelines-macro-usage)
55 #define ATOMIC_FETCH_ADD(addr, val, mem) (addr)->fetch_add(val, std::mem) // NOLINT(cppcoreguidelines-macro-usage)
56 #define ATOMIC_FETCH_SUB(addr, val, mem) (addr)->fetch_sub(val, std::mem) // NOLINT(cppcoreguidelines-macro-usage)
57 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
58 #define ATOMIC_CAS_WEAK(addr, old_val, new_val, mem1, mem2) \
59 (addr)->compare_exchange_weak(old_val, new_val, std::mem1, std::mem2)
60 #endif
61
62 // Copy of mutex storage, after complete implementation should totally replace mutex::current_tid
63 extern thread_local THREAD_ID current_tid;
64
65 void MutexInit(struct fmutex *m);
66 void MutexDestroy(struct fmutex *m);
67 bool MutexLock(struct fmutex *m, bool trylock);
68 bool MutexTryLockWithSpinning(struct fmutex *m);
69 void MutexUnlock(struct fmutex *m);
70 void MutexLockForOther(struct fmutex *m, THREAD_ID thread);
71 void MutexUnlockForOther(struct fmutex *m, THREAD_ID thread);
72
73 #ifdef MC_ON
74 // GenMC does not support syscalls(futex)
75 // Models are defined in .c file to avoid code style warnings
76 #else
futex(volatile int * uaddr,int op,int val,const struct timespec * timeout,volatile int * uaddr2,int val3)77 inline int futex(volatile int *uaddr, int op, int val, const struct timespec *timeout, volatile int *uaddr2, int val3)
78 {
79 // NOLINTNEXTLINE
80 return syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
81 }
82 #endif
83
84 static constexpr int WAKE_ONE = 1;
85 static constexpr int WAKE_ALL = INT_MAX;
86 static constexpr int32_t HELD_MASK = 1;
87 static constexpr int32_t WAITER_SHIFT = 1;
88 // NOLINTNEXTLINE(hicpp-signed-bitwise, cppcoreguidelines-narrowing-conversions, bugprone-narrowing-conversions)
89 static constexpr int32_t WAITER_INCREMENT = 1 << WAITER_SHIFT;
90
91 struct fmutex {
92 // Lowest bit: 0 - unlocked, 1 - locked.
93 // Other bits: Number of waiters.
94 // Unified lock state and waiters count to avoid requirement of double seq_cst memory order on mutex unlock
95 // as it's done in RWLock::WriteUnlock
96 ATOMIC_INT state_and_waiters_;
97 ATOMIC(THREAD_ID) exclusive_owner_;
98 int recursiveCount;
99 bool recursive_mutex_;
100 };
101
102 int *GetStateAddr(struct fmutex *const m);
103 void IncrementWaiters(struct fmutex *m);
104 void DecrementWaiters(struct fmutex *m);
105 int32_t GetWaiters(struct fmutex *const m);
106 bool IsHeld(struct fmutex *const m, THREAD_ID thread);
107 bool MutexDoNotCheckOnDeadlock();
108 void MutexIgnoreChecksOnDeadlock();
109
110 #ifdef MC_ON
111 #else
112 } // namespace panda::os::unix::memory::futex
113 #endif
114
115 #endif // PLATFORMS_UNIX_LIBPANDABASE_FUTEX_FMUTEX_H
116