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