Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 1 | #if !AOS_DEBUG |
Austin Schuh | 7a41be6 | 2015-10-31 13:06:55 -0700 | [diff] [blame] | 2 | #undef NDEBUG |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 3 | #define NDEBUG |
| 4 | #endif |
| 5 | |
John Park | 398c74a | 2018-10-20 21:17:39 -0700 | [diff] [blame] | 6 | #include "aos/ipc_lib/aos_sync.h" |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 7 | |
| 8 | #include <linux/futex.h> |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 9 | #include <pthread.h> |
| 10 | #include <sched.h> |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 11 | #include <sys/syscall.h> |
| 12 | #include <sys/types.h> |
| 13 | #include <unistd.h> |
| 14 | |
| 15 | #include <cassert> |
| 16 | #include <cerrno> |
| 17 | #include <cinttypes> |
| 18 | #include <climits> |
| 19 | #include <cstddef> |
| 20 | #include <cstdint> |
| 21 | #include <cstring> |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 22 | |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 23 | #include "aos/ipc_lib/shm_observers.h" |
| 24 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 25 | #ifdef AOS_SANITIZER_thread |
| 26 | #include <sanitizer/tsan_interface_atomic.h> |
| 27 | #endif |
| 28 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 29 | #include <algorithm> |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 30 | #include <type_traits> |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 31 | |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 32 | #include "absl/base/call_once.h" |
Philipp Schrader | 790cb54 | 2023-07-05 21:06:52 -0700 | [diff] [blame] | 33 | #include "glog/logging.h" |
| 34 | |
Brian Silverman | b47f555 | 2020-10-01 15:08:14 -0700 | [diff] [blame] | 35 | #include "aos/macros.h" |
| 36 | #include "aos/thread_local.h" |
| 37 | #include "aos/util/compiler_memory_barrier.h" |
| 38 | |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 39 | using ::aos::linux_code::ipc_lib::RunShmObservers; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 40 | |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 41 | // This code was originally based on |
| 42 | // <https://www.akkadia.org/drepper/futex.pdf>, but is has since evolved a lot. |
| 43 | // However, that still has useful information. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 44 | // |
| 45 | // Finding information about actually using futexes is really REALLY hard, so |
| 46 | // here's a list of the stuff that I've used: |
| 47 | // futex(7) has a really high-level overview. |
| 48 | // <http://locklessinc.com/articles/futex_cheat_sheet/> describes some of the |
| 49 | // operations in a bit more detail than most places. |
| 50 | // <http://locklessinc.com/articles/mutex_cv_futex/> is the basis of our |
| 51 | // implementations (before PI). |
| 52 | // <http://lwn.net/Articles/360699/> has a nice overview of futexes in late 2009 |
| 53 | // (fairly recent compared to everything else...). |
| 54 | // <https://www.kernel.org/doc/Documentation/pi-futex.txt>, |
| 55 | // <https://www.kernel.org/doc/Documentation/futex-requeue-pi.txt>, |
| 56 | // <https://www.kernel.org/doc/Documentation/robust-futexes.txt>, |
| 57 | // and <https://www.kernel.org/doc/Documentation/robust-futex-ABI.txt> are all |
| 58 | // useful references. |
| 59 | // The kernel source (kernel/futex.c) has some useful comments about what the |
| 60 | // various operations do (except figuring out which argument goes where in the |
| 61 | // syscall is still confusing). |
| 62 | // futex(2) is basically useless except for describing the order of the |
| 63 | // arguments (it only has high-level descriptions of what some of the |
| 64 | // operations do, and some of them are wrong in Wheezy). |
| 65 | // glibc's nptl pthreads implementation is the intended user of most of these |
| 66 | // things, so it is also a good place to look for examples. However, it is all |
| 67 | // very hard to read because it supports ~20 different kinds of mutexes and |
| 68 | // several variations of condition variables, and some of the pieces of code |
| 69 | // are only written in assembly. |
| 70 | // set_robust_list(2) is wrong in Wheezy (it doesn't actually take a TID |
| 71 | // argument). |
| 72 | // |
| 73 | // Can't use PRIVATE futex operations because they use the pid (or something) as |
| 74 | // part of the hash. |
| 75 | // |
| 76 | // ThreadSanitizer understands how these mutexes etc work. It appears to be able |
| 77 | // to figure out the happens-before relationship from the __ATOMIC_SEQ_CST |
| 78 | // atomic primitives. |
| 79 | // |
| 80 | // Remember that EAGAIN and EWOUDBLOCK are the same! (ie if you get EAGAIN from |
| 81 | // FUTEX_WAIT, the docs call it EWOULDBLOCK...) |
| 82 | |
| 83 | // Values for an aos_mutex.futex (kernel-mandated): |
| 84 | // 0 = unlocked |
| 85 | // TID = locked, not contended |
| 86 | // |FUTEX_WAITERS = there are waiters (aka contended) |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 87 | // |FUTEX_OWNER_DIED = old owner died |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 88 | // |
| 89 | // Values for an aos_futex being used directly: |
| 90 | // 0 = unset |
| 91 | // 1 = set |
| 92 | // |
| 93 | // The value of an aos_condition is just a generation counter. |
| 94 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 95 | #ifdef AOS_SANITIZER_thread |
| 96 | extern "C" void AnnotateHappensBefore(const char *file, int line, |
| 97 | uintptr_t addr); |
| 98 | extern "C" void AnnotateHappensAfter(const char *file, int line, |
| 99 | uintptr_t addr); |
| 100 | #define ANNOTATE_HAPPENS_BEFORE(address) \ |
| 101 | AnnotateHappensBefore(__FILE__, __LINE__, \ |
| 102 | reinterpret_cast<uintptr_t>(address)) |
| 103 | #define ANNOTATE_HAPPENS_AFTER(address) \ |
| 104 | AnnotateHappensAfter(__FILE__, __LINE__, reinterpret_cast<uintptr_t>(address)) |
| 105 | #else |
| 106 | #define ANNOTATE_HAPPENS_BEFORE(address) |
| 107 | #define ANNOTATE_HAPPENS_AFTER(address) |
| 108 | #endif |
| 109 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 110 | namespace { |
| 111 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 112 | const bool kRobustListDebug = false; |
| 113 | const bool kLockDebug = false; |
| 114 | const bool kPrintOperations = false; |
| 115 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 116 | // These sys_futex_* functions are wrappers around syscall(SYS_futex). They each |
| 117 | // take a specific set of arguments for a given futex operation. They return the |
| 118 | // result or a negated errno value. -1..-4095 mean errors and not successful |
| 119 | // results, which is guaranteed by the kernel. |
| 120 | // |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 121 | // They each have optimized versions for some architectures which don't go |
| 122 | // through syscall(2) or errno. These use register variables to get the values |
| 123 | // in the right registers to actually make the syscall. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 124 | |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 125 | // The actual macros that we key off of to use the inline versions or not. |
Brian Silverman | 17426d9 | 2018-08-09 11:38:49 -0700 | [diff] [blame] | 126 | #if defined(__ARM_EABI__) |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 127 | // The syscall interface is different for non-EABI ARM, so we test specifically |
| 128 | // for EABI. |
Brian Silverman | 17426d9 | 2018-08-09 11:38:49 -0700 | [diff] [blame] | 129 | #define ARM_EABI_INLINE_SYSCALL 1 |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 130 | #define AARCH64_INLINE_SYSCALL 0 |
| 131 | #elif defined(__aarch64__) |
| 132 | // Linux only has one supported syscall ABI on aarch64, which is the one we |
| 133 | // support. |
| 134 | #define ARM_EABI_INLINE_SYSCALL 0 |
| 135 | #define AARCH64_INLINE_SYSCALL 1 |
Brian Silverman | 17426d9 | 2018-08-09 11:38:49 -0700 | [diff] [blame] | 136 | #else |
| 137 | #define ARM_EABI_INLINE_SYSCALL 0 |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 138 | #define AARCH64_INLINE_SYSCALL 0 |
Brian Silverman | 17426d9 | 2018-08-09 11:38:49 -0700 | [diff] [blame] | 139 | #endif |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 140 | |
| 141 | // Used for FUTEX_WAIT, FUTEX_LOCK_PI, and FUTEX_TRYLOCK_PI. |
| 142 | inline int sys_futex_wait(int op, aos_futex *addr1, int val1, |
| 143 | const struct timespec *timeout) { |
| 144 | #if ARM_EABI_INLINE_SYSCALL |
| 145 | register aos_futex *addr1_reg __asm__("r0") = addr1; |
| 146 | register int op_reg __asm__("r1") = op; |
| 147 | register int val1_reg __asm__("r2") = val1; |
| 148 | register const struct timespec *timeout_reg __asm__("r3") = timeout; |
| 149 | register int syscall_number __asm__("r7") = SYS_futex; |
| 150 | register int result __asm__("r0"); |
| 151 | __asm__ volatile("swi #0" |
| 152 | : "=r"(result) |
| 153 | : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg), |
| 154 | "r"(timeout_reg), "r"(syscall_number) |
| 155 | : "memory"); |
| 156 | return result; |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 157 | #elif AARCH64_INLINE_SYSCALL |
| 158 | register aos_futex *addr1_reg __asm__("x0") = addr1; |
| 159 | register int op_reg __asm__("x1") = op; |
| 160 | register int val1_reg __asm__("x2") = val1; |
| 161 | register const struct timespec *timeout_reg __asm__("x3") = timeout; |
| 162 | register int syscall_number __asm__("x8") = SYS_futex; |
| 163 | register int result __asm__("x0"); |
| 164 | __asm__ volatile("svc #0" |
| 165 | : "=r"(result) |
| 166 | : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg), |
| 167 | "r"(timeout_reg), "r"(syscall_number) |
| 168 | : "memory"); |
| 169 | return result; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 170 | #else |
| 171 | const int r = syscall(SYS_futex, addr1, op, val1, timeout); |
| 172 | if (r == -1) return -errno; |
| 173 | return r; |
| 174 | #endif |
| 175 | } |
| 176 | |
| 177 | inline int sys_futex_wake(aos_futex *addr1, int val1) { |
| 178 | #if ARM_EABI_INLINE_SYSCALL |
| 179 | register aos_futex *addr1_reg __asm__("r0") = addr1; |
| 180 | register int op_reg __asm__("r1") = FUTEX_WAKE; |
| 181 | register int val1_reg __asm__("r2") = val1; |
| 182 | register int syscall_number __asm__("r7") = SYS_futex; |
| 183 | register int result __asm__("r0"); |
| 184 | __asm__ volatile("swi #0" |
| 185 | : "=r"(result) |
| 186 | : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg), |
| 187 | "r"(syscall_number) |
| 188 | : "memory"); |
| 189 | return result; |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 190 | #elif AARCH64_INLINE_SYSCALL |
| 191 | register aos_futex *addr1_reg __asm__("x0") = addr1; |
| 192 | register int op_reg __asm__("x1") = FUTEX_WAKE; |
| 193 | register int val1_reg __asm__("x2") = val1; |
| 194 | register int syscall_number __asm__("x8") = SYS_futex; |
| 195 | register int result __asm__("x0"); |
| 196 | __asm__ volatile("svc #0" |
| 197 | : "=r"(result) |
| 198 | : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg), |
| 199 | "r"(syscall_number) |
| 200 | : "memory"); |
| 201 | return result; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 202 | #else |
| 203 | const int r = syscall(SYS_futex, addr1, FUTEX_WAKE, val1); |
| 204 | if (r == -1) return -errno; |
| 205 | return r; |
| 206 | #endif |
| 207 | } |
| 208 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 209 | inline int sys_futex_cmp_requeue_pi(aos_futex *addr1, int num_wake, |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 210 | int num_requeue, aos_futex *m, |
| 211 | uint32_t val) { |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 212 | #if ARM_EABI_INLINE_SYSCALL |
| 213 | register aos_futex *addr1_reg __asm__("r0") = addr1; |
| 214 | register int op_reg __asm__("r1") = FUTEX_CMP_REQUEUE_PI; |
| 215 | register int num_wake_reg __asm__("r2") = num_wake; |
| 216 | register int num_requeue_reg __asm__("r3") = num_requeue; |
| 217 | register aos_futex *m_reg __asm__("r4") = m; |
| 218 | register uint32_t val_reg __asm__("r5") = val; |
| 219 | register int syscall_number __asm__("r7") = SYS_futex; |
| 220 | register int result __asm__("r0"); |
| 221 | __asm__ volatile("swi #0" |
| 222 | : "=r"(result) |
| 223 | : "r"(addr1_reg), "r"(op_reg), "r"(num_wake_reg), |
| 224 | "r"(num_requeue_reg), "r"(m_reg), "r"(val_reg), |
| 225 | "r"(syscall_number) |
| 226 | : "memory"); |
| 227 | return result; |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 228 | #elif AARCH64_INLINE_SYSCALL |
| 229 | register aos_futex *addr1_reg __asm__("x0") = addr1; |
| 230 | register int op_reg __asm__("x1") = FUTEX_CMP_REQUEUE_PI; |
| 231 | register int num_wake_reg __asm__("x2") = num_wake; |
| 232 | register int num_requeue_reg __asm__("x3") = num_requeue; |
| 233 | register aos_futex *m_reg __asm__("x4") = m; |
| 234 | register uint32_t val_reg __asm__("x5") = val; |
| 235 | register int syscall_number __asm__("x8") = SYS_futex; |
| 236 | register int result __asm__("x0"); |
| 237 | __asm__ volatile("svc #0" |
| 238 | : "=r"(result) |
| 239 | : "r"(addr1_reg), "r"(op_reg), "r"(num_wake_reg), |
| 240 | "r"(num_requeue_reg), "r"(m_reg), "r"(val_reg), |
| 241 | "r"(syscall_number) |
| 242 | : "memory"); |
| 243 | return result; |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 244 | #else |
| 245 | const int r = syscall(SYS_futex, addr1, FUTEX_CMP_REQUEUE_PI, num_wake, |
| 246 | num_requeue, m, val); |
| 247 | if (r == -1) return -errno; |
| 248 | return r; |
| 249 | #endif |
| 250 | } |
| 251 | |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 252 | inline int sys_futex_wait_requeue_pi(aos_condition *addr1, uint32_t start_val, |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 253 | const struct timespec *timeout, |
| 254 | aos_futex *m) { |
| 255 | #if ARM_EABI_INLINE_SYSCALL |
| 256 | register aos_condition *addr1_reg __asm__("r0") = addr1; |
| 257 | register int op_reg __asm__("r1") = FUTEX_WAIT_REQUEUE_PI; |
| 258 | register uint32_t start_val_reg __asm__("r2") = start_val; |
| 259 | register const struct timespec *timeout_reg __asm__("r3") = timeout; |
| 260 | register aos_futex *m_reg __asm__("r4") = m; |
| 261 | register int syscall_number __asm__("r7") = SYS_futex; |
| 262 | register int result __asm__("r0"); |
| 263 | __asm__ volatile("swi #0" |
| 264 | : "=r"(result) |
| 265 | : "r"(addr1_reg), "r"(op_reg), "r"(start_val_reg), |
| 266 | "r"(timeout_reg), "r"(m_reg), "r"(syscall_number) |
| 267 | : "memory"); |
| 268 | return result; |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 269 | #elif AARCH64_INLINE_SYSCALL |
| 270 | register aos_condition *addr1_reg __asm__("x0") = addr1; |
| 271 | register int op_reg __asm__("x1") = FUTEX_WAIT_REQUEUE_PI; |
| 272 | register uint32_t start_val_reg __asm__("x2") = start_val; |
| 273 | register const struct timespec *timeout_reg __asm__("x3") = timeout; |
| 274 | register aos_futex *m_reg __asm__("x4") = m; |
| 275 | register int syscall_number __asm__("x8") = SYS_futex; |
| 276 | register int result __asm__("x0"); |
| 277 | __asm__ volatile("svc #0" |
| 278 | : "=r"(result) |
| 279 | : "r"(addr1_reg), "r"(op_reg), "r"(start_val_reg), |
| 280 | "r"(timeout_reg), "r"(m_reg), "r"(syscall_number) |
| 281 | : "memory"); |
| 282 | return result; |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 283 | #else |
| 284 | const int r = |
| 285 | syscall(SYS_futex, addr1, FUTEX_WAIT_REQUEUE_PI, start_val, timeout, m); |
| 286 | if (r == -1) return -errno; |
| 287 | return r; |
| 288 | #endif |
| 289 | } |
| 290 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 291 | inline int sys_futex_unlock_pi(aos_futex *addr1) { |
| 292 | #if ARM_EABI_INLINE_SYSCALL |
| 293 | register aos_futex *addr1_reg __asm__("r0") = addr1; |
| 294 | register int op_reg __asm__("r1") = FUTEX_UNLOCK_PI; |
| 295 | register int syscall_number __asm__("r7") = SYS_futex; |
| 296 | register int result __asm__("r0"); |
| 297 | __asm__ volatile("swi #0" |
| 298 | : "=r"(result) |
| 299 | : "r"(addr1_reg), "r"(op_reg), "r"(syscall_number) |
| 300 | : "memory"); |
| 301 | return result; |
Brian Silverman | 0b58a61 | 2021-09-13 15:32:29 -0700 | [diff] [blame] | 302 | #elif AARCH64_INLINE_SYSCALL |
| 303 | register aos_futex *addr1_reg __asm__("x0") = addr1; |
| 304 | register int op_reg __asm__("x1") = FUTEX_UNLOCK_PI; |
| 305 | register int syscall_number __asm__("x8") = SYS_futex; |
| 306 | register int result __asm__("x0"); |
| 307 | __asm__ volatile("svc #0" |
| 308 | : "=r"(result) |
| 309 | : "r"(addr1_reg), "r"(op_reg), "r"(syscall_number) |
| 310 | : "memory"); |
| 311 | return result; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 312 | #else |
| 313 | const int r = syscall(SYS_futex, addr1, FUTEX_UNLOCK_PI); |
| 314 | if (r == -1) return -errno; |
| 315 | return r; |
| 316 | #endif |
| 317 | } |
| 318 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 319 | // Returns the previous value of f. |
| 320 | inline uint32_t compare_and_swap_val(aos_futex *f, uint32_t before, |
| 321 | uint32_t after) { |
| 322 | #ifdef AOS_SANITIZER_thread |
| 323 | // This is a workaround for <https://llvm.org/bugs/show_bug.cgi?id=23176>. |
| 324 | // Basically, most of the atomic operations are broken under tsan, but this |
| 325 | // particular one isn't. |
| 326 | // TODO(Brian): Remove this #ifdef (and the one in compare_and_swap) once we |
| 327 | // don't have to worry about tsan with this bug any more. |
| 328 | uint32_t before_value = before; |
| 329 | __tsan_atomic32_compare_exchange_strong( |
| 330 | reinterpret_cast<int32_t *>(f), |
| 331 | reinterpret_cast<int32_t *>(&before_value), after, |
| 332 | __tsan_memory_order_seq_cst, __tsan_memory_order_seq_cst); |
| 333 | return before_value; |
| 334 | #else |
| 335 | return __sync_val_compare_and_swap(f, before, after); |
| 336 | #endif |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 337 | } |
| 338 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 339 | // Returns true if it succeeds and false if it fails. |
| 340 | inline bool compare_and_swap(aos_futex *f, uint32_t before, uint32_t after) { |
| 341 | #ifdef AOS_SANITIZER_thread |
| 342 | return compare_and_swap_val(f, before, after) == before; |
| 343 | #else |
| 344 | return __sync_bool_compare_and_swap(f, before, after); |
| 345 | #endif |
| 346 | } |
| 347 | |
| 348 | #ifdef AOS_SANITIZER_thread |
| 349 | |
| 350 | // Simple macro for checking something which should always be true. |
| 351 | // Using the standard CHECK macro isn't safe because failures often result in |
| 352 | // reentering the mutex locking code, which doesn't work. |
| 353 | #define SIMPLE_CHECK(expr) \ |
| 354 | do { \ |
| 355 | if (!(expr)) { \ |
| 356 | fprintf(stderr, "%s: %d: SIMPLE_CHECK(" #expr ") failed!\n", __FILE__, \ |
| 357 | __LINE__); \ |
| 358 | abort(); \ |
| 359 | } \ |
| 360 | } while (false) |
| 361 | |
| 362 | // Forcibly initializes the pthread mutex for *m. |
| 363 | // This sequence of operations is only safe for the simpler kinds of mutexes in |
| 364 | // glibc's pthreads implementation on Linux. |
| 365 | void init_pthread_mutex(aos_mutex *m) { |
| 366 | // Re-initialize the mutex so the destroy won't fail if it's locked. |
| 367 | // tsan ignores this. |
| 368 | SIMPLE_CHECK(0 == pthread_mutex_init(&m->pthread_mutex, nullptr)); |
| 369 | // Destroy the mutex so tsan will forget about it if some now-dead thread |
| 370 | // locked it. |
| 371 | SIMPLE_CHECK(0 == pthread_mutex_destroy(&m->pthread_mutex)); |
| 372 | |
| 373 | // Now actually initialize it, making sure it's process-shareable so it works |
| 374 | // correctly across shared memory. |
| 375 | pthread_mutexattr_t attr; |
| 376 | SIMPLE_CHECK(0 == pthread_mutexattr_init(&attr)); |
| 377 | SIMPLE_CHECK(0 == pthread_mutexattr_setpshared(&attr, true)); |
| 378 | SIMPLE_CHECK(0 == pthread_mutex_init(&m->pthread_mutex, &attr)); |
| 379 | SIMPLE_CHECK(0 == pthread_mutexattr_destroy(&attr)); |
| 380 | } |
| 381 | |
| 382 | // Locks the pthread mutex for *m. |
| 383 | // If a stack trace ever reveals the pthread_mutex_lock call in here blocking, |
| 384 | // there is a bug in our mutex code or the way somebody is calling it. |
| 385 | void lock_pthread_mutex(aos_mutex *m) { |
| 386 | if (!m->pthread_mutex_init) { |
| 387 | init_pthread_mutex(m); |
| 388 | m->pthread_mutex_init = true; |
| 389 | } |
| 390 | SIMPLE_CHECK(0 == pthread_mutex_lock(&m->pthread_mutex)); |
| 391 | } |
| 392 | |
| 393 | // Forcibly locks the pthread mutex for *m. |
| 394 | // This will (somewhat hackily) rip the lock out from underneath somebody else |
| 395 | // who is already holding it. |
| 396 | void force_lock_pthread_mutex(aos_mutex *m) { |
| 397 | if (!m->pthread_mutex_init) { |
| 398 | init_pthread_mutex(m); |
| 399 | m->pthread_mutex_init = true; |
| 400 | } |
| 401 | const int trylock_result = pthread_mutex_trylock(&m->pthread_mutex); |
| 402 | SIMPLE_CHECK(trylock_result == 0 || trylock_result == EBUSY); |
| 403 | if (trylock_result == 0) { |
| 404 | // We're good, so unlock it and then go for a real lock down below. |
| 405 | SIMPLE_CHECK(0 == pthread_mutex_unlock(&m->pthread_mutex)); |
| 406 | } else { |
| 407 | // Somebody (should always be somebody else who died with it held) already |
| 408 | // has it, so make tsan forget about that. |
| 409 | init_pthread_mutex(m); |
| 410 | } |
| 411 | lock_pthread_mutex(m); |
| 412 | } |
| 413 | |
| 414 | // Unlocks the pthread mutex for *m. |
| 415 | void unlock_pthread_mutex(aos_mutex *m) { |
| 416 | assert(m->pthread_mutex_init); |
| 417 | SIMPLE_CHECK(0 == pthread_mutex_unlock(&m->pthread_mutex)); |
| 418 | } |
| 419 | |
| 420 | #else |
| 421 | |
| 422 | // Empty implementations of all these so the code below doesn't need #ifdefs. |
| 423 | static inline void lock_pthread_mutex(aos_mutex *) {} |
| 424 | static inline void force_lock_pthread_mutex(aos_mutex *) {} |
| 425 | static inline void unlock_pthread_mutex(aos_mutex *) {} |
| 426 | |
| 427 | #endif |
| 428 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 429 | pid_t do_get_tid() { |
| 430 | pid_t r = syscall(SYS_gettid); |
| 431 | assert(r > 0); |
| 432 | return r; |
| 433 | } |
| 434 | |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 435 | // This gets called by functions before LOG(FATAL)ing with error messages |
Austin Schuh | f257f3c | 2019-10-27 21:00:43 -0700 | [diff] [blame] | 436 | // that would be incorrect if the error was caused by a process forking without |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 437 | // initialize_in_new_thread getting called in the fork. |
| 438 | void check_cached_tid(pid_t tid) { |
| 439 | pid_t actual = do_get_tid(); |
| 440 | if (tid != actual) { |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 441 | LOG(FATAL) << "task " << static_cast<intmax_t>(tid) << " forked into " |
| 442 | << static_cast<intmax_t>(actual) |
| 443 | << " without letting aos_sync know so we're not really sure " |
| 444 | "what's going on"; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 445 | } |
| 446 | } |
| 447 | |
| 448 | // Starts off at 0 in each new thread (because that's what it gets initialized |
| 449 | // to in most of them or it gets to reset to 0 after a fork by atfork_child()). |
Brian Silverman | b47f555 | 2020-10-01 15:08:14 -0700 | [diff] [blame] | 450 | AOS_THREAD_LOCAL pid_t my_tid = 0; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 451 | |
| 452 | // Gets called before the fork(2) wrapper function returns in the child. |
| 453 | void atfork_child() { |
| 454 | // The next time get_tid() is called, it will set everything up again. |
| 455 | my_tid = 0; |
| 456 | } |
| 457 | |
John Park | 0e69950 | 2019-11-20 19:36:05 -0800 | [diff] [blame] | 458 | void InstallAtforkHook() { |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 459 | PCHECK(pthread_atfork(NULL, NULL, &atfork_child) == 0) |
| 460 | << ": pthread_atfork(NULL, NULL, " |
| 461 | << reinterpret_cast<void *>(&atfork_child) << ") failed"; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 462 | } |
| 463 | |
| 464 | // This gets called to set everything up in a new thread by get_tid(). |
| 465 | void initialize_in_new_thread(); |
| 466 | |
| 467 | // Gets the current thread's TID and does all of the 1-time initialization the |
| 468 | // first time it's called in a given thread. |
| 469 | inline uint32_t get_tid() { |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 470 | if (__builtin_expect(my_tid == 0, false)) { |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 471 | initialize_in_new_thread(); |
| 472 | } |
| 473 | static_assert(sizeof(my_tid) <= sizeof(uint32_t), "pid_t is too big"); |
| 474 | return static_cast<uint32_t>(my_tid); |
| 475 | } |
| 476 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 477 | // Contains all of the stuff for dealing with the robust list. Nothing outside |
| 478 | // this namespace should touch anything inside it except Init, Adder, and |
| 479 | // Remover. |
| 480 | namespace my_robust_list { |
| 481 | |
| 482 | static_assert(offsetof(aos_mutex, next) == 0, |
| 483 | "Our math all assumes that the beginning of a mutex and its next " |
| 484 | "pointer are at the same place in memory."); |
| 485 | |
| 486 | // Our version of robust_list_head. |
| 487 | // This is copied from the kernel header because that's a pretty stable ABI (and |
| 488 | // any changes will be backwards compatible anyways) and we want ours to have |
| 489 | // different types. |
| 490 | // The uintptr_ts are &next of the elements in the list (with stuff |ed in). |
| 491 | struct aos_robust_list_head { |
| 492 | uintptr_t next; |
| 493 | long futex_offset; |
| 494 | uintptr_t pending_next; |
| 495 | }; |
| 496 | |
| 497 | static_assert(offsetof(aos_robust_list_head, next) == |
| 498 | offsetof(robust_list_head, list), |
| 499 | "Our aos_robust_list_head doesn't match the kernel's"); |
| 500 | static_assert(offsetof(aos_robust_list_head, futex_offset) == |
| 501 | offsetof(robust_list_head, futex_offset), |
| 502 | "Our aos_robust_list_head doesn't match the kernel's"); |
| 503 | static_assert(offsetof(aos_robust_list_head, pending_next) == |
| 504 | offsetof(robust_list_head, list_op_pending), |
| 505 | "Our aos_robust_list_head doesn't match the kernel's"); |
| 506 | static_assert(sizeof(aos_robust_list_head) == sizeof(robust_list_head), |
| 507 | "Our aos_robust_list_head doesn't match the kernel's"); |
| 508 | |
Brian Silverman | b47f555 | 2020-10-01 15:08:14 -0700 | [diff] [blame] | 509 | AOS_THREAD_LOCAL aos_robust_list_head robust_head; |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 510 | |
| 511 | // Extra offset between mutex values and where we point to for their robust list |
| 512 | // entries (from SetRobustListOffset). |
| 513 | uintptr_t robust_list_offset = 0; |
| 514 | |
| 515 | // The value to OR each pointer's value with whenever putting it into the robust |
| 516 | // list (technically only if it's PI, but all of ours are, so...). |
| 517 | static const uintptr_t kRobustListOr = 1; |
| 518 | |
| 519 | // Returns the value which goes into a next variable to represent the head. |
| 520 | inline uintptr_t robust_head_next_value() { |
| 521 | return reinterpret_cast<uintptr_t>(&robust_head.next); |
| 522 | } |
| 523 | // Returns true iff next represents the head. |
| 524 | inline bool next_is_head(uintptr_t next) { |
| 525 | return next == robust_head_next_value(); |
| 526 | } |
| 527 | // Returns the (psuedo-)mutex corresponding to the head. |
| 528 | // This does NOT have a previous pointer, so be careful with the return value. |
| 529 | inline aos_mutex *robust_head_mutex() { |
| 530 | return reinterpret_cast<aos_mutex *>(robust_head_next_value()); |
| 531 | } |
| 532 | |
| 533 | inline uintptr_t mutex_to_next(aos_mutex *m) { |
| 534 | return (reinterpret_cast<uintptr_t>(&m->next) + robust_list_offset) | |
| 535 | kRobustListOr; |
| 536 | } |
| 537 | inline aos_mutex *next_to_mutex(uintptr_t next) { |
| 538 | if (__builtin_expect(robust_list_offset != 0, false) && next_is_head(next)) { |
| 539 | // We don't offset the head pointer, so be careful. |
| 540 | return reinterpret_cast<aos_mutex *>(next); |
| 541 | } |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 542 | return reinterpret_cast<aos_mutex *>((next & ~kRobustListOr) - |
| 543 | robust_list_offset); |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 544 | } |
| 545 | |
| 546 | // Sets up the robust list for each thread. |
| 547 | void Init() { |
| 548 | // It starts out just pointing back to itself. |
| 549 | robust_head.next = robust_head_next_value(); |
| 550 | robust_head.futex_offset = static_cast<ssize_t>(offsetof(aos_mutex, futex)) - |
| 551 | static_cast<ssize_t>(offsetof(aos_mutex, next)); |
| 552 | robust_head.pending_next = 0; |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 553 | PCHECK(syscall(SYS_set_robust_list, robust_head_next_value(), |
| 554 | sizeof(robust_head)) == 0) |
| 555 | << ": set_robust_list(" << reinterpret_cast<void *>(robust_head.next) |
| 556 | << ", " << sizeof(robust_head) << ") failed"; |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 557 | if (kRobustListDebug) { |
| 558 | printf("%" PRId32 ": init done\n", get_tid()); |
| 559 | } |
| 560 | } |
| 561 | |
| 562 | // Updating the offset with locked mutexes is important during robustness |
| 563 | // testing, because there are mutexes which are locked before this is set to a |
| 564 | // non-0 value and then unlocked after it is changed back. However, to make sure |
| 565 | // the code works correctly when manipulating the next pointer of the last of |
| 566 | // those mutexes, all of their next values have to be adjusted appropriately. |
| 567 | void SetRobustListOffset(uintptr_t offset) { |
| 568 | const uintptr_t offset_change = offset - robust_list_offset; |
| 569 | robust_list_offset = offset; |
| 570 | aos_mutex *m = robust_head_mutex(); |
| 571 | // Update the offset contained in each of the mutexes which is already locked. |
| 572 | while (!next_is_head(m->next)) { |
| 573 | m->next += offset_change; |
| 574 | m = next_to_mutex(m->next); |
| 575 | } |
| 576 | } |
| 577 | |
| 578 | bool HaveLockedMutexes() { |
| 579 | return robust_head.next != robust_head_next_value(); |
| 580 | } |
| 581 | |
| 582 | // Handles adding a mutex to the robust list. |
| 583 | // The idea is to create one of these at the beginning of a function that needs |
| 584 | // to do this and then call Add() iff it should actually be added. |
| 585 | class Adder { |
| 586 | public: |
| 587 | Adder(aos_mutex *m) : m_(m) { |
| 588 | assert(robust_head.pending_next == 0); |
| 589 | if (kRobustListDebug) { |
| 590 | printf("%" PRId32 ": maybe add %p\n", get_tid(), m_); |
| 591 | } |
| 592 | robust_head.pending_next = mutex_to_next(m); |
| 593 | aos_compiler_memory_barrier(); |
| 594 | } |
| 595 | ~Adder() { |
| 596 | assert(robust_head.pending_next == mutex_to_next(m_)); |
| 597 | if (kRobustListDebug) { |
| 598 | printf("%" PRId32 ": done maybe add %p, n=%p p=%p\n", get_tid(), m_, |
| 599 | next_to_mutex(m_->next), m_->previous); |
| 600 | } |
| 601 | aos_compiler_memory_barrier(); |
| 602 | robust_head.pending_next = 0; |
| 603 | } |
| 604 | |
| 605 | void Add() { |
| 606 | assert(robust_head.pending_next == mutex_to_next(m_)); |
| 607 | if (kRobustListDebug) { |
| 608 | printf("%" PRId32 ": adding %p\n", get_tid(), m_); |
| 609 | } |
| 610 | const uintptr_t old_head_next_value = robust_head.next; |
| 611 | |
| 612 | m_->next = old_head_next_value; |
| 613 | aos_compiler_memory_barrier(); |
| 614 | robust_head.next = mutex_to_next(m_); |
| 615 | |
| 616 | m_->previous = robust_head_mutex(); |
| 617 | if (!next_is_head(old_head_next_value)) { |
| 618 | // robust_head's psuedo-mutex doesn't have a previous pointer to update. |
| 619 | next_to_mutex(old_head_next_value)->previous = m_; |
| 620 | } |
| 621 | aos_compiler_memory_barrier(); |
| 622 | if (kRobustListDebug) { |
| 623 | printf("%" PRId32 ": done adding %p\n", get_tid(), m_); |
| 624 | } |
| 625 | } |
| 626 | |
| 627 | private: |
| 628 | aos_mutex *const m_; |
| 629 | |
| 630 | DISALLOW_COPY_AND_ASSIGN(Adder); |
| 631 | }; |
| 632 | |
| 633 | // Handles removing a mutex from the robust list. |
| 634 | // The idea is to create one of these at the beginning of a function that needs |
| 635 | // to do this. |
| 636 | class Remover { |
| 637 | public: |
| 638 | Remover(aos_mutex *m) { |
| 639 | assert(robust_head.pending_next == 0); |
| 640 | if (kRobustListDebug) { |
| 641 | printf("%" PRId32 ": beginning to remove %p, n=%p p=%p\n", get_tid(), m, |
| 642 | next_to_mutex(m->next), m->previous); |
| 643 | } |
| 644 | robust_head.pending_next = mutex_to_next(m); |
| 645 | aos_compiler_memory_barrier(); |
| 646 | |
| 647 | aos_mutex *const previous = m->previous; |
| 648 | const uintptr_t next_value = m->next; |
| 649 | |
| 650 | previous->next = m->next; |
| 651 | if (!next_is_head(next_value)) { |
| 652 | // robust_head's psuedo-mutex doesn't have a previous pointer to update. |
| 653 | next_to_mutex(next_value)->previous = previous; |
| 654 | } |
| 655 | |
| 656 | if (kRobustListDebug) { |
| 657 | printf("%" PRId32 ": done removing %p\n", get_tid(), m); |
| 658 | } |
| 659 | } |
| 660 | ~Remover() { |
| 661 | assert(robust_head.pending_next != 0); |
| 662 | aos_compiler_memory_barrier(); |
| 663 | robust_head.pending_next = 0; |
| 664 | if (kRobustListDebug) { |
| 665 | printf("%" PRId32 ": done with removal\n", get_tid()); |
| 666 | } |
| 667 | } |
| 668 | |
| 669 | private: |
| 670 | DISALLOW_COPY_AND_ASSIGN(Remover); |
| 671 | }; |
| 672 | |
| 673 | } // namespace my_robust_list |
| 674 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 675 | void initialize_in_new_thread() { |
| 676 | // No synchronization necessary in most of this because it's all thread-local! |
| 677 | |
| 678 | my_tid = do_get_tid(); |
| 679 | |
John Park | 9372a68 | 2019-11-27 18:07:48 -0800 | [diff] [blame] | 680 | static absl::once_flag once; |
| 681 | absl::call_once(once, InstallAtforkHook); |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 682 | |
| 683 | my_robust_list::Init(); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 684 | } |
| 685 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 686 | // Finishes the locking of a mutex by potentially clearing FUTEX_OWNER_DIED in |
| 687 | // the futex and returning the correct value. |
| 688 | inline int mutex_finish_lock(aos_mutex *m) { |
| 689 | const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_ACQUIRE); |
| 690 | if (__builtin_expect((value & FUTEX_OWNER_DIED) != 0, false)) { |
| 691 | __atomic_and_fetch(&m->futex, ~FUTEX_OWNER_DIED, __ATOMIC_RELAXED); |
| 692 | force_lock_pthread_mutex(m); |
| 693 | return 1; |
| 694 | } else { |
| 695 | lock_pthread_mutex(m); |
| 696 | return 0; |
| 697 | } |
| 698 | } |
| 699 | |
| 700 | // Split out separately from mutex_get so condition_wait can call it and use its |
| 701 | // own my_robust_list::Adder. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 702 | inline int mutex_do_get(aos_mutex *m, bool signals_fail, |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 703 | const struct timespec *timeout, uint32_t tid) { |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 704 | RunShmObservers run_observers(m, true); |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 705 | if (kPrintOperations) { |
| 706 | printf("%" PRId32 ": %p do_get\n", tid, m); |
| 707 | } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 708 | |
| 709 | while (true) { |
| 710 | // If the atomic 0->TID transition fails. |
| 711 | if (!compare_and_swap(&m->futex, 0, tid)) { |
| 712 | // Wait in the kernel, which handles atomically ORing in FUTEX_WAITERS |
| 713 | // before actually sleeping. |
| 714 | const int ret = sys_futex_wait(FUTEX_LOCK_PI, &m->futex, 1, timeout); |
| 715 | if (ret != 0) { |
| 716 | if (timeout != NULL && ret == -ETIMEDOUT) { |
| 717 | return 3; |
| 718 | } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 719 | if (__builtin_expect(ret == -EINTR, true)) { |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 720 | if (signals_fail) { |
| 721 | return 2; |
| 722 | } else { |
| 723 | continue; |
| 724 | } |
| 725 | } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 726 | my_robust_list::robust_head.pending_next = 0; |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 727 | CHECK_NE(ret, -EDEADLK) << ": multiple lock of " << m << " by " << tid; |
| 728 | |
| 729 | errno = -ret; |
| 730 | PLOG(FATAL) << "FUTEX_LOCK_PI(" << &m->futex |
| 731 | << "(=" << __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST) |
| 732 | << "), 1, " << timeout << ") failed"; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 733 | } else { |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 734 | if (kLockDebug) { |
| 735 | printf("%" PRId32 ": %p kernel lock done\n", tid, m); |
| 736 | } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 737 | // The kernel already handled setting the value to our TID (ish). |
| 738 | break; |
| 739 | } |
| 740 | } else { |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 741 | if (kLockDebug) { |
| 742 | printf("%" PRId32 ": %p fast lock done\n", tid, m); |
| 743 | } |
| 744 | lock_pthread_mutex(m); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 745 | // Fastpath succeeded, so no need to call into the kernel. |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 746 | // Because this is the fastpath, it's a good idea to avoid even having to |
| 747 | // load the value again down below. |
| 748 | return 0; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 749 | } |
| 750 | } |
| 751 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 752 | return mutex_finish_lock(m); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 753 | } |
| 754 | |
| 755 | // The common implementation for everything that wants to lock a mutex. |
| 756 | // If signals_fail is false, the function will try again if the wait syscall is |
| 757 | // interrupted by a signal. |
| 758 | // timeout can be NULL for no timeout. |
| 759 | inline int mutex_get(aos_mutex *m, bool signals_fail, |
| 760 | const struct timespec *timeout) { |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 761 | const uint32_t tid = get_tid(); |
| 762 | my_robust_list::Adder adder(m); |
| 763 | const int r = mutex_do_get(m, signals_fail, timeout, tid); |
| 764 | if (r == 0 || r == 1) adder.Add(); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 765 | return r; |
| 766 | } |
| 767 | |
| 768 | // The common implementation for broadcast and signal. |
| 769 | // number_requeue is the number of waiters to requeue (probably INT_MAX or 0). 1 |
| 770 | // will always be woken. |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 771 | void condition_wake(aos_condition *c, aos_mutex *m, int number_requeue) { |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 772 | RunShmObservers run_observers(c, true); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 773 | // Make it so that anybody just going to sleep won't. |
| 774 | // This is where we might accidentally wake more than just 1 waiter with 1 |
| 775 | // signal(): |
| 776 | // 1 already sleeping will be woken but n might never actually make it to |
| 777 | // sleep in the kernel because of this. |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 778 | uint32_t new_value = __atomic_add_fetch(c, 1, __ATOMIC_SEQ_CST); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 779 | |
Brian | 2a4294f | 2019-06-12 20:23:32 -0700 | [diff] [blame] | 780 | while (true) { |
| 781 | // This really wants to be FUTEX_REQUEUE_PI, but the kernel doesn't have |
| 782 | // that... However, the code to support that is in the kernel, so it might |
| 783 | // be a good idea to patch it to support that and use it iff it's there. |
| 784 | const int ret = |
| 785 | sys_futex_cmp_requeue_pi(c, 1, number_requeue, &m->futex, new_value); |
| 786 | if (ret < 0) { |
| 787 | // If the value got changed out from under us (aka somebody else did a |
| 788 | // condition_wake). |
| 789 | if (__builtin_expect(ret == -EAGAIN, true)) { |
| 790 | // If we're doing a broadcast, the other guy might have done a signal |
| 791 | // instead, so we have to try again. |
| 792 | // If we're doing a signal, we have to go again to make sure that 2 |
| 793 | // signals wake 2 processes. |
| 794 | new_value = __atomic_load_n(c, __ATOMIC_RELAXED); |
| 795 | continue; |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 796 | } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 797 | my_robust_list::robust_head.pending_next = 0; |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 798 | errno = -ret; |
| 799 | PLOG(FATAL) << "FUTEX_CMP_REQUEUE_PI(" << c << ", 1, " << number_requeue |
| 800 | << ", " << &m->futex << ", *" << c << ") failed"; |
Brian | 2a4294f | 2019-06-12 20:23:32 -0700 | [diff] [blame] | 801 | } else { |
| 802 | return; |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 803 | } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 804 | } |
| 805 | } |
| 806 | |
| 807 | } // namespace |
| 808 | |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 809 | int mutex_lock(aos_mutex *m) { return mutex_get(m, true, NULL); } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 810 | int mutex_lock_timeout(aos_mutex *m, const struct timespec *timeout) { |
| 811 | return mutex_get(m, true, timeout); |
| 812 | } |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 813 | int mutex_grab(aos_mutex *m) { return mutex_get(m, false, NULL); } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 814 | |
| 815 | void mutex_unlock(aos_mutex *m) { |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 816 | RunShmObservers run_observers(m, true); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 817 | const uint32_t tid = get_tid(); |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 818 | if (kPrintOperations) { |
| 819 | printf("%" PRId32 ": %p unlock\n", tid, m); |
| 820 | } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 821 | |
| 822 | const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST); |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 823 | if (__builtin_expect((value & FUTEX_TID_MASK) != tid, false)) { |
| 824 | my_robust_list::robust_head.pending_next = 0; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 825 | check_cached_tid(tid); |
| 826 | if ((value & FUTEX_TID_MASK) == 0) { |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 827 | LOG(FATAL) << "multiple unlock of aos_mutex " << m << " by " << tid; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 828 | } else { |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 829 | LOG(FATAL) << "aos_mutex " << m << " is locked by " |
| 830 | << (value & FUTEX_TID_MASK) << ", not " << tid; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 831 | } |
| 832 | } |
| 833 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 834 | my_robust_list::Remover remover(m); |
| 835 | unlock_pthread_mutex(m); |
| 836 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 837 | // If the atomic TID->0 transition fails (ie FUTEX_WAITERS is set), |
| 838 | if (!compare_and_swap(&m->futex, tid, 0)) { |
| 839 | // The kernel handles everything else. |
| 840 | const int ret = sys_futex_unlock_pi(&m->futex); |
| 841 | if (ret != 0) { |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 842 | my_robust_list::robust_head.pending_next = 0; |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 843 | errno = -ret; |
| 844 | PLOG(FATAL) << "FUTEX_UNLOCK_PI(" << (&m->futex) << ") failed"; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 845 | } |
| 846 | } else { |
| 847 | // There aren't any waiters, so no need to call into the kernel. |
| 848 | } |
| 849 | } |
| 850 | |
| 851 | int mutex_trylock(aos_mutex *m) { |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 852 | RunShmObservers run_observers(m, true); |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 853 | const uint32_t tid = get_tid(); |
| 854 | if (kPrintOperations) { |
| 855 | printf("%" PRId32 ": %p trylock\n", tid, m); |
| 856 | } |
| 857 | my_robust_list::Adder adder(m); |
| 858 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 859 | // Try an atomic 0->TID transition. |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 860 | uint32_t c = compare_and_swap_val(&m->futex, 0, tid); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 861 | |
| 862 | if (c != 0) { |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 863 | if (__builtin_expect((c & FUTEX_OWNER_DIED) == 0, true)) { |
| 864 | // Somebody else had it locked; we failed. |
| 865 | return 4; |
| 866 | } else { |
| 867 | // FUTEX_OWNER_DIED was set, so we have to call into the kernel to deal |
| 868 | // with resetting it. |
| 869 | const int ret = sys_futex_wait(FUTEX_TRYLOCK_PI, &m->futex, 0, NULL); |
| 870 | if (ret == 0) { |
| 871 | adder.Add(); |
| 872 | // Only clear the owner died if somebody else didn't do the recovery |
| 873 | // and then unlock before our TRYLOCK happened. |
| 874 | return mutex_finish_lock(m); |
| 875 | } else { |
| 876 | // EWOULDBLOCK means that somebody else beat us to it. |
| 877 | if (__builtin_expect(ret == -EWOULDBLOCK, true)) { |
| 878 | return 4; |
| 879 | } |
| 880 | my_robust_list::robust_head.pending_next = 0; |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 881 | errno = -ret; |
| 882 | PLOG(FATAL) << "FUTEX_TRYLOCK_PI(" << (&m->futex) |
| 883 | << ", 0, NULL) failed"; |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 884 | } |
| 885 | } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 886 | } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 887 | |
| 888 | lock_pthread_mutex(m); |
| 889 | adder.Add(); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 890 | return 0; |
| 891 | } |
| 892 | |
| 893 | bool mutex_islocked(const aos_mutex *m) { |
| 894 | const uint32_t tid = get_tid(); |
| 895 | |
| 896 | const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_RELAXED); |
| 897 | return (value & FUTEX_TID_MASK) == tid; |
| 898 | } |
| 899 | |
Brian Silverman | 27af1f6 | 2019-11-18 12:04:48 -0800 | [diff] [blame] | 900 | void death_notification_init(aos_mutex *m) { |
| 901 | const uint32_t tid = get_tid(); |
| 902 | if (kPrintOperations) { |
| 903 | printf("%" PRId32 ": %p death_notification start\n", tid, m); |
| 904 | } |
| 905 | my_robust_list::Adder adder(m); |
| 906 | { |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 907 | RunShmObservers run_observers(m, true); |
Brian Silverman | 27af1f6 | 2019-11-18 12:04:48 -0800 | [diff] [blame] | 908 | CHECK(compare_and_swap(&m->futex, 0, tid)); |
| 909 | } |
| 910 | adder.Add(); |
| 911 | } |
| 912 | |
| 913 | void death_notification_release(aos_mutex *m) { |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 914 | RunShmObservers run_observers(m, true); |
Brian Silverman | 27af1f6 | 2019-11-18 12:04:48 -0800 | [diff] [blame] | 915 | |
| 916 | #ifndef NDEBUG |
| 917 | // Verify it's "locked", like it should be. |
| 918 | { |
| 919 | const uint32_t tid = get_tid(); |
| 920 | if (kPrintOperations) { |
| 921 | printf("%" PRId32 ": %p death_notification release\n", tid, m); |
| 922 | } |
| 923 | const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST); |
| 924 | assert((value & ~FUTEX_WAITERS) == tid); |
| 925 | } |
| 926 | #endif |
| 927 | |
| 928 | my_robust_list::Remover remover(m); |
| 929 | ANNOTATE_HAPPENS_BEFORE(m); |
| 930 | const int ret = sys_futex_unlock_pi(&m->futex); |
| 931 | if (ret != 0) { |
| 932 | my_robust_list::robust_head.pending_next = 0; |
| 933 | errno = -ret; |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 934 | PLOG(FATAL) << "FUTEX_UNLOCK_PI(" << &m->futex << ") failed"; |
Brian Silverman | 27af1f6 | 2019-11-18 12:04:48 -0800 | [diff] [blame] | 935 | } |
| 936 | } |
| 937 | |
Austin Schuh | 0ad2b6f | 2019-06-09 21:27:07 -0700 | [diff] [blame] | 938 | int condition_wait(aos_condition *c, aos_mutex *m, struct timespec *end_time) { |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 939 | RunShmObservers run_observers(c, false); |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 940 | const uint32_t tid = get_tid(); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 941 | const uint32_t wait_start = __atomic_load_n(c, __ATOMIC_SEQ_CST); |
| 942 | |
| 943 | mutex_unlock(m); |
| 944 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 945 | my_robust_list::Adder adder(m); |
| 946 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 947 | while (true) { |
| 948 | // Wait in the kernel iff the value of it doesn't change (ie somebody else |
| 949 | // does a wake) from before we unlocked the mutex. |
Austin Schuh | 0ad2b6f | 2019-06-09 21:27:07 -0700 | [diff] [blame] | 950 | int ret = sys_futex_wait_requeue_pi(c, wait_start, end_time, &m->futex); |
| 951 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 952 | if (ret != 0) { |
Austin Schuh | 0ad2b6f | 2019-06-09 21:27:07 -0700 | [diff] [blame] | 953 | // Timed out waiting. Signal that back up to the user. |
| 954 | if (__builtin_expect(ret == -ETIMEDOUT, true)) { |
| 955 | // We have to relock it ourself because the kernel didn't do it. |
| 956 | const int r = mutex_do_get(m, false, nullptr, tid); |
| 957 | assert(__builtin_expect(r == 0 || r == 1, true)); |
| 958 | adder.Add(); |
| 959 | |
| 960 | // OWNER_DIED takes priority. Pass it on if we found it. |
| 961 | if (r == 1) return r; |
| 962 | // Otherwise communicate that we were interrupted. |
| 963 | return -1; |
| 964 | } |
| 965 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 966 | // If it failed because somebody else did a wake and changed the value |
| 967 | // before we actually made it to sleep. |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 968 | if (__builtin_expect(ret == -EAGAIN, true)) { |
| 969 | // There's no need to unconditionally set FUTEX_WAITERS here if we're |
| 970 | // using REQUEUE_PI because the kernel automatically does that in the |
| 971 | // REQUEUE_PI iff it requeued anybody. |
| 972 | // If we're not using REQUEUE_PI, then everything is just normal locks |
| 973 | // etc, so there's no need to do anything special there either. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 974 | |
| 975 | // We have to relock it ourself because the kernel didn't do it. |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 976 | const int r = mutex_do_get(m, false, nullptr, tid); |
| 977 | assert(__builtin_expect(r == 0 || r == 1, true)); |
| 978 | adder.Add(); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 979 | return r; |
| 980 | } |
| 981 | // Try again if it was because of a signal. |
Austin Schuh | 0ad2b6f | 2019-06-09 21:27:07 -0700 | [diff] [blame] | 982 | if (__builtin_expect((ret == -EINTR), true)) { |
| 983 | continue; |
| 984 | } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 985 | my_robust_list::robust_head.pending_next = 0; |
Alex Perry | cb7da4b | 2019-08-28 19:35:56 -0700 | [diff] [blame] | 986 | errno = -ret; |
| 987 | PLOG(FATAL) << "FUTEX_WAIT_REQUEUE_PI(" << c << ", " << wait_start << ", " |
| 988 | << (&m->futex) << ") failed"; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 989 | } else { |
Brian | 2a4294f | 2019-06-12 20:23:32 -0700 | [diff] [blame] | 990 | // Record that the kernel relocked it for us. |
| 991 | lock_pthread_mutex(m); |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 992 | |
Austin Schuh | 0ad2b6f | 2019-06-09 21:27:07 -0700 | [diff] [blame] | 993 | // We succeeded in waiting, and the kernel took care of locking the |
| 994 | // mutex |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 995 | // for us and setting FUTEX_WAITERS iff it needed to (for REQUEUE_PI). |
| 996 | |
| 997 | adder.Add(); |
| 998 | |
| 999 | const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_RELAXED); |
| 1000 | if (__builtin_expect((value & FUTEX_OWNER_DIED) != 0, false)) { |
| 1001 | __atomic_and_fetch(&m->futex, ~FUTEX_OWNER_DIED, __ATOMIC_RELAXED); |
| 1002 | return 1; |
| 1003 | } else { |
| 1004 | return 0; |
| 1005 | } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 1006 | } |
| 1007 | } |
| 1008 | } |
| 1009 | |
| 1010 | void condition_signal(aos_condition *c, aos_mutex *m) { |
| 1011 | condition_wake(c, m, 0); |
| 1012 | } |
| 1013 | |
| 1014 | void condition_broadcast(aos_condition *c, aos_mutex *m) { |
| 1015 | condition_wake(c, m, INT_MAX); |
| 1016 | } |
| 1017 | |
| 1018 | int futex_wait_timeout(aos_futex *m, const struct timespec *timeout) { |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 1019 | RunShmObservers run_observers(m, false); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 1020 | const int ret = sys_futex_wait(FUTEX_WAIT, m, 0, timeout); |
| 1021 | if (ret != 0) { |
| 1022 | if (ret == -EINTR) { |
| 1023 | return 1; |
| 1024 | } else if (ret == -ETIMEDOUT) { |
| 1025 | return 2; |
| 1026 | } else if (ret != -EWOULDBLOCK) { |
| 1027 | errno = -ret; |
| 1028 | return -1; |
| 1029 | } |
| 1030 | } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 1031 | ANNOTATE_HAPPENS_AFTER(m); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 1032 | return 0; |
| 1033 | } |
| 1034 | |
| 1035 | int futex_wait(aos_futex *m) { return futex_wait_timeout(m, NULL); } |
| 1036 | |
| 1037 | int futex_set_value(aos_futex *m, uint32_t value) { |
Brian Silverman | 1ed5df5 | 2021-09-13 20:14:06 -0700 | [diff] [blame] | 1038 | RunShmObservers run_observers(m, false); |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 1039 | ANNOTATE_HAPPENS_BEFORE(m); |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 1040 | __atomic_store_n(m, value, __ATOMIC_SEQ_CST); |
| 1041 | const int r = sys_futex_wake(m, INT_MAX - 4096); |
| 1042 | if (__builtin_expect( |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 1043 | static_cast<unsigned int>(r) > static_cast<unsigned int>(-4096), |
| 1044 | false)) { |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 1045 | errno = -r; |
| 1046 | return -1; |
| 1047 | } else { |
| 1048 | return r; |
| 1049 | } |
| 1050 | } |
| 1051 | |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 1052 | int futex_set(aos_futex *m) { return futex_set_value(m, 1); } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 1053 | |
| 1054 | int futex_unset(aos_futex *m) { |
| 1055 | return !__atomic_exchange_n(m, 0, __ATOMIC_SEQ_CST); |
| 1056 | } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 1057 | |
| 1058 | namespace aos { |
| 1059 | namespace linux_code { |
| 1060 | namespace ipc_lib { |
| 1061 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 1062 | // Sets an extra offset between mutexes and the value we use for them in the |
| 1063 | // robust list (only the forward pointers). This is used to work around a kernel |
| 1064 | // bug by keeping a second set of mutexes which is always writable so the kernel |
| 1065 | // won't go into an infinite loop when trying to unlock them. |
| 1066 | void SetRobustListOffset(ptrdiff_t offset) { |
| 1067 | my_robust_list::SetRobustListOffset(offset); |
| 1068 | } |
| 1069 | |
| 1070 | // Returns true iff there are any mutexes locked by the current thread. |
| 1071 | // This is mainly useful for testing. |
Tyler Chatow | bf0609c | 2021-07-31 16:13:27 -0700 | [diff] [blame] | 1072 | bool HaveLockedMutexes() { return my_robust_list::HaveLockedMutexes(); } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 1073 | |
| 1074 | } // namespace ipc_lib |
| 1075 | } // namespace linux_code |
| 1076 | } // namespace aos |