blob: 78c6b2cc6c16587622b7d88f5fbb27f0c98ead6b [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>
Stephan Pleines682928d2024-05-31 20:43:48 -070010#include <signal.h>
11#include <stdio.h>
Tyler Chatowbf0609c2021-07-31 16:13:27 -070012#include <sys/syscall.h>
Tyler Chatowbf0609c2021-07-31 16:13:27 -070013#include <unistd.h>
14
15#include <cassert>
16#include <cerrno>
17#include <cinttypes>
18#include <climits>
Stephan Pleines682928d2024-05-31 20:43:48 -070019#include <ostream>
Brian Silvermandc1eb272014-08-19 14:25:59 -040020
Brian Silverman1ed5df52021-09-13 20:14:06 -070021#include "aos/ipc_lib/shm_observers.h"
22
Brian Silverman71c55c52014-08-19 14:31:59 -040023#ifdef AOS_SANITIZER_thread
24#include <sanitizer/tsan_interface_atomic.h>
25#endif
26
Alex Perrycb7da4b2019-08-28 19:35:56 -070027#include "absl/base/call_once.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070028#include "glog/logging.h"
29
Brian Silvermanb47f5552020-10-01 15:08:14 -070030#include "aos/macros.h"
Brian Silvermanb47f5552020-10-01 15:08:14 -070031#include "aos/util/compiler_memory_barrier.h"
32
Brian Silverman1ed5df52021-09-13 20:14:06 -070033using ::aos::linux_code::ipc_lib::RunShmObservers;
Brian Silvermandc1eb272014-08-19 14:25:59 -040034
Tyler Chatowbf0609c2021-07-31 16:13:27 -070035// This code was originally based on
36// <https://www.akkadia.org/drepper/futex.pdf>, but is has since evolved a lot.
37// However, that still has useful information.
Brian Silvermandc1eb272014-08-19 14:25:59 -040038//
39// Finding information about actually using futexes is really REALLY hard, so
40// here's a list of the stuff that I've used:
41// futex(7) has a really high-level overview.
42// <http://locklessinc.com/articles/futex_cheat_sheet/> describes some of the
43// operations in a bit more detail than most places.
44// <http://locklessinc.com/articles/mutex_cv_futex/> is the basis of our
45// implementations (before PI).
46// <http://lwn.net/Articles/360699/> has a nice overview of futexes in late 2009
47// (fairly recent compared to everything else...).
48// <https://www.kernel.org/doc/Documentation/pi-futex.txt>,
49// <https://www.kernel.org/doc/Documentation/futex-requeue-pi.txt>,
50// <https://www.kernel.org/doc/Documentation/robust-futexes.txt>,
51// and <https://www.kernel.org/doc/Documentation/robust-futex-ABI.txt> are all
52// useful references.
53// The kernel source (kernel/futex.c) has some useful comments about what the
54// various operations do (except figuring out which argument goes where in the
55// syscall is still confusing).
56// futex(2) is basically useless except for describing the order of the
57// arguments (it only has high-level descriptions of what some of the
58// operations do, and some of them are wrong in Wheezy).
59// glibc's nptl pthreads implementation is the intended user of most of these
60// things, so it is also a good place to look for examples. However, it is all
61// very hard to read because it supports ~20 different kinds of mutexes and
62// several variations of condition variables, and some of the pieces of code
63// are only written in assembly.
64// set_robust_list(2) is wrong in Wheezy (it doesn't actually take a TID
65// argument).
66//
67// Can't use PRIVATE futex operations because they use the pid (or something) as
68// part of the hash.
69//
70// ThreadSanitizer understands how these mutexes etc work. It appears to be able
71// to figure out the happens-before relationship from the __ATOMIC_SEQ_CST
72// atomic primitives.
73//
74// Remember that EAGAIN and EWOUDBLOCK are the same! (ie if you get EAGAIN from
75// FUTEX_WAIT, the docs call it EWOULDBLOCK...)
76
77// Values for an aos_mutex.futex (kernel-mandated):
78// 0 = unlocked
79// TID = locked, not contended
80// |FUTEX_WAITERS = there are waiters (aka contended)
Brian Silverman71c55c52014-08-19 14:31:59 -040081// |FUTEX_OWNER_DIED = old owner died
Brian Silvermandc1eb272014-08-19 14:25:59 -040082//
83// Values for an aos_futex being used directly:
84// 0 = unset
85// 1 = set
86//
87// The value of an aos_condition is just a generation counter.
88
Brian Silverman71c55c52014-08-19 14:31:59 -040089#ifdef AOS_SANITIZER_thread
90extern "C" void AnnotateHappensBefore(const char *file, int line,
91 uintptr_t addr);
92extern "C" void AnnotateHappensAfter(const char *file, int line,
93 uintptr_t addr);
94#define ANNOTATE_HAPPENS_BEFORE(address) \
95 AnnotateHappensBefore(__FILE__, __LINE__, \
96 reinterpret_cast<uintptr_t>(address))
97#define ANNOTATE_HAPPENS_AFTER(address) \
98 AnnotateHappensAfter(__FILE__, __LINE__, reinterpret_cast<uintptr_t>(address))
99#else
100#define ANNOTATE_HAPPENS_BEFORE(address)
101#define ANNOTATE_HAPPENS_AFTER(address)
102#endif
103
Brian Silvermandc1eb272014-08-19 14:25:59 -0400104namespace {
105
Brian Silverman71c55c52014-08-19 14:31:59 -0400106const bool kRobustListDebug = false;
107const bool kLockDebug = false;
108const bool kPrintOperations = false;
109
Brian Silvermandc1eb272014-08-19 14:25:59 -0400110// These sys_futex_* functions are wrappers around syscall(SYS_futex). They each
111// take a specific set of arguments for a given futex operation. They return the
112// result or a negated errno value. -1..-4095 mean errors and not successful
113// results, which is guaranteed by the kernel.
114//
Brian Silverman0b58a612021-09-13 15:32:29 -0700115// They each have optimized versions for some architectures which don't go
116// through syscall(2) or errno. These use register variables to get the values
117// in the right registers to actually make the syscall.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400118
Brian Silverman0b58a612021-09-13 15:32:29 -0700119// The actual macros that we key off of to use the inline versions or not.
Brian Silverman17426d92018-08-09 11:38:49 -0700120#if defined(__ARM_EABI__)
Brian Silverman0b58a612021-09-13 15:32:29 -0700121// The syscall interface is different for non-EABI ARM, so we test specifically
122// for EABI.
Brian Silverman17426d92018-08-09 11:38:49 -0700123#define ARM_EABI_INLINE_SYSCALL 1
Brian Silverman0b58a612021-09-13 15:32:29 -0700124#define AARCH64_INLINE_SYSCALL 0
125#elif defined(__aarch64__)
126// Linux only has one supported syscall ABI on aarch64, which is the one we
127// support.
128#define ARM_EABI_INLINE_SYSCALL 0
129#define AARCH64_INLINE_SYSCALL 1
Brian Silverman17426d92018-08-09 11:38:49 -0700130#else
131#define ARM_EABI_INLINE_SYSCALL 0
Brian Silverman0b58a612021-09-13 15:32:29 -0700132#define AARCH64_INLINE_SYSCALL 0
Brian Silverman17426d92018-08-09 11:38:49 -0700133#endif
Brian Silvermandc1eb272014-08-19 14:25:59 -0400134
135// Used for FUTEX_WAIT, FUTEX_LOCK_PI, and FUTEX_TRYLOCK_PI.
136inline int sys_futex_wait(int op, aos_futex *addr1, int val1,
137 const struct timespec *timeout) {
138#if ARM_EABI_INLINE_SYSCALL
139 register aos_futex *addr1_reg __asm__("r0") = addr1;
140 register int op_reg __asm__("r1") = op;
141 register int val1_reg __asm__("r2") = val1;
142 register const struct timespec *timeout_reg __asm__("r3") = timeout;
143 register int syscall_number __asm__("r7") = SYS_futex;
144 register int result __asm__("r0");
145 __asm__ volatile("swi #0"
146 : "=r"(result)
147 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
148 "r"(timeout_reg), "r"(syscall_number)
149 : "memory");
150 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700151#elif AARCH64_INLINE_SYSCALL
152 register aos_futex *addr1_reg __asm__("x0") = addr1;
153 register int op_reg __asm__("x1") = op;
154 register int val1_reg __asm__("x2") = val1;
155 register const struct timespec *timeout_reg __asm__("x3") = timeout;
156 register int syscall_number __asm__("x8") = SYS_futex;
157 register int result __asm__("x0");
158 __asm__ volatile("svc #0"
159 : "=r"(result)
160 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
161 "r"(timeout_reg), "r"(syscall_number)
162 : "memory");
163 return result;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400164#else
165 const int r = syscall(SYS_futex, addr1, op, val1, timeout);
166 if (r == -1) return -errno;
167 return r;
168#endif
169}
170
171inline int sys_futex_wake(aos_futex *addr1, int val1) {
172#if ARM_EABI_INLINE_SYSCALL
173 register aos_futex *addr1_reg __asm__("r0") = addr1;
174 register int op_reg __asm__("r1") = FUTEX_WAKE;
175 register int val1_reg __asm__("r2") = val1;
176 register int syscall_number __asm__("r7") = SYS_futex;
177 register int result __asm__("r0");
178 __asm__ volatile("swi #0"
179 : "=r"(result)
180 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
181 "r"(syscall_number)
182 : "memory");
183 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700184#elif AARCH64_INLINE_SYSCALL
185 register aos_futex *addr1_reg __asm__("x0") = addr1;
186 register int op_reg __asm__("x1") = FUTEX_WAKE;
187 register int val1_reg __asm__("x2") = val1;
188 register int syscall_number __asm__("x8") = SYS_futex;
189 register int result __asm__("x0");
190 __asm__ volatile("svc #0"
191 : "=r"(result)
192 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
193 "r"(syscall_number)
194 : "memory");
195 return result;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400196#else
197 const int r = syscall(SYS_futex, addr1, FUTEX_WAKE, val1);
198 if (r == -1) return -errno;
199 return r;
200#endif
201}
202
Brian Silverman71c55c52014-08-19 14:31:59 -0400203inline int sys_futex_cmp_requeue_pi(aos_futex *addr1, int num_wake,
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700204 int num_requeue, aos_futex *m,
205 uint32_t val) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400206#if ARM_EABI_INLINE_SYSCALL
207 register aos_futex *addr1_reg __asm__("r0") = addr1;
208 register int op_reg __asm__("r1") = FUTEX_CMP_REQUEUE_PI;
209 register int num_wake_reg __asm__("r2") = num_wake;
210 register int num_requeue_reg __asm__("r3") = num_requeue;
211 register aos_futex *m_reg __asm__("r4") = m;
212 register uint32_t val_reg __asm__("r5") = val;
213 register int syscall_number __asm__("r7") = SYS_futex;
214 register int result __asm__("r0");
215 __asm__ volatile("swi #0"
216 : "=r"(result)
217 : "r"(addr1_reg), "r"(op_reg), "r"(num_wake_reg),
218 "r"(num_requeue_reg), "r"(m_reg), "r"(val_reg),
219 "r"(syscall_number)
220 : "memory");
221 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700222#elif AARCH64_INLINE_SYSCALL
223 register aos_futex *addr1_reg __asm__("x0") = addr1;
224 register int op_reg __asm__("x1") = FUTEX_CMP_REQUEUE_PI;
225 register int num_wake_reg __asm__("x2") = num_wake;
226 register int num_requeue_reg __asm__("x3") = num_requeue;
227 register aos_futex *m_reg __asm__("x4") = m;
228 register uint32_t val_reg __asm__("x5") = val;
229 register int syscall_number __asm__("x8") = SYS_futex;
230 register int result __asm__("x0");
231 __asm__ volatile("svc #0"
232 : "=r"(result)
233 : "r"(addr1_reg), "r"(op_reg), "r"(num_wake_reg),
234 "r"(num_requeue_reg), "r"(m_reg), "r"(val_reg),
235 "r"(syscall_number)
236 : "memory");
237 return result;
Brian Silverman71c55c52014-08-19 14:31:59 -0400238#else
239 const int r = syscall(SYS_futex, addr1, FUTEX_CMP_REQUEUE_PI, num_wake,
240 num_requeue, m, val);
241 if (r == -1) return -errno;
242 return r;
243#endif
244}
245
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700246inline int sys_futex_wait_requeue_pi(aos_condition *addr1, uint32_t start_val,
Brian Silverman71c55c52014-08-19 14:31:59 -0400247 const struct timespec *timeout,
248 aos_futex *m) {
249#if ARM_EABI_INLINE_SYSCALL
250 register aos_condition *addr1_reg __asm__("r0") = addr1;
251 register int op_reg __asm__("r1") = FUTEX_WAIT_REQUEUE_PI;
252 register uint32_t start_val_reg __asm__("r2") = start_val;
253 register const struct timespec *timeout_reg __asm__("r3") = timeout;
254 register aos_futex *m_reg __asm__("r4") = m;
255 register int syscall_number __asm__("r7") = SYS_futex;
256 register int result __asm__("r0");
257 __asm__ volatile("swi #0"
258 : "=r"(result)
259 : "r"(addr1_reg), "r"(op_reg), "r"(start_val_reg),
260 "r"(timeout_reg), "r"(m_reg), "r"(syscall_number)
261 : "memory");
262 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700263#elif AARCH64_INLINE_SYSCALL
264 register aos_condition *addr1_reg __asm__("x0") = addr1;
265 register int op_reg __asm__("x1") = FUTEX_WAIT_REQUEUE_PI;
266 register uint32_t start_val_reg __asm__("x2") = start_val;
267 register const struct timespec *timeout_reg __asm__("x3") = timeout;
268 register aos_futex *m_reg __asm__("x4") = m;
269 register int syscall_number __asm__("x8") = SYS_futex;
270 register int result __asm__("x0");
271 __asm__ volatile("svc #0"
272 : "=r"(result)
273 : "r"(addr1_reg), "r"(op_reg), "r"(start_val_reg),
274 "r"(timeout_reg), "r"(m_reg), "r"(syscall_number)
275 : "memory");
276 return result;
Brian Silverman71c55c52014-08-19 14:31:59 -0400277#else
278 const int r =
279 syscall(SYS_futex, addr1, FUTEX_WAIT_REQUEUE_PI, start_val, timeout, m);
280 if (r == -1) return -errno;
281 return r;
282#endif
283}
284
Brian Silvermandc1eb272014-08-19 14:25:59 -0400285inline int sys_futex_unlock_pi(aos_futex *addr1) {
286#if ARM_EABI_INLINE_SYSCALL
287 register aos_futex *addr1_reg __asm__("r0") = addr1;
288 register int op_reg __asm__("r1") = FUTEX_UNLOCK_PI;
289 register int syscall_number __asm__("r7") = SYS_futex;
290 register int result __asm__("r0");
291 __asm__ volatile("swi #0"
292 : "=r"(result)
293 : "r"(addr1_reg), "r"(op_reg), "r"(syscall_number)
294 : "memory");
295 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700296#elif AARCH64_INLINE_SYSCALL
297 register aos_futex *addr1_reg __asm__("x0") = addr1;
298 register int op_reg __asm__("x1") = FUTEX_UNLOCK_PI;
299 register int syscall_number __asm__("x8") = SYS_futex;
300 register int result __asm__("x0");
301 __asm__ volatile("svc #0"
302 : "=r"(result)
303 : "r"(addr1_reg), "r"(op_reg), "r"(syscall_number)
304 : "memory");
305 return result;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400306#else
307 const int r = syscall(SYS_futex, addr1, FUTEX_UNLOCK_PI);
308 if (r == -1) return -errno;
309 return r;
310#endif
311}
312
Brian Silverman71c55c52014-08-19 14:31:59 -0400313// Returns the previous value of f.
314inline uint32_t compare_and_swap_val(aos_futex *f, uint32_t before,
315 uint32_t after) {
316#ifdef AOS_SANITIZER_thread
317 // This is a workaround for <https://llvm.org/bugs/show_bug.cgi?id=23176>.
318 // Basically, most of the atomic operations are broken under tsan, but this
319 // particular one isn't.
320 // TODO(Brian): Remove this #ifdef (and the one in compare_and_swap) once we
321 // don't have to worry about tsan with this bug any more.
322 uint32_t before_value = before;
323 __tsan_atomic32_compare_exchange_strong(
324 reinterpret_cast<int32_t *>(f),
325 reinterpret_cast<int32_t *>(&before_value), after,
326 __tsan_memory_order_seq_cst, __tsan_memory_order_seq_cst);
327 return before_value;
328#else
329 return __sync_val_compare_and_swap(f, before, after);
330#endif
Brian Silvermandc1eb272014-08-19 14:25:59 -0400331}
332
Brian Silverman71c55c52014-08-19 14:31:59 -0400333// Returns true if it succeeds and false if it fails.
334inline bool compare_and_swap(aos_futex *f, uint32_t before, uint32_t after) {
335#ifdef AOS_SANITIZER_thread
336 return compare_and_swap_val(f, before, after) == before;
337#else
338 return __sync_bool_compare_and_swap(f, before, after);
339#endif
340}
341
342#ifdef AOS_SANITIZER_thread
343
344// Simple macro for checking something which should always be true.
345// Using the standard CHECK macro isn't safe because failures often result in
346// reentering the mutex locking code, which doesn't work.
347#define SIMPLE_CHECK(expr) \
348 do { \
349 if (!(expr)) { \
350 fprintf(stderr, "%s: %d: SIMPLE_CHECK(" #expr ") failed!\n", __FILE__, \
351 __LINE__); \
352 abort(); \
353 } \
354 } while (false)
355
356// Forcibly initializes the pthread mutex for *m.
357// This sequence of operations is only safe for the simpler kinds of mutexes in
358// glibc's pthreads implementation on Linux.
359void init_pthread_mutex(aos_mutex *m) {
360 // Re-initialize the mutex so the destroy won't fail if it's locked.
361 // tsan ignores this.
362 SIMPLE_CHECK(0 == pthread_mutex_init(&m->pthread_mutex, nullptr));
363 // Destroy the mutex so tsan will forget about it if some now-dead thread
364 // locked it.
365 SIMPLE_CHECK(0 == pthread_mutex_destroy(&m->pthread_mutex));
366
367 // Now actually initialize it, making sure it's process-shareable so it works
368 // correctly across shared memory.
369 pthread_mutexattr_t attr;
370 SIMPLE_CHECK(0 == pthread_mutexattr_init(&attr));
371 SIMPLE_CHECK(0 == pthread_mutexattr_setpshared(&attr, true));
372 SIMPLE_CHECK(0 == pthread_mutex_init(&m->pthread_mutex, &attr));
373 SIMPLE_CHECK(0 == pthread_mutexattr_destroy(&attr));
374}
375
376// Locks the pthread mutex for *m.
377// If a stack trace ever reveals the pthread_mutex_lock call in here blocking,
378// there is a bug in our mutex code or the way somebody is calling it.
379void lock_pthread_mutex(aos_mutex *m) {
380 if (!m->pthread_mutex_init) {
381 init_pthread_mutex(m);
382 m->pthread_mutex_init = true;
383 }
384 SIMPLE_CHECK(0 == pthread_mutex_lock(&m->pthread_mutex));
385}
386
387// Forcibly locks the pthread mutex for *m.
388// This will (somewhat hackily) rip the lock out from underneath somebody else
389// who is already holding it.
390void force_lock_pthread_mutex(aos_mutex *m) {
391 if (!m->pthread_mutex_init) {
392 init_pthread_mutex(m);
393 m->pthread_mutex_init = true;
394 }
395 const int trylock_result = pthread_mutex_trylock(&m->pthread_mutex);
396 SIMPLE_CHECK(trylock_result == 0 || trylock_result == EBUSY);
397 if (trylock_result == 0) {
398 // We're good, so unlock it and then go for a real lock down below.
399 SIMPLE_CHECK(0 == pthread_mutex_unlock(&m->pthread_mutex));
400 } else {
401 // Somebody (should always be somebody else who died with it held) already
402 // has it, so make tsan forget about that.
403 init_pthread_mutex(m);
404 }
405 lock_pthread_mutex(m);
406}
407
408// Unlocks the pthread mutex for *m.
409void unlock_pthread_mutex(aos_mutex *m) {
410 assert(m->pthread_mutex_init);
411 SIMPLE_CHECK(0 == pthread_mutex_unlock(&m->pthread_mutex));
412}
413
414#else
415
416// Empty implementations of all these so the code below doesn't need #ifdefs.
417static inline void lock_pthread_mutex(aos_mutex *) {}
418static inline void force_lock_pthread_mutex(aos_mutex *) {}
419static inline void unlock_pthread_mutex(aos_mutex *) {}
420
421#endif
422
Brian Silvermandc1eb272014-08-19 14:25:59 -0400423pid_t do_get_tid() {
424 pid_t r = syscall(SYS_gettid);
425 assert(r > 0);
426 return r;
427}
428
Alex Perrycb7da4b2019-08-28 19:35:56 -0700429// This gets called by functions before LOG(FATAL)ing with error messages
Austin Schuhf257f3c2019-10-27 21:00:43 -0700430// that would be incorrect if the error was caused by a process forking without
Brian Silvermandc1eb272014-08-19 14:25:59 -0400431// initialize_in_new_thread getting called in the fork.
432void check_cached_tid(pid_t tid) {
433 pid_t actual = do_get_tid();
434 if (tid != actual) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700435 LOG(FATAL) << "task " << static_cast<intmax_t>(tid) << " forked into "
436 << static_cast<intmax_t>(actual)
437 << " without letting aos_sync know so we're not really sure "
438 "what's going on";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400439 }
440}
441
442// Starts off at 0 in each new thread (because that's what it gets initialized
443// 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 -0700444thread_local pid_t my_tid = 0;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400445
446// Gets called before the fork(2) wrapper function returns in the child.
447void atfork_child() {
448 // The next time get_tid() is called, it will set everything up again.
449 my_tid = 0;
450}
451
John Park0e699502019-11-20 19:36:05 -0800452void InstallAtforkHook() {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700453 PCHECK(pthread_atfork(NULL, NULL, &atfork_child) == 0)
454 << ": pthread_atfork(NULL, NULL, "
455 << reinterpret_cast<void *>(&atfork_child) << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400456}
457
458// This gets called to set everything up in a new thread by get_tid().
459void initialize_in_new_thread();
460
461// Gets the current thread's TID and does all of the 1-time initialization the
462// first time it's called in a given thread.
463inline uint32_t get_tid() {
Brian Silverman71c55c52014-08-19 14:31:59 -0400464 if (__builtin_expect(my_tid == 0, false)) {
Brian Silvermandc1eb272014-08-19 14:25:59 -0400465 initialize_in_new_thread();
466 }
467 static_assert(sizeof(my_tid) <= sizeof(uint32_t), "pid_t is too big");
468 return static_cast<uint32_t>(my_tid);
469}
470
Brian Silverman71c55c52014-08-19 14:31:59 -0400471// Contains all of the stuff for dealing with the robust list. Nothing outside
472// this namespace should touch anything inside it except Init, Adder, and
473// Remover.
474namespace my_robust_list {
475
476static_assert(offsetof(aos_mutex, next) == 0,
477 "Our math all assumes that the beginning of a mutex and its next "
478 "pointer are at the same place in memory.");
479
480// Our version of robust_list_head.
481// This is copied from the kernel header because that's a pretty stable ABI (and
482// any changes will be backwards compatible anyways) and we want ours to have
483// different types.
484// The uintptr_ts are &next of the elements in the list (with stuff |ed in).
485struct aos_robust_list_head {
486 uintptr_t next;
487 long futex_offset;
488 uintptr_t pending_next;
489};
490
491static_assert(offsetof(aos_robust_list_head, next) ==
492 offsetof(robust_list_head, list),
493 "Our aos_robust_list_head doesn't match the kernel's");
494static_assert(offsetof(aos_robust_list_head, futex_offset) ==
495 offsetof(robust_list_head, futex_offset),
496 "Our aos_robust_list_head doesn't match the kernel's");
497static_assert(offsetof(aos_robust_list_head, pending_next) ==
498 offsetof(robust_list_head, list_op_pending),
499 "Our aos_robust_list_head doesn't match the kernel's");
500static_assert(sizeof(aos_robust_list_head) == sizeof(robust_list_head),
501 "Our aos_robust_list_head doesn't match the kernel's");
502
Austin Schuhf7bfb652023-08-25 14:22:50 -0700503thread_local aos_robust_list_head robust_head;
Brian Silverman71c55c52014-08-19 14:31:59 -0400504
505// Extra offset between mutex values and where we point to for their robust list
506// entries (from SetRobustListOffset).
507uintptr_t robust_list_offset = 0;
508
509// The value to OR each pointer's value with whenever putting it into the robust
510// list (technically only if it's PI, but all of ours are, so...).
511static const uintptr_t kRobustListOr = 1;
512
513// Returns the value which goes into a next variable to represent the head.
514inline uintptr_t robust_head_next_value() {
515 return reinterpret_cast<uintptr_t>(&robust_head.next);
516}
517// Returns true iff next represents the head.
518inline bool next_is_head(uintptr_t next) {
519 return next == robust_head_next_value();
520}
521// Returns the (psuedo-)mutex corresponding to the head.
522// This does NOT have a previous pointer, so be careful with the return value.
523inline aos_mutex *robust_head_mutex() {
524 return reinterpret_cast<aos_mutex *>(robust_head_next_value());
525}
526
527inline uintptr_t mutex_to_next(aos_mutex *m) {
528 return (reinterpret_cast<uintptr_t>(&m->next) + robust_list_offset) |
529 kRobustListOr;
530}
531inline aos_mutex *next_to_mutex(uintptr_t next) {
532 if (__builtin_expect(robust_list_offset != 0, false) && next_is_head(next)) {
533 // We don't offset the head pointer, so be careful.
534 return reinterpret_cast<aos_mutex *>(next);
535 }
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700536 return reinterpret_cast<aos_mutex *>((next & ~kRobustListOr) -
537 robust_list_offset);
Brian Silverman71c55c52014-08-19 14:31:59 -0400538}
539
540// Sets up the robust list for each thread.
541void Init() {
542 // It starts out just pointing back to itself.
543 robust_head.next = robust_head_next_value();
544 robust_head.futex_offset = static_cast<ssize_t>(offsetof(aos_mutex, futex)) -
545 static_cast<ssize_t>(offsetof(aos_mutex, next));
546 robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700547 PCHECK(syscall(SYS_set_robust_list, robust_head_next_value(),
548 sizeof(robust_head)) == 0)
549 << ": set_robust_list(" << reinterpret_cast<void *>(robust_head.next)
550 << ", " << sizeof(robust_head) << ") failed";
Brian Silverman71c55c52014-08-19 14:31:59 -0400551 if (kRobustListDebug) {
552 printf("%" PRId32 ": init done\n", get_tid());
553 }
554}
555
556// Updating the offset with locked mutexes is important during robustness
557// testing, because there are mutexes which are locked before this is set to a
558// non-0 value and then unlocked after it is changed back. However, to make sure
559// the code works correctly when manipulating the next pointer of the last of
560// those mutexes, all of their next values have to be adjusted appropriately.
561void SetRobustListOffset(uintptr_t offset) {
562 const uintptr_t offset_change = offset - robust_list_offset;
563 robust_list_offset = offset;
564 aos_mutex *m = robust_head_mutex();
565 // Update the offset contained in each of the mutexes which is already locked.
566 while (!next_is_head(m->next)) {
567 m->next += offset_change;
568 m = next_to_mutex(m->next);
569 }
570}
571
572bool HaveLockedMutexes() {
573 return robust_head.next != robust_head_next_value();
574}
575
576// Handles adding a mutex to the robust list.
577// The idea is to create one of these at the beginning of a function that needs
578// to do this and then call Add() iff it should actually be added.
579class Adder {
580 public:
581 Adder(aos_mutex *m) : m_(m) {
582 assert(robust_head.pending_next == 0);
583 if (kRobustListDebug) {
584 printf("%" PRId32 ": maybe add %p\n", get_tid(), m_);
585 }
586 robust_head.pending_next = mutex_to_next(m);
587 aos_compiler_memory_barrier();
588 }
589 ~Adder() {
590 assert(robust_head.pending_next == mutex_to_next(m_));
591 if (kRobustListDebug) {
592 printf("%" PRId32 ": done maybe add %p, n=%p p=%p\n", get_tid(), m_,
593 next_to_mutex(m_->next), m_->previous);
594 }
595 aos_compiler_memory_barrier();
596 robust_head.pending_next = 0;
597 }
598
599 void Add() {
600 assert(robust_head.pending_next == mutex_to_next(m_));
601 if (kRobustListDebug) {
602 printf("%" PRId32 ": adding %p\n", get_tid(), m_);
603 }
604 const uintptr_t old_head_next_value = robust_head.next;
605
606 m_->next = old_head_next_value;
607 aos_compiler_memory_barrier();
608 robust_head.next = mutex_to_next(m_);
609
610 m_->previous = robust_head_mutex();
611 if (!next_is_head(old_head_next_value)) {
612 // robust_head's psuedo-mutex doesn't have a previous pointer to update.
613 next_to_mutex(old_head_next_value)->previous = m_;
614 }
615 aos_compiler_memory_barrier();
616 if (kRobustListDebug) {
617 printf("%" PRId32 ": done adding %p\n", get_tid(), m_);
618 }
619 }
620
621 private:
622 aos_mutex *const m_;
623
624 DISALLOW_COPY_AND_ASSIGN(Adder);
625};
626
627// Handles removing a mutex from the robust list.
628// The idea is to create one of these at the beginning of a function that needs
629// to do this.
630class Remover {
631 public:
632 Remover(aos_mutex *m) {
633 assert(robust_head.pending_next == 0);
634 if (kRobustListDebug) {
635 printf("%" PRId32 ": beginning to remove %p, n=%p p=%p\n", get_tid(), m,
636 next_to_mutex(m->next), m->previous);
637 }
638 robust_head.pending_next = mutex_to_next(m);
639 aos_compiler_memory_barrier();
640
641 aos_mutex *const previous = m->previous;
642 const uintptr_t next_value = m->next;
643
644 previous->next = m->next;
645 if (!next_is_head(next_value)) {
646 // robust_head's psuedo-mutex doesn't have a previous pointer to update.
647 next_to_mutex(next_value)->previous = previous;
648 }
649
650 if (kRobustListDebug) {
651 printf("%" PRId32 ": done removing %p\n", get_tid(), m);
652 }
653 }
654 ~Remover() {
655 assert(robust_head.pending_next != 0);
656 aos_compiler_memory_barrier();
657 robust_head.pending_next = 0;
658 if (kRobustListDebug) {
659 printf("%" PRId32 ": done with removal\n", get_tid());
660 }
661 }
662
663 private:
664 DISALLOW_COPY_AND_ASSIGN(Remover);
665};
666
667} // namespace my_robust_list
668
Brian Silvermandc1eb272014-08-19 14:25:59 -0400669void initialize_in_new_thread() {
670 // No synchronization necessary in most of this because it's all thread-local!
671
672 my_tid = do_get_tid();
673
John Park9372a682019-11-27 18:07:48 -0800674 static absl::once_flag once;
675 absl::call_once(once, InstallAtforkHook);
Brian Silverman71c55c52014-08-19 14:31:59 -0400676
677 my_robust_list::Init();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400678}
679
Brian Silverman71c55c52014-08-19 14:31:59 -0400680// Finishes the locking of a mutex by potentially clearing FUTEX_OWNER_DIED in
681// the futex and returning the correct value.
682inline int mutex_finish_lock(aos_mutex *m) {
683 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_ACQUIRE);
684 if (__builtin_expect((value & FUTEX_OWNER_DIED) != 0, false)) {
685 __atomic_and_fetch(&m->futex, ~FUTEX_OWNER_DIED, __ATOMIC_RELAXED);
686 force_lock_pthread_mutex(m);
687 return 1;
688 } else {
689 lock_pthread_mutex(m);
690 return 0;
691 }
692}
693
694// Split out separately from mutex_get so condition_wait can call it and use its
695// own my_robust_list::Adder.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400696inline int mutex_do_get(aos_mutex *m, bool signals_fail,
Brian Silverman71c55c52014-08-19 14:31:59 -0400697 const struct timespec *timeout, uint32_t tid) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700698 RunShmObservers run_observers(m, true);
Brian Silverman71c55c52014-08-19 14:31:59 -0400699 if (kPrintOperations) {
700 printf("%" PRId32 ": %p do_get\n", tid, m);
701 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400702
703 while (true) {
704 // If the atomic 0->TID transition fails.
705 if (!compare_and_swap(&m->futex, 0, tid)) {
706 // Wait in the kernel, which handles atomically ORing in FUTEX_WAITERS
707 // before actually sleeping.
708 const int ret = sys_futex_wait(FUTEX_LOCK_PI, &m->futex, 1, timeout);
709 if (ret != 0) {
710 if (timeout != NULL && ret == -ETIMEDOUT) {
711 return 3;
712 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400713 if (__builtin_expect(ret == -EINTR, true)) {
Brian Silvermandc1eb272014-08-19 14:25:59 -0400714 if (signals_fail) {
715 return 2;
716 } else {
717 continue;
718 }
719 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400720 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700721 CHECK_NE(ret, -EDEADLK) << ": multiple lock of " << m << " by " << tid;
722
723 errno = -ret;
724 PLOG(FATAL) << "FUTEX_LOCK_PI(" << &m->futex
725 << "(=" << __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST)
726 << "), 1, " << timeout << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400727 } else {
Brian Silverman71c55c52014-08-19 14:31:59 -0400728 if (kLockDebug) {
729 printf("%" PRId32 ": %p kernel lock done\n", tid, m);
730 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400731 // The kernel already handled setting the value to our TID (ish).
732 break;
733 }
734 } else {
Brian Silverman71c55c52014-08-19 14:31:59 -0400735 if (kLockDebug) {
736 printf("%" PRId32 ": %p fast lock done\n", tid, m);
737 }
738 lock_pthread_mutex(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400739 // Fastpath succeeded, so no need to call into the kernel.
Brian Silverman71c55c52014-08-19 14:31:59 -0400740 // Because this is the fastpath, it's a good idea to avoid even having to
741 // load the value again down below.
742 return 0;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400743 }
744 }
745
Brian Silverman71c55c52014-08-19 14:31:59 -0400746 return mutex_finish_lock(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400747}
748
749// The common implementation for everything that wants to lock a mutex.
750// If signals_fail is false, the function will try again if the wait syscall is
751// interrupted by a signal.
752// timeout can be NULL for no timeout.
753inline int mutex_get(aos_mutex *m, bool signals_fail,
754 const struct timespec *timeout) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400755 const uint32_t tid = get_tid();
756 my_robust_list::Adder adder(m);
757 const int r = mutex_do_get(m, signals_fail, timeout, tid);
758 if (r == 0 || r == 1) adder.Add();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400759 return r;
760}
761
762// The common implementation for broadcast and signal.
763// number_requeue is the number of waiters to requeue (probably INT_MAX or 0). 1
764// will always be woken.
Brian Silverman71c55c52014-08-19 14:31:59 -0400765void condition_wake(aos_condition *c, aos_mutex *m, int number_requeue) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700766 RunShmObservers run_observers(c, true);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400767 // Make it so that anybody just going to sleep won't.
768 // This is where we might accidentally wake more than just 1 waiter with 1
769 // signal():
770 // 1 already sleeping will be woken but n might never actually make it to
771 // sleep in the kernel because of this.
Brian Silverman71c55c52014-08-19 14:31:59 -0400772 uint32_t new_value = __atomic_add_fetch(c, 1, __ATOMIC_SEQ_CST);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400773
Brian2a4294f2019-06-12 20:23:32 -0700774 while (true) {
775 // This really wants to be FUTEX_REQUEUE_PI, but the kernel doesn't have
776 // that... However, the code to support that is in the kernel, so it might
777 // be a good idea to patch it to support that and use it iff it's there.
778 const int ret =
779 sys_futex_cmp_requeue_pi(c, 1, number_requeue, &m->futex, new_value);
780 if (ret < 0) {
781 // If the value got changed out from under us (aka somebody else did a
782 // condition_wake).
783 if (__builtin_expect(ret == -EAGAIN, true)) {
784 // If we're doing a broadcast, the other guy might have done a signal
785 // instead, so we have to try again.
786 // If we're doing a signal, we have to go again to make sure that 2
787 // signals wake 2 processes.
788 new_value = __atomic_load_n(c, __ATOMIC_RELAXED);
789 continue;
Brian Silverman71c55c52014-08-19 14:31:59 -0400790 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400791 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700792 errno = -ret;
793 PLOG(FATAL) << "FUTEX_CMP_REQUEUE_PI(" << c << ", 1, " << number_requeue
794 << ", " << &m->futex << ", *" << c << ") failed";
Brian2a4294f2019-06-12 20:23:32 -0700795 } else {
796 return;
Brian Silverman71c55c52014-08-19 14:31:59 -0400797 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400798 }
799}
800
801} // namespace
802
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700803int mutex_lock(aos_mutex *m) { return mutex_get(m, true, NULL); }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400804int mutex_lock_timeout(aos_mutex *m, const struct timespec *timeout) {
805 return mutex_get(m, true, timeout);
806}
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700807int mutex_grab(aos_mutex *m) { return mutex_get(m, false, NULL); }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400808
809void mutex_unlock(aos_mutex *m) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700810 RunShmObservers run_observers(m, true);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400811 const uint32_t tid = get_tid();
Brian Silverman71c55c52014-08-19 14:31:59 -0400812 if (kPrintOperations) {
813 printf("%" PRId32 ": %p unlock\n", tid, m);
814 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400815
816 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST);
Brian Silverman71c55c52014-08-19 14:31:59 -0400817 if (__builtin_expect((value & FUTEX_TID_MASK) != tid, false)) {
818 my_robust_list::robust_head.pending_next = 0;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400819 check_cached_tid(tid);
820 if ((value & FUTEX_TID_MASK) == 0) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700821 LOG(FATAL) << "multiple unlock of aos_mutex " << m << " by " << tid;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400822 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700823 LOG(FATAL) << "aos_mutex " << m << " is locked by "
824 << (value & FUTEX_TID_MASK) << ", not " << tid;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400825 }
826 }
827
Brian Silverman71c55c52014-08-19 14:31:59 -0400828 my_robust_list::Remover remover(m);
829 unlock_pthread_mutex(m);
830
Brian Silvermandc1eb272014-08-19 14:25:59 -0400831 // If the atomic TID->0 transition fails (ie FUTEX_WAITERS is set),
832 if (!compare_and_swap(&m->futex, tid, 0)) {
833 // The kernel handles everything else.
834 const int ret = sys_futex_unlock_pi(&m->futex);
835 if (ret != 0) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400836 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700837 errno = -ret;
838 PLOG(FATAL) << "FUTEX_UNLOCK_PI(" << (&m->futex) << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400839 }
840 } else {
841 // There aren't any waiters, so no need to call into the kernel.
842 }
843}
844
845int mutex_trylock(aos_mutex *m) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700846 RunShmObservers run_observers(m, true);
Brian Silverman71c55c52014-08-19 14:31:59 -0400847 const uint32_t tid = get_tid();
848 if (kPrintOperations) {
849 printf("%" PRId32 ": %p trylock\n", tid, m);
850 }
851 my_robust_list::Adder adder(m);
852
Brian Silvermandc1eb272014-08-19 14:25:59 -0400853 // Try an atomic 0->TID transition.
Brian Silverman71c55c52014-08-19 14:31:59 -0400854 uint32_t c = compare_and_swap_val(&m->futex, 0, tid);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400855
856 if (c != 0) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400857 if (__builtin_expect((c & FUTEX_OWNER_DIED) == 0, true)) {
858 // Somebody else had it locked; we failed.
859 return 4;
860 } else {
861 // FUTEX_OWNER_DIED was set, so we have to call into the kernel to deal
862 // with resetting it.
863 const int ret = sys_futex_wait(FUTEX_TRYLOCK_PI, &m->futex, 0, NULL);
864 if (ret == 0) {
865 adder.Add();
866 // Only clear the owner died if somebody else didn't do the recovery
867 // and then unlock before our TRYLOCK happened.
868 return mutex_finish_lock(m);
869 } else {
870 // EWOULDBLOCK means that somebody else beat us to it.
871 if (__builtin_expect(ret == -EWOULDBLOCK, true)) {
872 return 4;
873 }
874 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700875 errno = -ret;
876 PLOG(FATAL) << "FUTEX_TRYLOCK_PI(" << (&m->futex)
877 << ", 0, NULL) failed";
Brian Silverman71c55c52014-08-19 14:31:59 -0400878 }
879 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400880 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400881
882 lock_pthread_mutex(m);
883 adder.Add();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400884 return 0;
885}
886
887bool mutex_islocked(const aos_mutex *m) {
888 const uint32_t tid = get_tid();
889
890 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_RELAXED);
891 return (value & FUTEX_TID_MASK) == tid;
892}
893
Brian Silverman27af1f62019-11-18 12:04:48 -0800894void death_notification_init(aos_mutex *m) {
895 const uint32_t tid = get_tid();
896 if (kPrintOperations) {
897 printf("%" PRId32 ": %p death_notification start\n", tid, m);
898 }
899 my_robust_list::Adder adder(m);
900 {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700901 RunShmObservers run_observers(m, true);
Brian Silverman27af1f62019-11-18 12:04:48 -0800902 CHECK(compare_and_swap(&m->futex, 0, tid));
903 }
904 adder.Add();
905}
906
907void death_notification_release(aos_mutex *m) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700908 RunShmObservers run_observers(m, true);
Brian Silverman27af1f62019-11-18 12:04:48 -0800909
910#ifndef NDEBUG
911 // Verify it's "locked", like it should be.
912 {
913 const uint32_t tid = get_tid();
914 if (kPrintOperations) {
915 printf("%" PRId32 ": %p death_notification release\n", tid, m);
916 }
917 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST);
918 assert((value & ~FUTEX_WAITERS) == tid);
919 }
920#endif
921
922 my_robust_list::Remover remover(m);
923 ANNOTATE_HAPPENS_BEFORE(m);
924 const int ret = sys_futex_unlock_pi(&m->futex);
925 if (ret != 0) {
926 my_robust_list::robust_head.pending_next = 0;
927 errno = -ret;
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700928 PLOG(FATAL) << "FUTEX_UNLOCK_PI(" << &m->futex << ") failed";
Brian Silverman27af1f62019-11-18 12:04:48 -0800929 }
930}
931
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700932int condition_wait(aos_condition *c, aos_mutex *m, struct timespec *end_time) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700933 RunShmObservers run_observers(c, false);
Brian Silverman71c55c52014-08-19 14:31:59 -0400934 const uint32_t tid = get_tid();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400935 const uint32_t wait_start = __atomic_load_n(c, __ATOMIC_SEQ_CST);
936
937 mutex_unlock(m);
938
Brian Silverman71c55c52014-08-19 14:31:59 -0400939 my_robust_list::Adder adder(m);
940
Brian Silvermandc1eb272014-08-19 14:25:59 -0400941 while (true) {
942 // Wait in the kernel iff the value of it doesn't change (ie somebody else
943 // does a wake) from before we unlocked the mutex.
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700944 int ret = sys_futex_wait_requeue_pi(c, wait_start, end_time, &m->futex);
945
Brian Silvermandc1eb272014-08-19 14:25:59 -0400946 if (ret != 0) {
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700947 // Timed out waiting. Signal that back up to the user.
948 if (__builtin_expect(ret == -ETIMEDOUT, true)) {
949 // We have to relock it ourself because the kernel didn't do it.
950 const int r = mutex_do_get(m, false, nullptr, tid);
951 assert(__builtin_expect(r == 0 || r == 1, true));
952 adder.Add();
953
954 // OWNER_DIED takes priority. Pass it on if we found it.
955 if (r == 1) return r;
956 // Otherwise communicate that we were interrupted.
957 return -1;
958 }
959
Brian Silvermandc1eb272014-08-19 14:25:59 -0400960 // If it failed because somebody else did a wake and changed the value
961 // before we actually made it to sleep.
Brian Silverman71c55c52014-08-19 14:31:59 -0400962 if (__builtin_expect(ret == -EAGAIN, true)) {
963 // There's no need to unconditionally set FUTEX_WAITERS here if we're
964 // using REQUEUE_PI because the kernel automatically does that in the
965 // REQUEUE_PI iff it requeued anybody.
966 // If we're not using REQUEUE_PI, then everything is just normal locks
967 // etc, so there's no need to do anything special there either.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400968
969 // We have to relock it ourself because the kernel didn't do it.
Brian Silverman71c55c52014-08-19 14:31:59 -0400970 const int r = mutex_do_get(m, false, nullptr, tid);
971 assert(__builtin_expect(r == 0 || r == 1, true));
972 adder.Add();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400973 return r;
974 }
975 // Try again if it was because of a signal.
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700976 if (__builtin_expect((ret == -EINTR), true)) {
977 continue;
978 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400979 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700980 errno = -ret;
981 PLOG(FATAL) << "FUTEX_WAIT_REQUEUE_PI(" << c << ", " << wait_start << ", "
982 << (&m->futex) << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400983 } else {
Brian2a4294f2019-06-12 20:23:32 -0700984 // Record that the kernel relocked it for us.
985 lock_pthread_mutex(m);
Brian Silverman71c55c52014-08-19 14:31:59 -0400986
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700987 // We succeeded in waiting, and the kernel took care of locking the
988 // mutex
Brian Silverman71c55c52014-08-19 14:31:59 -0400989 // for us and setting FUTEX_WAITERS iff it needed to (for REQUEUE_PI).
990
991 adder.Add();
992
993 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_RELAXED);
994 if (__builtin_expect((value & FUTEX_OWNER_DIED) != 0, false)) {
995 __atomic_and_fetch(&m->futex, ~FUTEX_OWNER_DIED, __ATOMIC_RELAXED);
996 return 1;
997 } else {
998 return 0;
999 }
Brian Silvermandc1eb272014-08-19 14:25:59 -04001000 }
1001 }
1002}
1003
1004void condition_signal(aos_condition *c, aos_mutex *m) {
1005 condition_wake(c, m, 0);
1006}
1007
1008void condition_broadcast(aos_condition *c, aos_mutex *m) {
1009 condition_wake(c, m, INT_MAX);
1010}
1011
1012int futex_wait_timeout(aos_futex *m, const struct timespec *timeout) {
Brian Silverman1ed5df52021-09-13 20:14:06 -07001013 RunShmObservers run_observers(m, false);
Brian Silvermandc1eb272014-08-19 14:25:59 -04001014 const int ret = sys_futex_wait(FUTEX_WAIT, m, 0, timeout);
1015 if (ret != 0) {
1016 if (ret == -EINTR) {
1017 return 1;
1018 } else if (ret == -ETIMEDOUT) {
1019 return 2;
1020 } else if (ret != -EWOULDBLOCK) {
1021 errno = -ret;
1022 return -1;
1023 }
1024 }
Brian Silverman71c55c52014-08-19 14:31:59 -04001025 ANNOTATE_HAPPENS_AFTER(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -04001026 return 0;
1027}
1028
1029int futex_wait(aos_futex *m) { return futex_wait_timeout(m, NULL); }
1030
1031int futex_set_value(aos_futex *m, uint32_t value) {
Brian Silverman1ed5df52021-09-13 20:14:06 -07001032 RunShmObservers run_observers(m, false);
Brian Silverman71c55c52014-08-19 14:31:59 -04001033 ANNOTATE_HAPPENS_BEFORE(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -04001034 __atomic_store_n(m, value, __ATOMIC_SEQ_CST);
1035 const int r = sys_futex_wake(m, INT_MAX - 4096);
1036 if (__builtin_expect(
Brian Silverman71c55c52014-08-19 14:31:59 -04001037 static_cast<unsigned int>(r) > static_cast<unsigned int>(-4096),
1038 false)) {
Brian Silvermandc1eb272014-08-19 14:25:59 -04001039 errno = -r;
1040 return -1;
1041 } else {
1042 return r;
1043 }
1044}
1045
Tyler Chatowbf0609c2021-07-31 16:13:27 -07001046int futex_set(aos_futex *m) { return futex_set_value(m, 1); }
Brian Silvermandc1eb272014-08-19 14:25:59 -04001047
1048int futex_unset(aos_futex *m) {
1049 return !__atomic_exchange_n(m, 0, __ATOMIC_SEQ_CST);
1050}
Brian Silverman71c55c52014-08-19 14:31:59 -04001051
Stephan Pleinesf63bde82024-01-13 15:59:33 -08001052namespace aos::linux_code::ipc_lib {
Brian Silverman71c55c52014-08-19 14:31:59 -04001053
Brian Silverman71c55c52014-08-19 14:31:59 -04001054// Sets an extra offset between mutexes and the value we use for them in the
1055// robust list (only the forward pointers). This is used to work around a kernel
1056// bug by keeping a second set of mutexes which is always writable so the kernel
1057// won't go into an infinite loop when trying to unlock them.
1058void SetRobustListOffset(ptrdiff_t offset) {
1059 my_robust_list::SetRobustListOffset(offset);
1060}
1061
1062// Returns true iff there are any mutexes locked by the current thread.
1063// This is mainly useful for testing.
Tyler Chatowbf0609c2021-07-31 16:13:27 -07001064bool HaveLockedMutexes() { return my_robust_list::HaveLockedMutexes(); }
Brian Silverman71c55c52014-08-19 14:31:59 -04001065
Stephan Pleinesf63bde82024-01-13 15:59:33 -08001066} // namespace aos::linux_code::ipc_lib