blob: 56424343f2fe71b3dd7813481b2d93efbbe3930f [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"
Austin Schuh99f7c6a2024-06-25 22:07:44 -070028#include "absl/log/check.h"
29#include "absl/log/log.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070030
Brian Silvermanb47f5552020-10-01 15:08:14 -070031#include "aos/macros.h"
Brian Silvermanb47f5552020-10-01 15:08:14 -070032#include "aos/util/compiler_memory_barrier.h"
33
Brian Silverman1ed5df52021-09-13 20:14:06 -070034using ::aos::linux_code::ipc_lib::RunShmObservers;
Brian Silvermandc1eb272014-08-19 14:25:59 -040035
Tyler Chatowbf0609c2021-07-31 16:13:27 -070036// This code was originally based on
37// <https://www.akkadia.org/drepper/futex.pdf>, but is has since evolved a lot.
38// However, that still has useful information.
Brian Silvermandc1eb272014-08-19 14:25:59 -040039//
40// Finding information about actually using futexes is really REALLY hard, so
41// here's a list of the stuff that I've used:
42// futex(7) has a really high-level overview.
43// <http://locklessinc.com/articles/futex_cheat_sheet/> describes some of the
44// operations in a bit more detail than most places.
45// <http://locklessinc.com/articles/mutex_cv_futex/> is the basis of our
46// implementations (before PI).
47// <http://lwn.net/Articles/360699/> has a nice overview of futexes in late 2009
48// (fairly recent compared to everything else...).
49// <https://www.kernel.org/doc/Documentation/pi-futex.txt>,
50// <https://www.kernel.org/doc/Documentation/futex-requeue-pi.txt>,
51// <https://www.kernel.org/doc/Documentation/robust-futexes.txt>,
52// and <https://www.kernel.org/doc/Documentation/robust-futex-ABI.txt> are all
53// useful references.
54// The kernel source (kernel/futex.c) has some useful comments about what the
55// various operations do (except figuring out which argument goes where in the
56// syscall is still confusing).
57// futex(2) is basically useless except for describing the order of the
58// arguments (it only has high-level descriptions of what some of the
59// operations do, and some of them are wrong in Wheezy).
60// glibc's nptl pthreads implementation is the intended user of most of these
61// things, so it is also a good place to look for examples. However, it is all
62// very hard to read because it supports ~20 different kinds of mutexes and
63// several variations of condition variables, and some of the pieces of code
64// are only written in assembly.
65// set_robust_list(2) is wrong in Wheezy (it doesn't actually take a TID
66// argument).
67//
68// Can't use PRIVATE futex operations because they use the pid (or something) as
69// part of the hash.
70//
71// ThreadSanitizer understands how these mutexes etc work. It appears to be able
72// to figure out the happens-before relationship from the __ATOMIC_SEQ_CST
73// atomic primitives.
74//
75// Remember that EAGAIN and EWOUDBLOCK are the same! (ie if you get EAGAIN from
76// FUTEX_WAIT, the docs call it EWOULDBLOCK...)
77
78// Values for an aos_mutex.futex (kernel-mandated):
79// 0 = unlocked
80// TID = locked, not contended
81// |FUTEX_WAITERS = there are waiters (aka contended)
Brian Silverman71c55c52014-08-19 14:31:59 -040082// |FUTEX_OWNER_DIED = old owner died
Brian Silvermandc1eb272014-08-19 14:25:59 -040083//
84// Values for an aos_futex being used directly:
85// 0 = unset
86// 1 = set
87//
88// The value of an aos_condition is just a generation counter.
89
Brian Silverman71c55c52014-08-19 14:31:59 -040090#ifdef AOS_SANITIZER_thread
91extern "C" void AnnotateHappensBefore(const char *file, int line,
92 uintptr_t addr);
93extern "C" void AnnotateHappensAfter(const char *file, int line,
94 uintptr_t addr);
95#define ANNOTATE_HAPPENS_BEFORE(address) \
96 AnnotateHappensBefore(__FILE__, __LINE__, \
97 reinterpret_cast<uintptr_t>(address))
98#define ANNOTATE_HAPPENS_AFTER(address) \
99 AnnotateHappensAfter(__FILE__, __LINE__, reinterpret_cast<uintptr_t>(address))
100#else
101#define ANNOTATE_HAPPENS_BEFORE(address)
102#define ANNOTATE_HAPPENS_AFTER(address)
103#endif
104
Brian Silvermandc1eb272014-08-19 14:25:59 -0400105namespace {
106
Brian Silverman71c55c52014-08-19 14:31:59 -0400107const bool kRobustListDebug = false;
108const bool kLockDebug = false;
109const bool kPrintOperations = false;
110
Brian Silvermandc1eb272014-08-19 14:25:59 -0400111// These sys_futex_* functions are wrappers around syscall(SYS_futex). They each
112// take a specific set of arguments for a given futex operation. They return the
113// result or a negated errno value. -1..-4095 mean errors and not successful
114// results, which is guaranteed by the kernel.
115//
Brian Silverman0b58a612021-09-13 15:32:29 -0700116// They each have optimized versions for some architectures which don't go
117// through syscall(2) or errno. These use register variables to get the values
118// in the right registers to actually make the syscall.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400119
Brian Silverman0b58a612021-09-13 15:32:29 -0700120// The actual macros that we key off of to use the inline versions or not.
Brian Silverman17426d92018-08-09 11:38:49 -0700121#if defined(__ARM_EABI__)
Brian Silverman0b58a612021-09-13 15:32:29 -0700122// The syscall interface is different for non-EABI ARM, so we test specifically
123// for EABI.
Brian Silverman17426d92018-08-09 11:38:49 -0700124#define ARM_EABI_INLINE_SYSCALL 1
Brian Silverman0b58a612021-09-13 15:32:29 -0700125#define AARCH64_INLINE_SYSCALL 0
126#elif defined(__aarch64__)
127// Linux only has one supported syscall ABI on aarch64, which is the one we
128// support.
129#define ARM_EABI_INLINE_SYSCALL 0
130#define AARCH64_INLINE_SYSCALL 1
Brian Silverman17426d92018-08-09 11:38:49 -0700131#else
132#define ARM_EABI_INLINE_SYSCALL 0
Brian Silverman0b58a612021-09-13 15:32:29 -0700133#define AARCH64_INLINE_SYSCALL 0
Brian Silverman17426d92018-08-09 11:38:49 -0700134#endif
Brian Silvermandc1eb272014-08-19 14:25:59 -0400135
136// Used for FUTEX_WAIT, FUTEX_LOCK_PI, and FUTEX_TRYLOCK_PI.
137inline int sys_futex_wait(int op, aos_futex *addr1, int val1,
138 const struct timespec *timeout) {
139#if ARM_EABI_INLINE_SYSCALL
140 register aos_futex *addr1_reg __asm__("r0") = addr1;
141 register int op_reg __asm__("r1") = op;
142 register int val1_reg __asm__("r2") = val1;
143 register const struct timespec *timeout_reg __asm__("r3") = timeout;
144 register int syscall_number __asm__("r7") = SYS_futex;
145 register int result __asm__("r0");
146 __asm__ volatile("swi #0"
147 : "=r"(result)
148 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
149 "r"(timeout_reg), "r"(syscall_number)
150 : "memory");
151 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700152#elif AARCH64_INLINE_SYSCALL
153 register aos_futex *addr1_reg __asm__("x0") = addr1;
154 register int op_reg __asm__("x1") = op;
155 register int val1_reg __asm__("x2") = val1;
156 register const struct timespec *timeout_reg __asm__("x3") = timeout;
157 register int syscall_number __asm__("x8") = SYS_futex;
158 register int result __asm__("x0");
159 __asm__ volatile("svc #0"
160 : "=r"(result)
161 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
162 "r"(timeout_reg), "r"(syscall_number)
163 : "memory");
164 return result;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400165#else
166 const int r = syscall(SYS_futex, addr1, op, val1, timeout);
167 if (r == -1) return -errno;
168 return r;
169#endif
170}
171
172inline int sys_futex_wake(aos_futex *addr1, int val1) {
173#if ARM_EABI_INLINE_SYSCALL
174 register aos_futex *addr1_reg __asm__("r0") = addr1;
175 register int op_reg __asm__("r1") = FUTEX_WAKE;
176 register int val1_reg __asm__("r2") = val1;
177 register int syscall_number __asm__("r7") = SYS_futex;
178 register int result __asm__("r0");
179 __asm__ volatile("swi #0"
180 : "=r"(result)
181 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
182 "r"(syscall_number)
183 : "memory");
184 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700185#elif AARCH64_INLINE_SYSCALL
186 register aos_futex *addr1_reg __asm__("x0") = addr1;
187 register int op_reg __asm__("x1") = FUTEX_WAKE;
188 register int val1_reg __asm__("x2") = val1;
189 register int syscall_number __asm__("x8") = SYS_futex;
190 register int result __asm__("x0");
191 __asm__ volatile("svc #0"
192 : "=r"(result)
193 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
194 "r"(syscall_number)
195 : "memory");
196 return result;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400197#else
198 const int r = syscall(SYS_futex, addr1, FUTEX_WAKE, val1);
199 if (r == -1) return -errno;
200 return r;
201#endif
202}
203
Brian Silverman71c55c52014-08-19 14:31:59 -0400204inline int sys_futex_cmp_requeue_pi(aos_futex *addr1, int num_wake,
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700205 int num_requeue, aos_futex *m,
206 uint32_t val) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400207#if ARM_EABI_INLINE_SYSCALL
208 register aos_futex *addr1_reg __asm__("r0") = addr1;
209 register int op_reg __asm__("r1") = FUTEX_CMP_REQUEUE_PI;
210 register int num_wake_reg __asm__("r2") = num_wake;
211 register int num_requeue_reg __asm__("r3") = num_requeue;
212 register aos_futex *m_reg __asm__("r4") = m;
213 register uint32_t val_reg __asm__("r5") = val;
214 register int syscall_number __asm__("r7") = SYS_futex;
215 register int result __asm__("r0");
216 __asm__ volatile("swi #0"
217 : "=r"(result)
218 : "r"(addr1_reg), "r"(op_reg), "r"(num_wake_reg),
219 "r"(num_requeue_reg), "r"(m_reg), "r"(val_reg),
220 "r"(syscall_number)
221 : "memory");
222 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700223#elif AARCH64_INLINE_SYSCALL
224 register aos_futex *addr1_reg __asm__("x0") = addr1;
225 register int op_reg __asm__("x1") = FUTEX_CMP_REQUEUE_PI;
226 register int num_wake_reg __asm__("x2") = num_wake;
227 register int num_requeue_reg __asm__("x3") = num_requeue;
228 register aos_futex *m_reg __asm__("x4") = m;
229 register uint32_t val_reg __asm__("x5") = val;
230 register int syscall_number __asm__("x8") = SYS_futex;
231 register int result __asm__("x0");
232 __asm__ volatile("svc #0"
233 : "=r"(result)
234 : "r"(addr1_reg), "r"(op_reg), "r"(num_wake_reg),
235 "r"(num_requeue_reg), "r"(m_reg), "r"(val_reg),
236 "r"(syscall_number)
237 : "memory");
238 return result;
Brian Silverman71c55c52014-08-19 14:31:59 -0400239#else
240 const int r = syscall(SYS_futex, addr1, FUTEX_CMP_REQUEUE_PI, num_wake,
241 num_requeue, m, val);
242 if (r == -1) return -errno;
243 return r;
244#endif
245}
246
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700247inline int sys_futex_wait_requeue_pi(aos_condition *addr1, uint32_t start_val,
Brian Silverman71c55c52014-08-19 14:31:59 -0400248 const struct timespec *timeout,
249 aos_futex *m) {
250#if ARM_EABI_INLINE_SYSCALL
251 register aos_condition *addr1_reg __asm__("r0") = addr1;
252 register int op_reg __asm__("r1") = FUTEX_WAIT_REQUEUE_PI;
253 register uint32_t start_val_reg __asm__("r2") = start_val;
254 register const struct timespec *timeout_reg __asm__("r3") = timeout;
255 register aos_futex *m_reg __asm__("r4") = m;
256 register int syscall_number __asm__("r7") = SYS_futex;
257 register int result __asm__("r0");
258 __asm__ volatile("swi #0"
259 : "=r"(result)
260 : "r"(addr1_reg), "r"(op_reg), "r"(start_val_reg),
261 "r"(timeout_reg), "r"(m_reg), "r"(syscall_number)
262 : "memory");
263 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700264#elif AARCH64_INLINE_SYSCALL
265 register aos_condition *addr1_reg __asm__("x0") = addr1;
266 register int op_reg __asm__("x1") = FUTEX_WAIT_REQUEUE_PI;
267 register uint32_t start_val_reg __asm__("x2") = start_val;
268 register const struct timespec *timeout_reg __asm__("x3") = timeout;
269 register aos_futex *m_reg __asm__("x4") = m;
270 register int syscall_number __asm__("x8") = SYS_futex;
271 register int result __asm__("x0");
272 __asm__ volatile("svc #0"
273 : "=r"(result)
274 : "r"(addr1_reg), "r"(op_reg), "r"(start_val_reg),
275 "r"(timeout_reg), "r"(m_reg), "r"(syscall_number)
276 : "memory");
277 return result;
Brian Silverman71c55c52014-08-19 14:31:59 -0400278#else
279 const int r =
280 syscall(SYS_futex, addr1, FUTEX_WAIT_REQUEUE_PI, start_val, timeout, m);
281 if (r == -1) return -errno;
282 return r;
283#endif
284}
285
Brian Silvermandc1eb272014-08-19 14:25:59 -0400286inline int sys_futex_unlock_pi(aos_futex *addr1) {
287#if ARM_EABI_INLINE_SYSCALL
288 register aos_futex *addr1_reg __asm__("r0") = addr1;
289 register int op_reg __asm__("r1") = FUTEX_UNLOCK_PI;
290 register int syscall_number __asm__("r7") = SYS_futex;
291 register int result __asm__("r0");
292 __asm__ volatile("swi #0"
293 : "=r"(result)
294 : "r"(addr1_reg), "r"(op_reg), "r"(syscall_number)
295 : "memory");
296 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700297#elif AARCH64_INLINE_SYSCALL
298 register aos_futex *addr1_reg __asm__("x0") = addr1;
299 register int op_reg __asm__("x1") = FUTEX_UNLOCK_PI;
300 register int syscall_number __asm__("x8") = SYS_futex;
301 register int result __asm__("x0");
302 __asm__ volatile("svc #0"
303 : "=r"(result)
304 : "r"(addr1_reg), "r"(op_reg), "r"(syscall_number)
305 : "memory");
306 return result;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400307#else
308 const int r = syscall(SYS_futex, addr1, FUTEX_UNLOCK_PI);
309 if (r == -1) return -errno;
310 return r;
311#endif
312}
313
Brian Silverman71c55c52014-08-19 14:31:59 -0400314// Returns the previous value of f.
315inline uint32_t compare_and_swap_val(aos_futex *f, uint32_t before,
316 uint32_t after) {
317#ifdef AOS_SANITIZER_thread
318 // This is a workaround for <https://llvm.org/bugs/show_bug.cgi?id=23176>.
319 // Basically, most of the atomic operations are broken under tsan, but this
320 // particular one isn't.
321 // TODO(Brian): Remove this #ifdef (and the one in compare_and_swap) once we
322 // don't have to worry about tsan with this bug any more.
323 uint32_t before_value = before;
324 __tsan_atomic32_compare_exchange_strong(
325 reinterpret_cast<int32_t *>(f),
326 reinterpret_cast<int32_t *>(&before_value), after,
327 __tsan_memory_order_seq_cst, __tsan_memory_order_seq_cst);
328 return before_value;
329#else
330 return __sync_val_compare_and_swap(f, before, after);
331#endif
Brian Silvermandc1eb272014-08-19 14:25:59 -0400332}
333
Brian Silverman71c55c52014-08-19 14:31:59 -0400334// Returns true if it succeeds and false if it fails.
335inline bool compare_and_swap(aos_futex *f, uint32_t before, uint32_t after) {
336#ifdef AOS_SANITIZER_thread
337 return compare_and_swap_val(f, before, after) == before;
338#else
339 return __sync_bool_compare_and_swap(f, before, after);
340#endif
341}
342
343#ifdef AOS_SANITIZER_thread
344
345// Simple macro for checking something which should always be true.
346// Using the standard CHECK macro isn't safe because failures often result in
347// reentering the mutex locking code, which doesn't work.
348#define SIMPLE_CHECK(expr) \
349 do { \
350 if (!(expr)) { \
351 fprintf(stderr, "%s: %d: SIMPLE_CHECK(" #expr ") failed!\n", __FILE__, \
352 __LINE__); \
353 abort(); \
354 } \
355 } while (false)
356
357// Forcibly initializes the pthread mutex for *m.
358// This sequence of operations is only safe for the simpler kinds of mutexes in
359// glibc's pthreads implementation on Linux.
360void init_pthread_mutex(aos_mutex *m) {
361 // Re-initialize the mutex so the destroy won't fail if it's locked.
362 // tsan ignores this.
363 SIMPLE_CHECK(0 == pthread_mutex_init(&m->pthread_mutex, nullptr));
364 // Destroy the mutex so tsan will forget about it if some now-dead thread
365 // locked it.
366 SIMPLE_CHECK(0 == pthread_mutex_destroy(&m->pthread_mutex));
367
368 // Now actually initialize it, making sure it's process-shareable so it works
369 // correctly across shared memory.
370 pthread_mutexattr_t attr;
371 SIMPLE_CHECK(0 == pthread_mutexattr_init(&attr));
372 SIMPLE_CHECK(0 == pthread_mutexattr_setpshared(&attr, true));
373 SIMPLE_CHECK(0 == pthread_mutex_init(&m->pthread_mutex, &attr));
374 SIMPLE_CHECK(0 == pthread_mutexattr_destroy(&attr));
375}
376
377// Locks the pthread mutex for *m.
378// If a stack trace ever reveals the pthread_mutex_lock call in here blocking,
379// there is a bug in our mutex code or the way somebody is calling it.
380void lock_pthread_mutex(aos_mutex *m) {
381 if (!m->pthread_mutex_init) {
382 init_pthread_mutex(m);
383 m->pthread_mutex_init = true;
384 }
385 SIMPLE_CHECK(0 == pthread_mutex_lock(&m->pthread_mutex));
386}
387
388// Forcibly locks the pthread mutex for *m.
389// This will (somewhat hackily) rip the lock out from underneath somebody else
390// who is already holding it.
391void force_lock_pthread_mutex(aos_mutex *m) {
392 if (!m->pthread_mutex_init) {
393 init_pthread_mutex(m);
394 m->pthread_mutex_init = true;
395 }
396 const int trylock_result = pthread_mutex_trylock(&m->pthread_mutex);
397 SIMPLE_CHECK(trylock_result == 0 || trylock_result == EBUSY);
398 if (trylock_result == 0) {
399 // We're good, so unlock it and then go for a real lock down below.
400 SIMPLE_CHECK(0 == pthread_mutex_unlock(&m->pthread_mutex));
401 } else {
402 // Somebody (should always be somebody else who died with it held) already
403 // has it, so make tsan forget about that.
404 init_pthread_mutex(m);
405 }
406 lock_pthread_mutex(m);
407}
408
409// Unlocks the pthread mutex for *m.
410void unlock_pthread_mutex(aos_mutex *m) {
411 assert(m->pthread_mutex_init);
412 SIMPLE_CHECK(0 == pthread_mutex_unlock(&m->pthread_mutex));
413}
414
415#else
416
417// Empty implementations of all these so the code below doesn't need #ifdefs.
418static inline void lock_pthread_mutex(aos_mutex *) {}
419static inline void force_lock_pthread_mutex(aos_mutex *) {}
420static inline void unlock_pthread_mutex(aos_mutex *) {}
421
422#endif
423
Brian Silvermandc1eb272014-08-19 14:25:59 -0400424pid_t do_get_tid() {
425 pid_t r = syscall(SYS_gettid);
426 assert(r > 0);
427 return r;
428}
429
Alex Perrycb7da4b2019-08-28 19:35:56 -0700430// This gets called by functions before LOG(FATAL)ing with error messages
Austin Schuhf257f3c2019-10-27 21:00:43 -0700431// that would be incorrect if the error was caused by a process forking without
Brian Silvermandc1eb272014-08-19 14:25:59 -0400432// initialize_in_new_thread getting called in the fork.
433void check_cached_tid(pid_t tid) {
434 pid_t actual = do_get_tid();
435 if (tid != actual) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700436 LOG(FATAL) << "task " << static_cast<intmax_t>(tid) << " forked into "
437 << static_cast<intmax_t>(actual)
438 << " without letting aos_sync know so we're not really sure "
439 "what's going on";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400440 }
441}
442
443// Starts off at 0 in each new thread (because that's what it gets initialized
444// 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 -0700445thread_local pid_t my_tid = 0;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400446
447// Gets called before the fork(2) wrapper function returns in the child.
448void atfork_child() {
449 // The next time get_tid() is called, it will set everything up again.
450 my_tid = 0;
451}
452
John Park0e699502019-11-20 19:36:05 -0800453void InstallAtforkHook() {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700454 PCHECK(pthread_atfork(NULL, NULL, &atfork_child) == 0)
455 << ": pthread_atfork(NULL, NULL, "
456 << reinterpret_cast<void *>(&atfork_child) << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400457}
458
459// This gets called to set everything up in a new thread by get_tid().
460void initialize_in_new_thread();
461
462// Gets the current thread's TID and does all of the 1-time initialization the
463// first time it's called in a given thread.
464inline uint32_t get_tid() {
Brian Silverman71c55c52014-08-19 14:31:59 -0400465 if (__builtin_expect(my_tid == 0, false)) {
Brian Silvermandc1eb272014-08-19 14:25:59 -0400466 initialize_in_new_thread();
467 }
468 static_assert(sizeof(my_tid) <= sizeof(uint32_t), "pid_t is too big");
469 return static_cast<uint32_t>(my_tid);
470}
471
Brian Silverman71c55c52014-08-19 14:31:59 -0400472// Contains all of the stuff for dealing with the robust list. Nothing outside
473// this namespace should touch anything inside it except Init, Adder, and
474// Remover.
475namespace my_robust_list {
476
477static_assert(offsetof(aos_mutex, next) == 0,
478 "Our math all assumes that the beginning of a mutex and its next "
479 "pointer are at the same place in memory.");
480
481// Our version of robust_list_head.
482// This is copied from the kernel header because that's a pretty stable ABI (and
483// any changes will be backwards compatible anyways) and we want ours to have
484// different types.
485// The uintptr_ts are &next of the elements in the list (with stuff |ed in).
486struct aos_robust_list_head {
487 uintptr_t next;
488 long futex_offset;
489 uintptr_t pending_next;
490};
491
492static_assert(offsetof(aos_robust_list_head, next) ==
493 offsetof(robust_list_head, list),
494 "Our aos_robust_list_head doesn't match the kernel's");
495static_assert(offsetof(aos_robust_list_head, futex_offset) ==
496 offsetof(robust_list_head, futex_offset),
497 "Our aos_robust_list_head doesn't match the kernel's");
498static_assert(offsetof(aos_robust_list_head, pending_next) ==
499 offsetof(robust_list_head, list_op_pending),
500 "Our aos_robust_list_head doesn't match the kernel's");
501static_assert(sizeof(aos_robust_list_head) == sizeof(robust_list_head),
502 "Our aos_robust_list_head doesn't match the kernel's");
503
Austin Schuhf7bfb652023-08-25 14:22:50 -0700504thread_local aos_robust_list_head robust_head;
Brian Silverman71c55c52014-08-19 14:31:59 -0400505
506// Extra offset between mutex values and where we point to for their robust list
507// entries (from SetRobustListOffset).
508uintptr_t robust_list_offset = 0;
509
510// The value to OR each pointer's value with whenever putting it into the robust
511// list (technically only if it's PI, but all of ours are, so...).
512static const uintptr_t kRobustListOr = 1;
513
514// Returns the value which goes into a next variable to represent the head.
515inline uintptr_t robust_head_next_value() {
516 return reinterpret_cast<uintptr_t>(&robust_head.next);
517}
518// Returns true iff next represents the head.
519inline bool next_is_head(uintptr_t next) {
520 return next == robust_head_next_value();
521}
522// Returns the (psuedo-)mutex corresponding to the head.
523// This does NOT have a previous pointer, so be careful with the return value.
524inline aos_mutex *robust_head_mutex() {
525 return reinterpret_cast<aos_mutex *>(robust_head_next_value());
526}
527
528inline uintptr_t mutex_to_next(aos_mutex *m) {
529 return (reinterpret_cast<uintptr_t>(&m->next) + robust_list_offset) |
530 kRobustListOr;
531}
532inline aos_mutex *next_to_mutex(uintptr_t next) {
533 if (__builtin_expect(robust_list_offset != 0, false) && next_is_head(next)) {
534 // We don't offset the head pointer, so be careful.
535 return reinterpret_cast<aos_mutex *>(next);
536 }
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700537 return reinterpret_cast<aos_mutex *>((next & ~kRobustListOr) -
538 robust_list_offset);
Brian Silverman71c55c52014-08-19 14:31:59 -0400539}
540
541// Sets up the robust list for each thread.
542void Init() {
543 // It starts out just pointing back to itself.
544 robust_head.next = robust_head_next_value();
545 robust_head.futex_offset = static_cast<ssize_t>(offsetof(aos_mutex, futex)) -
546 static_cast<ssize_t>(offsetof(aos_mutex, next));
547 robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700548 PCHECK(syscall(SYS_set_robust_list, robust_head_next_value(),
549 sizeof(robust_head)) == 0)
550 << ": set_robust_list(" << reinterpret_cast<void *>(robust_head.next)
551 << ", " << sizeof(robust_head) << ") failed";
Brian Silverman71c55c52014-08-19 14:31:59 -0400552 if (kRobustListDebug) {
553 printf("%" PRId32 ": init done\n", get_tid());
554 }
555}
556
557// Updating the offset with locked mutexes is important during robustness
558// testing, because there are mutexes which are locked before this is set to a
559// non-0 value and then unlocked after it is changed back. However, to make sure
560// the code works correctly when manipulating the next pointer of the last of
561// those mutexes, all of their next values have to be adjusted appropriately.
562void SetRobustListOffset(uintptr_t offset) {
563 const uintptr_t offset_change = offset - robust_list_offset;
564 robust_list_offset = offset;
565 aos_mutex *m = robust_head_mutex();
566 // Update the offset contained in each of the mutexes which is already locked.
567 while (!next_is_head(m->next)) {
568 m->next += offset_change;
569 m = next_to_mutex(m->next);
570 }
571}
572
573bool HaveLockedMutexes() {
574 return robust_head.next != robust_head_next_value();
575}
576
577// Handles adding a mutex to the robust list.
578// The idea is to create one of these at the beginning of a function that needs
579// to do this and then call Add() iff it should actually be added.
580class Adder {
581 public:
582 Adder(aos_mutex *m) : m_(m) {
583 assert(robust_head.pending_next == 0);
584 if (kRobustListDebug) {
585 printf("%" PRId32 ": maybe add %p\n", get_tid(), m_);
586 }
587 robust_head.pending_next = mutex_to_next(m);
588 aos_compiler_memory_barrier();
589 }
590 ~Adder() {
591 assert(robust_head.pending_next == mutex_to_next(m_));
592 if (kRobustListDebug) {
593 printf("%" PRId32 ": done maybe add %p, n=%p p=%p\n", get_tid(), m_,
594 next_to_mutex(m_->next), m_->previous);
595 }
596 aos_compiler_memory_barrier();
597 robust_head.pending_next = 0;
598 }
599
600 void Add() {
601 assert(robust_head.pending_next == mutex_to_next(m_));
602 if (kRobustListDebug) {
603 printf("%" PRId32 ": adding %p\n", get_tid(), m_);
604 }
605 const uintptr_t old_head_next_value = robust_head.next;
606
607 m_->next = old_head_next_value;
608 aos_compiler_memory_barrier();
609 robust_head.next = mutex_to_next(m_);
610
611 m_->previous = robust_head_mutex();
612 if (!next_is_head(old_head_next_value)) {
613 // robust_head's psuedo-mutex doesn't have a previous pointer to update.
614 next_to_mutex(old_head_next_value)->previous = m_;
615 }
616 aos_compiler_memory_barrier();
617 if (kRobustListDebug) {
618 printf("%" PRId32 ": done adding %p\n", get_tid(), m_);
619 }
620 }
621
622 private:
623 aos_mutex *const m_;
624
625 DISALLOW_COPY_AND_ASSIGN(Adder);
626};
627
628// Handles removing a mutex from the robust list.
629// The idea is to create one of these at the beginning of a function that needs
630// to do this.
631class Remover {
632 public:
633 Remover(aos_mutex *m) {
634 assert(robust_head.pending_next == 0);
635 if (kRobustListDebug) {
636 printf("%" PRId32 ": beginning to remove %p, n=%p p=%p\n", get_tid(), m,
637 next_to_mutex(m->next), m->previous);
638 }
639 robust_head.pending_next = mutex_to_next(m);
640 aos_compiler_memory_barrier();
641
642 aos_mutex *const previous = m->previous;
643 const uintptr_t next_value = m->next;
644
645 previous->next = m->next;
646 if (!next_is_head(next_value)) {
647 // robust_head's psuedo-mutex doesn't have a previous pointer to update.
648 next_to_mutex(next_value)->previous = previous;
649 }
650
651 if (kRobustListDebug) {
652 printf("%" PRId32 ": done removing %p\n", get_tid(), m);
653 }
654 }
655 ~Remover() {
656 assert(robust_head.pending_next != 0);
657 aos_compiler_memory_barrier();
658 robust_head.pending_next = 0;
659 if (kRobustListDebug) {
660 printf("%" PRId32 ": done with removal\n", get_tid());
661 }
662 }
663
664 private:
665 DISALLOW_COPY_AND_ASSIGN(Remover);
666};
667
668} // namespace my_robust_list
669
Brian Silvermandc1eb272014-08-19 14:25:59 -0400670void initialize_in_new_thread() {
671 // No synchronization necessary in most of this because it's all thread-local!
672
673 my_tid = do_get_tid();
674
John Park9372a682019-11-27 18:07:48 -0800675 static absl::once_flag once;
676 absl::call_once(once, InstallAtforkHook);
Brian Silverman71c55c52014-08-19 14:31:59 -0400677
678 my_robust_list::Init();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400679}
680
Brian Silverman71c55c52014-08-19 14:31:59 -0400681// Finishes the locking of a mutex by potentially clearing FUTEX_OWNER_DIED in
682// the futex and returning the correct value.
683inline int mutex_finish_lock(aos_mutex *m) {
684 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_ACQUIRE);
685 if (__builtin_expect((value & FUTEX_OWNER_DIED) != 0, false)) {
686 __atomic_and_fetch(&m->futex, ~FUTEX_OWNER_DIED, __ATOMIC_RELAXED);
687 force_lock_pthread_mutex(m);
688 return 1;
689 } else {
690 lock_pthread_mutex(m);
691 return 0;
692 }
693}
694
695// Split out separately from mutex_get so condition_wait can call it and use its
696// own my_robust_list::Adder.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400697inline int mutex_do_get(aos_mutex *m, bool signals_fail,
Brian Silverman71c55c52014-08-19 14:31:59 -0400698 const struct timespec *timeout, uint32_t tid) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700699 RunShmObservers run_observers(m, true);
Brian Silverman71c55c52014-08-19 14:31:59 -0400700 if (kPrintOperations) {
701 printf("%" PRId32 ": %p do_get\n", tid, m);
702 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400703
704 while (true) {
705 // If the atomic 0->TID transition fails.
706 if (!compare_and_swap(&m->futex, 0, tid)) {
707 // Wait in the kernel, which handles atomically ORing in FUTEX_WAITERS
708 // before actually sleeping.
709 const int ret = sys_futex_wait(FUTEX_LOCK_PI, &m->futex, 1, timeout);
710 if (ret != 0) {
711 if (timeout != NULL && ret == -ETIMEDOUT) {
712 return 3;
713 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400714 if (__builtin_expect(ret == -EINTR, true)) {
Brian Silvermandc1eb272014-08-19 14:25:59 -0400715 if (signals_fail) {
716 return 2;
717 } else {
718 continue;
719 }
720 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400721 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700722 CHECK_NE(ret, -EDEADLK) << ": multiple lock of " << m << " by " << tid;
723
724 errno = -ret;
725 PLOG(FATAL) << "FUTEX_LOCK_PI(" << &m->futex
726 << "(=" << __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST)
727 << "), 1, " << timeout << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400728 } else {
Brian Silverman71c55c52014-08-19 14:31:59 -0400729 if (kLockDebug) {
730 printf("%" PRId32 ": %p kernel lock done\n", tid, m);
731 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400732 // The kernel already handled setting the value to our TID (ish).
733 break;
734 }
735 } else {
Brian Silverman71c55c52014-08-19 14:31:59 -0400736 if (kLockDebug) {
737 printf("%" PRId32 ": %p fast lock done\n", tid, m);
738 }
739 lock_pthread_mutex(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400740 // Fastpath succeeded, so no need to call into the kernel.
Brian Silverman71c55c52014-08-19 14:31:59 -0400741 // Because this is the fastpath, it's a good idea to avoid even having to
742 // load the value again down below.
743 return 0;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400744 }
745 }
746
Brian Silverman71c55c52014-08-19 14:31:59 -0400747 return mutex_finish_lock(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400748}
749
750// The common implementation for everything that wants to lock a mutex.
751// If signals_fail is false, the function will try again if the wait syscall is
752// interrupted by a signal.
753// timeout can be NULL for no timeout.
754inline int mutex_get(aos_mutex *m, bool signals_fail,
755 const struct timespec *timeout) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400756 const uint32_t tid = get_tid();
757 my_robust_list::Adder adder(m);
758 const int r = mutex_do_get(m, signals_fail, timeout, tid);
759 if (r == 0 || r == 1) adder.Add();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400760 return r;
761}
762
763// The common implementation for broadcast and signal.
764// number_requeue is the number of waiters to requeue (probably INT_MAX or 0). 1
765// will always be woken.
Brian Silverman71c55c52014-08-19 14:31:59 -0400766void condition_wake(aos_condition *c, aos_mutex *m, int number_requeue) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700767 RunShmObservers run_observers(c, true);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400768 // Make it so that anybody just going to sleep won't.
769 // This is where we might accidentally wake more than just 1 waiter with 1
770 // signal():
771 // 1 already sleeping will be woken but n might never actually make it to
772 // sleep in the kernel because of this.
Brian Silverman71c55c52014-08-19 14:31:59 -0400773 uint32_t new_value = __atomic_add_fetch(c, 1, __ATOMIC_SEQ_CST);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400774
Brian2a4294f2019-06-12 20:23:32 -0700775 while (true) {
776 // This really wants to be FUTEX_REQUEUE_PI, but the kernel doesn't have
777 // that... However, the code to support that is in the kernel, so it might
778 // be a good idea to patch it to support that and use it iff it's there.
779 const int ret =
780 sys_futex_cmp_requeue_pi(c, 1, number_requeue, &m->futex, new_value);
781 if (ret < 0) {
782 // If the value got changed out from under us (aka somebody else did a
783 // condition_wake).
784 if (__builtin_expect(ret == -EAGAIN, true)) {
785 // If we're doing a broadcast, the other guy might have done a signal
786 // instead, so we have to try again.
787 // If we're doing a signal, we have to go again to make sure that 2
788 // signals wake 2 processes.
789 new_value = __atomic_load_n(c, __ATOMIC_RELAXED);
790 continue;
Brian Silverman71c55c52014-08-19 14:31:59 -0400791 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400792 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700793 errno = -ret;
794 PLOG(FATAL) << "FUTEX_CMP_REQUEUE_PI(" << c << ", 1, " << number_requeue
795 << ", " << &m->futex << ", *" << c << ") failed";
Brian2a4294f2019-06-12 20:23:32 -0700796 } else {
797 return;
Brian Silverman71c55c52014-08-19 14:31:59 -0400798 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400799 }
800}
801
802} // namespace
803
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700804int mutex_lock(aos_mutex *m) { return mutex_get(m, true, NULL); }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400805int mutex_lock_timeout(aos_mutex *m, const struct timespec *timeout) {
806 return mutex_get(m, true, timeout);
807}
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700808int mutex_grab(aos_mutex *m) { return mutex_get(m, false, NULL); }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400809
810void mutex_unlock(aos_mutex *m) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700811 RunShmObservers run_observers(m, true);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400812 const uint32_t tid = get_tid();
Brian Silverman71c55c52014-08-19 14:31:59 -0400813 if (kPrintOperations) {
814 printf("%" PRId32 ": %p unlock\n", tid, m);
815 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400816
817 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST);
Brian Silverman71c55c52014-08-19 14:31:59 -0400818 if (__builtin_expect((value & FUTEX_TID_MASK) != tid, false)) {
819 my_robust_list::robust_head.pending_next = 0;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400820 check_cached_tid(tid);
821 if ((value & FUTEX_TID_MASK) == 0) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700822 LOG(FATAL) << "multiple unlock of aos_mutex " << m << " by " << tid;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400823 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700824 LOG(FATAL) << "aos_mutex " << m << " is locked by "
825 << (value & FUTEX_TID_MASK) << ", not " << tid;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400826 }
827 }
828
Brian Silverman71c55c52014-08-19 14:31:59 -0400829 my_robust_list::Remover remover(m);
830 unlock_pthread_mutex(m);
831
Brian Silvermandc1eb272014-08-19 14:25:59 -0400832 // If the atomic TID->0 transition fails (ie FUTEX_WAITERS is set),
833 if (!compare_and_swap(&m->futex, tid, 0)) {
834 // The kernel handles everything else.
835 const int ret = sys_futex_unlock_pi(&m->futex);
836 if (ret != 0) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400837 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700838 errno = -ret;
839 PLOG(FATAL) << "FUTEX_UNLOCK_PI(" << (&m->futex) << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400840 }
841 } else {
842 // There aren't any waiters, so no need to call into the kernel.
843 }
844}
845
846int mutex_trylock(aos_mutex *m) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700847 RunShmObservers run_observers(m, true);
Brian Silverman71c55c52014-08-19 14:31:59 -0400848 const uint32_t tid = get_tid();
849 if (kPrintOperations) {
850 printf("%" PRId32 ": %p trylock\n", tid, m);
851 }
852 my_robust_list::Adder adder(m);
853
Brian Silvermandc1eb272014-08-19 14:25:59 -0400854 // Try an atomic 0->TID transition.
Brian Silverman71c55c52014-08-19 14:31:59 -0400855 uint32_t c = compare_and_swap_val(&m->futex, 0, tid);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400856
857 if (c != 0) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400858 if (__builtin_expect((c & FUTEX_OWNER_DIED) == 0, true)) {
859 // Somebody else had it locked; we failed.
860 return 4;
861 } else {
862 // FUTEX_OWNER_DIED was set, so we have to call into the kernel to deal
863 // with resetting it.
864 const int ret = sys_futex_wait(FUTEX_TRYLOCK_PI, &m->futex, 0, NULL);
865 if (ret == 0) {
866 adder.Add();
867 // Only clear the owner died if somebody else didn't do the recovery
868 // and then unlock before our TRYLOCK happened.
869 return mutex_finish_lock(m);
870 } else {
871 // EWOULDBLOCK means that somebody else beat us to it.
872 if (__builtin_expect(ret == -EWOULDBLOCK, true)) {
873 return 4;
874 }
875 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700876 errno = -ret;
877 PLOG(FATAL) << "FUTEX_TRYLOCK_PI(" << (&m->futex)
878 << ", 0, NULL) failed";
Brian Silverman71c55c52014-08-19 14:31:59 -0400879 }
880 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400881 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400882
883 lock_pthread_mutex(m);
884 adder.Add();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400885 return 0;
886}
887
888bool mutex_islocked(const aos_mutex *m) {
889 const uint32_t tid = get_tid();
890
891 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_RELAXED);
892 return (value & FUTEX_TID_MASK) == tid;
893}
894
Brian Silverman27af1f62019-11-18 12:04:48 -0800895void death_notification_init(aos_mutex *m) {
896 const uint32_t tid = get_tid();
897 if (kPrintOperations) {
898 printf("%" PRId32 ": %p death_notification start\n", tid, m);
899 }
900 my_robust_list::Adder adder(m);
901 {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700902 RunShmObservers run_observers(m, true);
Brian Silverman27af1f62019-11-18 12:04:48 -0800903 CHECK(compare_and_swap(&m->futex, 0, tid));
904 }
905 adder.Add();
906}
907
908void death_notification_release(aos_mutex *m) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700909 RunShmObservers run_observers(m, true);
Brian Silverman27af1f62019-11-18 12:04:48 -0800910
911#ifndef NDEBUG
912 // Verify it's "locked", like it should be.
913 {
914 const uint32_t tid = get_tid();
915 if (kPrintOperations) {
916 printf("%" PRId32 ": %p death_notification release\n", tid, m);
917 }
918 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST);
919 assert((value & ~FUTEX_WAITERS) == tid);
920 }
921#endif
922
923 my_robust_list::Remover remover(m);
924 ANNOTATE_HAPPENS_BEFORE(m);
925 const int ret = sys_futex_unlock_pi(&m->futex);
926 if (ret != 0) {
927 my_robust_list::robust_head.pending_next = 0;
928 errno = -ret;
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700929 PLOG(FATAL) << "FUTEX_UNLOCK_PI(" << &m->futex << ") failed";
Brian Silverman27af1f62019-11-18 12:04:48 -0800930 }
931}
932
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700933int condition_wait(aos_condition *c, aos_mutex *m, struct timespec *end_time) {
Brian Silverman1ed5df52021-09-13 20:14:06 -0700934 RunShmObservers run_observers(c, false);
Brian Silverman71c55c52014-08-19 14:31:59 -0400935 const uint32_t tid = get_tid();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400936 const uint32_t wait_start = __atomic_load_n(c, __ATOMIC_SEQ_CST);
937
938 mutex_unlock(m);
939
Brian Silverman71c55c52014-08-19 14:31:59 -0400940 my_robust_list::Adder adder(m);
941
Brian Silvermandc1eb272014-08-19 14:25:59 -0400942 while (true) {
943 // Wait in the kernel iff the value of it doesn't change (ie somebody else
944 // does a wake) from before we unlocked the mutex.
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700945 int ret = sys_futex_wait_requeue_pi(c, wait_start, end_time, &m->futex);
946
Brian Silvermandc1eb272014-08-19 14:25:59 -0400947 if (ret != 0) {
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700948 // Timed out waiting. Signal that back up to the user.
949 if (__builtin_expect(ret == -ETIMEDOUT, true)) {
950 // We have to relock it ourself because the kernel didn't do it.
951 const int r = mutex_do_get(m, false, nullptr, tid);
952 assert(__builtin_expect(r == 0 || r == 1, true));
953 adder.Add();
954
955 // OWNER_DIED takes priority. Pass it on if we found it.
956 if (r == 1) return r;
957 // Otherwise communicate that we were interrupted.
958 return -1;
959 }
960
Brian Silvermandc1eb272014-08-19 14:25:59 -0400961 // If it failed because somebody else did a wake and changed the value
962 // before we actually made it to sleep.
Brian Silverman71c55c52014-08-19 14:31:59 -0400963 if (__builtin_expect(ret == -EAGAIN, true)) {
964 // There's no need to unconditionally set FUTEX_WAITERS here if we're
965 // using REQUEUE_PI because the kernel automatically does that in the
966 // REQUEUE_PI iff it requeued anybody.
967 // If we're not using REQUEUE_PI, then everything is just normal locks
968 // etc, so there's no need to do anything special there either.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400969
970 // We have to relock it ourself because the kernel didn't do it.
Brian Silverman71c55c52014-08-19 14:31:59 -0400971 const int r = mutex_do_get(m, false, nullptr, tid);
972 assert(__builtin_expect(r == 0 || r == 1, true));
973 adder.Add();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400974 return r;
975 }
976 // Try again if it was because of a signal.
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700977 if (__builtin_expect((ret == -EINTR), true)) {
978 continue;
979 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400980 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700981 errno = -ret;
982 PLOG(FATAL) << "FUTEX_WAIT_REQUEUE_PI(" << c << ", " << wait_start << ", "
983 << (&m->futex) << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400984 } else {
Brian2a4294f2019-06-12 20:23:32 -0700985 // Record that the kernel relocked it for us.
986 lock_pthread_mutex(m);
Brian Silverman71c55c52014-08-19 14:31:59 -0400987
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700988 // We succeeded in waiting, and the kernel took care of locking the
989 // mutex
Brian Silverman71c55c52014-08-19 14:31:59 -0400990 // for us and setting FUTEX_WAITERS iff it needed to (for REQUEUE_PI).
991
992 adder.Add();
993
994 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_RELAXED);
995 if (__builtin_expect((value & FUTEX_OWNER_DIED) != 0, false)) {
996 __atomic_and_fetch(&m->futex, ~FUTEX_OWNER_DIED, __ATOMIC_RELAXED);
997 return 1;
998 } else {
999 return 0;
1000 }
Brian Silvermandc1eb272014-08-19 14:25:59 -04001001 }
1002 }
1003}
1004
1005void condition_signal(aos_condition *c, aos_mutex *m) {
1006 condition_wake(c, m, 0);
1007}
1008
1009void condition_broadcast(aos_condition *c, aos_mutex *m) {
1010 condition_wake(c, m, INT_MAX);
1011}
1012
1013int futex_wait_timeout(aos_futex *m, const struct timespec *timeout) {
Brian Silverman1ed5df52021-09-13 20:14:06 -07001014 RunShmObservers run_observers(m, false);
Brian Silvermandc1eb272014-08-19 14:25:59 -04001015 const int ret = sys_futex_wait(FUTEX_WAIT, m, 0, timeout);
1016 if (ret != 0) {
1017 if (ret == -EINTR) {
1018 return 1;
1019 } else if (ret == -ETIMEDOUT) {
1020 return 2;
1021 } else if (ret != -EWOULDBLOCK) {
1022 errno = -ret;
1023 return -1;
1024 }
1025 }
Brian Silverman71c55c52014-08-19 14:31:59 -04001026 ANNOTATE_HAPPENS_AFTER(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -04001027 return 0;
1028}
1029
1030int futex_wait(aos_futex *m) { return futex_wait_timeout(m, NULL); }
1031
1032int futex_set_value(aos_futex *m, uint32_t value) {
Brian Silverman1ed5df52021-09-13 20:14:06 -07001033 RunShmObservers run_observers(m, false);
Brian Silverman71c55c52014-08-19 14:31:59 -04001034 ANNOTATE_HAPPENS_BEFORE(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -04001035 __atomic_store_n(m, value, __ATOMIC_SEQ_CST);
1036 const int r = sys_futex_wake(m, INT_MAX - 4096);
1037 if (__builtin_expect(
Brian Silverman71c55c52014-08-19 14:31:59 -04001038 static_cast<unsigned int>(r) > static_cast<unsigned int>(-4096),
1039 false)) {
Brian Silvermandc1eb272014-08-19 14:25:59 -04001040 errno = -r;
1041 return -1;
1042 } else {
1043 return r;
1044 }
1045}
1046
Tyler Chatowbf0609c2021-07-31 16:13:27 -07001047int futex_set(aos_futex *m) { return futex_set_value(m, 1); }
Brian Silvermandc1eb272014-08-19 14:25:59 -04001048
1049int futex_unset(aos_futex *m) {
1050 return !__atomic_exchange_n(m, 0, __ATOMIC_SEQ_CST);
1051}
Brian Silverman71c55c52014-08-19 14:31:59 -04001052
Stephan Pleinesf63bde82024-01-13 15:59:33 -08001053namespace aos::linux_code::ipc_lib {
Brian Silverman71c55c52014-08-19 14:31:59 -04001054
Brian Silverman71c55c52014-08-19 14:31:59 -04001055// Sets an extra offset between mutexes and the value we use for them in the
1056// robust list (only the forward pointers). This is used to work around a kernel
1057// bug by keeping a second set of mutexes which is always writable so the kernel
1058// won't go into an infinite loop when trying to unlock them.
1059void SetRobustListOffset(ptrdiff_t offset) {
1060 my_robust_list::SetRobustListOffset(offset);
1061}
1062
1063// Returns true iff there are any mutexes locked by the current thread.
1064// This is mainly useful for testing.
Tyler Chatowbf0609c2021-07-31 16:13:27 -07001065bool HaveLockedMutexes() { return my_robust_list::HaveLockedMutexes(); }
Brian Silverman71c55c52014-08-19 14:31:59 -04001066
Stephan Pleinesf63bde82024-01-13 15:59:33 -08001067} // namespace aos::linux_code::ipc_lib