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