blob: 6407a57a016399798ecf21eeb900eb45e4bc643f [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 Silverman71c55c52014-08-19 14:31:59 -040023#ifdef AOS_SANITIZER_thread
24#include <sanitizer/tsan_interface_atomic.h>
25#endif
26
Brian Silvermandc1eb272014-08-19 14:25:59 -040027#include <algorithm>
Brian Silverman71c55c52014-08-19 14:31:59 -040028#include <type_traits>
Brian Silvermandc1eb272014-08-19 14:25:59 -040029
Alex Perrycb7da4b2019-08-28 19:35:56 -070030#include "absl/base/call_once.h"
Brian Silvermanb47f5552020-10-01 15:08:14 -070031#include "aos/macros.h"
32#include "aos/thread_local.h"
33#include "aos/util/compiler_memory_barrier.h"
Tyler Chatowbf0609c2021-07-31 16:13:27 -070034#include "glog/logging.h"
Brian Silvermanb47f5552020-10-01 15:08:14 -070035
Brian Silverman71c55c52014-08-19 14:31:59 -040036using ::aos::linux_code::ipc_lib::FutexAccessorObserver;
Brian Silvermandc1eb272014-08-19 14:25:59 -040037
Tyler Chatowbf0609c2021-07-31 16:13:27 -070038// This code was originally based on
39// <https://www.akkadia.org/drepper/futex.pdf>, but is has since evolved a lot.
40// However, that still has useful information.
Brian Silvermandc1eb272014-08-19 14:25:59 -040041//
42// Finding information about actually using futexes is really REALLY hard, so
43// here's a list of the stuff that I've used:
44// futex(7) has a really high-level overview.
45// <http://locklessinc.com/articles/futex_cheat_sheet/> describes some of the
46// operations in a bit more detail than most places.
47// <http://locklessinc.com/articles/mutex_cv_futex/> is the basis of our
48// implementations (before PI).
49// <http://lwn.net/Articles/360699/> has a nice overview of futexes in late 2009
50// (fairly recent compared to everything else...).
51// <https://www.kernel.org/doc/Documentation/pi-futex.txt>,
52// <https://www.kernel.org/doc/Documentation/futex-requeue-pi.txt>,
53// <https://www.kernel.org/doc/Documentation/robust-futexes.txt>,
54// and <https://www.kernel.org/doc/Documentation/robust-futex-ABI.txt> are all
55// useful references.
56// The kernel source (kernel/futex.c) has some useful comments about what the
57// various operations do (except figuring out which argument goes where in the
58// syscall is still confusing).
59// futex(2) is basically useless except for describing the order of the
60// arguments (it only has high-level descriptions of what some of the
61// operations do, and some of them are wrong in Wheezy).
62// glibc's nptl pthreads implementation is the intended user of most of these
63// things, so it is also a good place to look for examples. However, it is all
64// very hard to read because it supports ~20 different kinds of mutexes and
65// several variations of condition variables, and some of the pieces of code
66// are only written in assembly.
67// set_robust_list(2) is wrong in Wheezy (it doesn't actually take a TID
68// argument).
69//
70// Can't use PRIVATE futex operations because they use the pid (or something) as
71// part of the hash.
72//
73// ThreadSanitizer understands how these mutexes etc work. It appears to be able
74// to figure out the happens-before relationship from the __ATOMIC_SEQ_CST
75// atomic primitives.
76//
77// Remember that EAGAIN and EWOUDBLOCK are the same! (ie if you get EAGAIN from
78// FUTEX_WAIT, the docs call it EWOULDBLOCK...)
79
80// Values for an aos_mutex.futex (kernel-mandated):
81// 0 = unlocked
82// TID = locked, not contended
83// |FUTEX_WAITERS = there are waiters (aka contended)
Brian Silverman71c55c52014-08-19 14:31:59 -040084// |FUTEX_OWNER_DIED = old owner died
Brian Silvermandc1eb272014-08-19 14:25:59 -040085//
86// Values for an aos_futex being used directly:
87// 0 = unset
88// 1 = set
89//
90// The value of an aos_condition is just a generation counter.
91
Brian Silverman71c55c52014-08-19 14:31:59 -040092#ifdef AOS_SANITIZER_thread
93extern "C" void AnnotateHappensBefore(const char *file, int line,
94 uintptr_t addr);
95extern "C" void AnnotateHappensAfter(const char *file, int line,
96 uintptr_t addr);
97#define ANNOTATE_HAPPENS_BEFORE(address) \
98 AnnotateHappensBefore(__FILE__, __LINE__, \
99 reinterpret_cast<uintptr_t>(address))
100#define ANNOTATE_HAPPENS_AFTER(address) \
101 AnnotateHappensAfter(__FILE__, __LINE__, reinterpret_cast<uintptr_t>(address))
102#else
103#define ANNOTATE_HAPPENS_BEFORE(address)
104#define ANNOTATE_HAPPENS_AFTER(address)
105#endif
106
Brian Silvermandc1eb272014-08-19 14:25:59 -0400107namespace {
108
Brian Silverman71c55c52014-08-19 14:31:59 -0400109const bool kRobustListDebug = false;
110const bool kLockDebug = false;
111const bool kPrintOperations = false;
112
Brian Silvermandc1eb272014-08-19 14:25:59 -0400113// These sys_futex_* functions are wrappers around syscall(SYS_futex). They each
114// take a specific set of arguments for a given futex operation. They return the
115// result or a negated errno value. -1..-4095 mean errors and not successful
116// results, which is guaranteed by the kernel.
117//
Brian Silverman0b58a612021-09-13 15:32:29 -0700118// They each have optimized versions for some architectures which don't go
119// through syscall(2) or errno. These use register variables to get the values
120// in the right registers to actually make the syscall.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400121
Brian Silverman0b58a612021-09-13 15:32:29 -0700122// The actual macros that we key off of to use the inline versions or not.
Brian Silverman17426d92018-08-09 11:38:49 -0700123#if defined(__ARM_EABI__)
Brian Silverman0b58a612021-09-13 15:32:29 -0700124// The syscall interface is different for non-EABI ARM, so we test specifically
125// for EABI.
Brian Silverman17426d92018-08-09 11:38:49 -0700126#define ARM_EABI_INLINE_SYSCALL 1
Brian Silverman0b58a612021-09-13 15:32:29 -0700127#define AARCH64_INLINE_SYSCALL 0
128#elif defined(__aarch64__)
129// Linux only has one supported syscall ABI on aarch64, which is the one we
130// support.
131#define ARM_EABI_INLINE_SYSCALL 0
132#define AARCH64_INLINE_SYSCALL 1
Brian Silverman17426d92018-08-09 11:38:49 -0700133#else
134#define ARM_EABI_INLINE_SYSCALL 0
Brian Silverman0b58a612021-09-13 15:32:29 -0700135#define AARCH64_INLINE_SYSCALL 0
Brian Silverman17426d92018-08-09 11:38:49 -0700136#endif
Brian Silvermandc1eb272014-08-19 14:25:59 -0400137
138// Used for FUTEX_WAIT, FUTEX_LOCK_PI, and FUTEX_TRYLOCK_PI.
139inline int sys_futex_wait(int op, aos_futex *addr1, int val1,
140 const struct timespec *timeout) {
141#if ARM_EABI_INLINE_SYSCALL
142 register aos_futex *addr1_reg __asm__("r0") = addr1;
143 register int op_reg __asm__("r1") = op;
144 register int val1_reg __asm__("r2") = val1;
145 register const struct timespec *timeout_reg __asm__("r3") = timeout;
146 register int syscall_number __asm__("r7") = SYS_futex;
147 register int result __asm__("r0");
148 __asm__ volatile("swi #0"
149 : "=r"(result)
150 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
151 "r"(timeout_reg), "r"(syscall_number)
152 : "memory");
153 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700154#elif AARCH64_INLINE_SYSCALL
155 register aos_futex *addr1_reg __asm__("x0") = addr1;
156 register int op_reg __asm__("x1") = op;
157 register int val1_reg __asm__("x2") = val1;
158 register const struct timespec *timeout_reg __asm__("x3") = timeout;
159 register int syscall_number __asm__("x8") = SYS_futex;
160 register int result __asm__("x0");
161 __asm__ volatile("svc #0"
162 : "=r"(result)
163 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
164 "r"(timeout_reg), "r"(syscall_number)
165 : "memory");
166 return result;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400167#else
168 const int r = syscall(SYS_futex, addr1, op, val1, timeout);
169 if (r == -1) return -errno;
170 return r;
171#endif
172}
173
174inline int sys_futex_wake(aos_futex *addr1, int val1) {
175#if ARM_EABI_INLINE_SYSCALL
176 register aos_futex *addr1_reg __asm__("r0") = addr1;
177 register int op_reg __asm__("r1") = FUTEX_WAKE;
178 register int val1_reg __asm__("r2") = val1;
179 register int syscall_number __asm__("r7") = SYS_futex;
180 register int result __asm__("r0");
181 __asm__ volatile("swi #0"
182 : "=r"(result)
183 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
184 "r"(syscall_number)
185 : "memory");
186 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700187#elif AARCH64_INLINE_SYSCALL
188 register aos_futex *addr1_reg __asm__("x0") = addr1;
189 register int op_reg __asm__("x1") = FUTEX_WAKE;
190 register int val1_reg __asm__("x2") = val1;
191 register int syscall_number __asm__("x8") = SYS_futex;
192 register int result __asm__("x0");
193 __asm__ volatile("svc #0"
194 : "=r"(result)
195 : "r"(addr1_reg), "r"(op_reg), "r"(val1_reg),
196 "r"(syscall_number)
197 : "memory");
198 return result;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400199#else
200 const int r = syscall(SYS_futex, addr1, FUTEX_WAKE, val1);
201 if (r == -1) return -errno;
202 return r;
203#endif
204}
205
Brian Silverman71c55c52014-08-19 14:31:59 -0400206inline int sys_futex_cmp_requeue_pi(aos_futex *addr1, int num_wake,
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700207 int num_requeue, aos_futex *m,
208 uint32_t val) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400209#if ARM_EABI_INLINE_SYSCALL
210 register aos_futex *addr1_reg __asm__("r0") = addr1;
211 register int op_reg __asm__("r1") = FUTEX_CMP_REQUEUE_PI;
212 register int num_wake_reg __asm__("r2") = num_wake;
213 register int num_requeue_reg __asm__("r3") = num_requeue;
214 register aos_futex *m_reg __asm__("r4") = m;
215 register uint32_t val_reg __asm__("r5") = val;
216 register int syscall_number __asm__("r7") = SYS_futex;
217 register int result __asm__("r0");
218 __asm__ volatile("swi #0"
219 : "=r"(result)
220 : "r"(addr1_reg), "r"(op_reg), "r"(num_wake_reg),
221 "r"(num_requeue_reg), "r"(m_reg), "r"(val_reg),
222 "r"(syscall_number)
223 : "memory");
224 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700225#elif AARCH64_INLINE_SYSCALL
226 register aos_futex *addr1_reg __asm__("x0") = addr1;
227 register int op_reg __asm__("x1") = FUTEX_CMP_REQUEUE_PI;
228 register int num_wake_reg __asm__("x2") = num_wake;
229 register int num_requeue_reg __asm__("x3") = num_requeue;
230 register aos_futex *m_reg __asm__("x4") = m;
231 register uint32_t val_reg __asm__("x5") = val;
232 register int syscall_number __asm__("x8") = SYS_futex;
233 register int result __asm__("x0");
234 __asm__ volatile("svc #0"
235 : "=r"(result)
236 : "r"(addr1_reg), "r"(op_reg), "r"(num_wake_reg),
237 "r"(num_requeue_reg), "r"(m_reg), "r"(val_reg),
238 "r"(syscall_number)
239 : "memory");
240 return result;
Brian Silverman71c55c52014-08-19 14:31:59 -0400241#else
242 const int r = syscall(SYS_futex, addr1, FUTEX_CMP_REQUEUE_PI, num_wake,
243 num_requeue, m, val);
244 if (r == -1) return -errno;
245 return r;
246#endif
247}
248
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700249inline int sys_futex_wait_requeue_pi(aos_condition *addr1, uint32_t start_val,
Brian Silverman71c55c52014-08-19 14:31:59 -0400250 const struct timespec *timeout,
251 aos_futex *m) {
252#if ARM_EABI_INLINE_SYSCALL
253 register aos_condition *addr1_reg __asm__("r0") = addr1;
254 register int op_reg __asm__("r1") = FUTEX_WAIT_REQUEUE_PI;
255 register uint32_t start_val_reg __asm__("r2") = start_val;
256 register const struct timespec *timeout_reg __asm__("r3") = timeout;
257 register aos_futex *m_reg __asm__("r4") = m;
258 register int syscall_number __asm__("r7") = SYS_futex;
259 register int result __asm__("r0");
260 __asm__ volatile("swi #0"
261 : "=r"(result)
262 : "r"(addr1_reg), "r"(op_reg), "r"(start_val_reg),
263 "r"(timeout_reg), "r"(m_reg), "r"(syscall_number)
264 : "memory");
265 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700266#elif AARCH64_INLINE_SYSCALL
267 register aos_condition *addr1_reg __asm__("x0") = addr1;
268 register int op_reg __asm__("x1") = FUTEX_WAIT_REQUEUE_PI;
269 register uint32_t start_val_reg __asm__("x2") = start_val;
270 register const struct timespec *timeout_reg __asm__("x3") = timeout;
271 register aos_futex *m_reg __asm__("x4") = m;
272 register int syscall_number __asm__("x8") = SYS_futex;
273 register int result __asm__("x0");
274 __asm__ volatile("svc #0"
275 : "=r"(result)
276 : "r"(addr1_reg), "r"(op_reg), "r"(start_val_reg),
277 "r"(timeout_reg), "r"(m_reg), "r"(syscall_number)
278 : "memory");
279 return result;
Brian Silverman71c55c52014-08-19 14:31:59 -0400280#else
281 const int r =
282 syscall(SYS_futex, addr1, FUTEX_WAIT_REQUEUE_PI, start_val, timeout, m);
283 if (r == -1) return -errno;
284 return r;
285#endif
286}
287
Brian Silvermandc1eb272014-08-19 14:25:59 -0400288inline int sys_futex_unlock_pi(aos_futex *addr1) {
289#if ARM_EABI_INLINE_SYSCALL
290 register aos_futex *addr1_reg __asm__("r0") = addr1;
291 register int op_reg __asm__("r1") = FUTEX_UNLOCK_PI;
292 register int syscall_number __asm__("r7") = SYS_futex;
293 register int result __asm__("r0");
294 __asm__ volatile("swi #0"
295 : "=r"(result)
296 : "r"(addr1_reg), "r"(op_reg), "r"(syscall_number)
297 : "memory");
298 return result;
Brian Silverman0b58a612021-09-13 15:32:29 -0700299#elif AARCH64_INLINE_SYSCALL
300 register aos_futex *addr1_reg __asm__("x0") = addr1;
301 register int op_reg __asm__("x1") = FUTEX_UNLOCK_PI;
302 register int syscall_number __asm__("x8") = SYS_futex;
303 register int result __asm__("x0");
304 __asm__ volatile("svc #0"
305 : "=r"(result)
306 : "r"(addr1_reg), "r"(op_reg), "r"(syscall_number)
307 : "memory");
308 return result;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400309#else
310 const int r = syscall(SYS_futex, addr1, FUTEX_UNLOCK_PI);
311 if (r == -1) return -errno;
312 return r;
313#endif
314}
315
Brian Silverman71c55c52014-08-19 14:31:59 -0400316// Returns the previous value of f.
317inline uint32_t compare_and_swap_val(aos_futex *f, uint32_t before,
318 uint32_t after) {
319#ifdef AOS_SANITIZER_thread
320 // This is a workaround for <https://llvm.org/bugs/show_bug.cgi?id=23176>.
321 // Basically, most of the atomic operations are broken under tsan, but this
322 // particular one isn't.
323 // TODO(Brian): Remove this #ifdef (and the one in compare_and_swap) once we
324 // don't have to worry about tsan with this bug any more.
325 uint32_t before_value = before;
326 __tsan_atomic32_compare_exchange_strong(
327 reinterpret_cast<int32_t *>(f),
328 reinterpret_cast<int32_t *>(&before_value), after,
329 __tsan_memory_order_seq_cst, __tsan_memory_order_seq_cst);
330 return before_value;
331#else
332 return __sync_val_compare_and_swap(f, before, after);
333#endif
Brian Silvermandc1eb272014-08-19 14:25:59 -0400334}
335
Brian Silverman71c55c52014-08-19 14:31:59 -0400336// Returns true if it succeeds and false if it fails.
337inline bool compare_and_swap(aos_futex *f, uint32_t before, uint32_t after) {
338#ifdef AOS_SANITIZER_thread
339 return compare_and_swap_val(f, before, after) == before;
340#else
341 return __sync_bool_compare_and_swap(f, before, after);
342#endif
343}
344
345#ifdef AOS_SANITIZER_thread
346
347// Simple macro for checking something which should always be true.
348// Using the standard CHECK macro isn't safe because failures often result in
349// reentering the mutex locking code, which doesn't work.
350#define SIMPLE_CHECK(expr) \
351 do { \
352 if (!(expr)) { \
353 fprintf(stderr, "%s: %d: SIMPLE_CHECK(" #expr ") failed!\n", __FILE__, \
354 __LINE__); \
355 abort(); \
356 } \
357 } while (false)
358
359// Forcibly initializes the pthread mutex for *m.
360// This sequence of operations is only safe for the simpler kinds of mutexes in
361// glibc's pthreads implementation on Linux.
362void init_pthread_mutex(aos_mutex *m) {
363 // Re-initialize the mutex so the destroy won't fail if it's locked.
364 // tsan ignores this.
365 SIMPLE_CHECK(0 == pthread_mutex_init(&m->pthread_mutex, nullptr));
366 // Destroy the mutex so tsan will forget about it if some now-dead thread
367 // locked it.
368 SIMPLE_CHECK(0 == pthread_mutex_destroy(&m->pthread_mutex));
369
370 // Now actually initialize it, making sure it's process-shareable so it works
371 // correctly across shared memory.
372 pthread_mutexattr_t attr;
373 SIMPLE_CHECK(0 == pthread_mutexattr_init(&attr));
374 SIMPLE_CHECK(0 == pthread_mutexattr_setpshared(&attr, true));
375 SIMPLE_CHECK(0 == pthread_mutex_init(&m->pthread_mutex, &attr));
376 SIMPLE_CHECK(0 == pthread_mutexattr_destroy(&attr));
377}
378
379// Locks the pthread mutex for *m.
380// If a stack trace ever reveals the pthread_mutex_lock call in here blocking,
381// there is a bug in our mutex code or the way somebody is calling it.
382void lock_pthread_mutex(aos_mutex *m) {
383 if (!m->pthread_mutex_init) {
384 init_pthread_mutex(m);
385 m->pthread_mutex_init = true;
386 }
387 SIMPLE_CHECK(0 == pthread_mutex_lock(&m->pthread_mutex));
388}
389
390// Forcibly locks the pthread mutex for *m.
391// This will (somewhat hackily) rip the lock out from underneath somebody else
392// who is already holding it.
393void force_lock_pthread_mutex(aos_mutex *m) {
394 if (!m->pthread_mutex_init) {
395 init_pthread_mutex(m);
396 m->pthread_mutex_init = true;
397 }
398 const int trylock_result = pthread_mutex_trylock(&m->pthread_mutex);
399 SIMPLE_CHECK(trylock_result == 0 || trylock_result == EBUSY);
400 if (trylock_result == 0) {
401 // We're good, so unlock it and then go for a real lock down below.
402 SIMPLE_CHECK(0 == pthread_mutex_unlock(&m->pthread_mutex));
403 } else {
404 // Somebody (should always be somebody else who died with it held) already
405 // has it, so make tsan forget about that.
406 init_pthread_mutex(m);
407 }
408 lock_pthread_mutex(m);
409}
410
411// Unlocks the pthread mutex for *m.
412void unlock_pthread_mutex(aos_mutex *m) {
413 assert(m->pthread_mutex_init);
414 SIMPLE_CHECK(0 == pthread_mutex_unlock(&m->pthread_mutex));
415}
416
417#else
418
419// Empty implementations of all these so the code below doesn't need #ifdefs.
420static inline void lock_pthread_mutex(aos_mutex *) {}
421static inline void force_lock_pthread_mutex(aos_mutex *) {}
422static inline void unlock_pthread_mutex(aos_mutex *) {}
423
424#endif
425
Brian Silvermandc1eb272014-08-19 14:25:59 -0400426pid_t do_get_tid() {
427 pid_t r = syscall(SYS_gettid);
428 assert(r > 0);
429 return r;
430}
431
Alex Perrycb7da4b2019-08-28 19:35:56 -0700432// This gets called by functions before LOG(FATAL)ing with error messages
Austin Schuhf257f3c2019-10-27 21:00:43 -0700433// that would be incorrect if the error was caused by a process forking without
Brian Silvermandc1eb272014-08-19 14:25:59 -0400434// initialize_in_new_thread getting called in the fork.
435void check_cached_tid(pid_t tid) {
436 pid_t actual = do_get_tid();
437 if (tid != actual) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700438 LOG(FATAL) << "task " << static_cast<intmax_t>(tid) << " forked into "
439 << static_cast<intmax_t>(actual)
440 << " without letting aos_sync know so we're not really sure "
441 "what's going on";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400442 }
443}
444
445// Starts off at 0 in each new thread (because that's what it gets initialized
446// 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 -0700447AOS_THREAD_LOCAL pid_t my_tid = 0;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400448
449// Gets called before the fork(2) wrapper function returns in the child.
450void atfork_child() {
451 // The next time get_tid() is called, it will set everything up again.
452 my_tid = 0;
453}
454
John Park0e699502019-11-20 19:36:05 -0800455void InstallAtforkHook() {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700456 PCHECK(pthread_atfork(NULL, NULL, &atfork_child) == 0)
457 << ": pthread_atfork(NULL, NULL, "
458 << reinterpret_cast<void *>(&atfork_child) << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400459}
460
461// This gets called to set everything up in a new thread by get_tid().
462void initialize_in_new_thread();
463
464// Gets the current thread's TID and does all of the 1-time initialization the
465// first time it's called in a given thread.
466inline uint32_t get_tid() {
Brian Silverman71c55c52014-08-19 14:31:59 -0400467 if (__builtin_expect(my_tid == 0, false)) {
Brian Silvermandc1eb272014-08-19 14:25:59 -0400468 initialize_in_new_thread();
469 }
470 static_assert(sizeof(my_tid) <= sizeof(uint32_t), "pid_t is too big");
471 return static_cast<uint32_t>(my_tid);
472}
473
Brian Silverman71c55c52014-08-19 14:31:59 -0400474// Contains all of the stuff for dealing with the robust list. Nothing outside
475// this namespace should touch anything inside it except Init, Adder, and
476// Remover.
477namespace my_robust_list {
478
479static_assert(offsetof(aos_mutex, next) == 0,
480 "Our math all assumes that the beginning of a mutex and its next "
481 "pointer are at the same place in memory.");
482
483// Our version of robust_list_head.
484// This is copied from the kernel header because that's a pretty stable ABI (and
485// any changes will be backwards compatible anyways) and we want ours to have
486// different types.
487// The uintptr_ts are &next of the elements in the list (with stuff |ed in).
488struct aos_robust_list_head {
489 uintptr_t next;
490 long futex_offset;
491 uintptr_t pending_next;
492};
493
494static_assert(offsetof(aos_robust_list_head, next) ==
495 offsetof(robust_list_head, list),
496 "Our aos_robust_list_head doesn't match the kernel's");
497static_assert(offsetof(aos_robust_list_head, futex_offset) ==
498 offsetof(robust_list_head, futex_offset),
499 "Our aos_robust_list_head doesn't match the kernel's");
500static_assert(offsetof(aos_robust_list_head, pending_next) ==
501 offsetof(robust_list_head, list_op_pending),
502 "Our aos_robust_list_head doesn't match the kernel's");
503static_assert(sizeof(aos_robust_list_head) == sizeof(robust_list_head),
504 "Our aos_robust_list_head doesn't match the kernel's");
505
Brian Silvermanb47f5552020-10-01 15:08:14 -0700506AOS_THREAD_LOCAL aos_robust_list_head robust_head;
Brian Silverman71c55c52014-08-19 14:31:59 -0400507
508// Extra offset between mutex values and where we point to for their robust list
509// entries (from SetRobustListOffset).
510uintptr_t robust_list_offset = 0;
511
512// The value to OR each pointer's value with whenever putting it into the robust
513// list (technically only if it's PI, but all of ours are, so...).
514static const uintptr_t kRobustListOr = 1;
515
516// Returns the value which goes into a next variable to represent the head.
517inline uintptr_t robust_head_next_value() {
518 return reinterpret_cast<uintptr_t>(&robust_head.next);
519}
520// Returns true iff next represents the head.
521inline bool next_is_head(uintptr_t next) {
522 return next == robust_head_next_value();
523}
524// Returns the (psuedo-)mutex corresponding to the head.
525// This does NOT have a previous pointer, so be careful with the return value.
526inline aos_mutex *robust_head_mutex() {
527 return reinterpret_cast<aos_mutex *>(robust_head_next_value());
528}
529
530inline uintptr_t mutex_to_next(aos_mutex *m) {
531 return (reinterpret_cast<uintptr_t>(&m->next) + robust_list_offset) |
532 kRobustListOr;
533}
534inline aos_mutex *next_to_mutex(uintptr_t next) {
535 if (__builtin_expect(robust_list_offset != 0, false) && next_is_head(next)) {
536 // We don't offset the head pointer, so be careful.
537 return reinterpret_cast<aos_mutex *>(next);
538 }
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700539 return reinterpret_cast<aos_mutex *>((next & ~kRobustListOr) -
540 robust_list_offset);
Brian Silverman71c55c52014-08-19 14:31:59 -0400541}
542
543// Sets up the robust list for each thread.
544void Init() {
545 // It starts out just pointing back to itself.
546 robust_head.next = robust_head_next_value();
547 robust_head.futex_offset = static_cast<ssize_t>(offsetof(aos_mutex, futex)) -
548 static_cast<ssize_t>(offsetof(aos_mutex, next));
549 robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700550 PCHECK(syscall(SYS_set_robust_list, robust_head_next_value(),
551 sizeof(robust_head)) == 0)
552 << ": set_robust_list(" << reinterpret_cast<void *>(robust_head.next)
553 << ", " << sizeof(robust_head) << ") failed";
Brian Silverman71c55c52014-08-19 14:31:59 -0400554 if (kRobustListDebug) {
555 printf("%" PRId32 ": init done\n", get_tid());
556 }
557}
558
559// Updating the offset with locked mutexes is important during robustness
560// testing, because there are mutexes which are locked before this is set to a
561// non-0 value and then unlocked after it is changed back. However, to make sure
562// the code works correctly when manipulating the next pointer of the last of
563// those mutexes, all of their next values have to be adjusted appropriately.
564void SetRobustListOffset(uintptr_t offset) {
565 const uintptr_t offset_change = offset - robust_list_offset;
566 robust_list_offset = offset;
567 aos_mutex *m = robust_head_mutex();
568 // Update the offset contained in each of the mutexes which is already locked.
569 while (!next_is_head(m->next)) {
570 m->next += offset_change;
571 m = next_to_mutex(m->next);
572 }
573}
574
575bool HaveLockedMutexes() {
576 return robust_head.next != robust_head_next_value();
577}
578
579// Handles adding a mutex to the robust list.
580// The idea is to create one of these at the beginning of a function that needs
581// to do this and then call Add() iff it should actually be added.
582class Adder {
583 public:
584 Adder(aos_mutex *m) : m_(m) {
585 assert(robust_head.pending_next == 0);
586 if (kRobustListDebug) {
587 printf("%" PRId32 ": maybe add %p\n", get_tid(), m_);
588 }
589 robust_head.pending_next = mutex_to_next(m);
590 aos_compiler_memory_barrier();
591 }
592 ~Adder() {
593 assert(robust_head.pending_next == mutex_to_next(m_));
594 if (kRobustListDebug) {
595 printf("%" PRId32 ": done maybe add %p, n=%p p=%p\n", get_tid(), m_,
596 next_to_mutex(m_->next), m_->previous);
597 }
598 aos_compiler_memory_barrier();
599 robust_head.pending_next = 0;
600 }
601
602 void Add() {
603 assert(robust_head.pending_next == mutex_to_next(m_));
604 if (kRobustListDebug) {
605 printf("%" PRId32 ": adding %p\n", get_tid(), m_);
606 }
607 const uintptr_t old_head_next_value = robust_head.next;
608
609 m_->next = old_head_next_value;
610 aos_compiler_memory_barrier();
611 robust_head.next = mutex_to_next(m_);
612
613 m_->previous = robust_head_mutex();
614 if (!next_is_head(old_head_next_value)) {
615 // robust_head's psuedo-mutex doesn't have a previous pointer to update.
616 next_to_mutex(old_head_next_value)->previous = m_;
617 }
618 aos_compiler_memory_barrier();
619 if (kRobustListDebug) {
620 printf("%" PRId32 ": done adding %p\n", get_tid(), m_);
621 }
622 }
623
624 private:
625 aos_mutex *const m_;
626
627 DISALLOW_COPY_AND_ASSIGN(Adder);
628};
629
630// Handles removing a mutex from the robust list.
631// The idea is to create one of these at the beginning of a function that needs
632// to do this.
633class Remover {
634 public:
635 Remover(aos_mutex *m) {
636 assert(robust_head.pending_next == 0);
637 if (kRobustListDebug) {
638 printf("%" PRId32 ": beginning to remove %p, n=%p p=%p\n", get_tid(), m,
639 next_to_mutex(m->next), m->previous);
640 }
641 robust_head.pending_next = mutex_to_next(m);
642 aos_compiler_memory_barrier();
643
644 aos_mutex *const previous = m->previous;
645 const uintptr_t next_value = m->next;
646
647 previous->next = m->next;
648 if (!next_is_head(next_value)) {
649 // robust_head's psuedo-mutex doesn't have a previous pointer to update.
650 next_to_mutex(next_value)->previous = previous;
651 }
652
653 if (kRobustListDebug) {
654 printf("%" PRId32 ": done removing %p\n", get_tid(), m);
655 }
656 }
657 ~Remover() {
658 assert(robust_head.pending_next != 0);
659 aos_compiler_memory_barrier();
660 robust_head.pending_next = 0;
661 if (kRobustListDebug) {
662 printf("%" PRId32 ": done with removal\n", get_tid());
663 }
664 }
665
666 private:
667 DISALLOW_COPY_AND_ASSIGN(Remover);
668};
669
670} // namespace my_robust_list
671
Brian Silvermandc1eb272014-08-19 14:25:59 -0400672void initialize_in_new_thread() {
673 // No synchronization necessary in most of this because it's all thread-local!
674
675 my_tid = do_get_tid();
676
John Park9372a682019-11-27 18:07:48 -0800677 static absl::once_flag once;
678 absl::call_once(once, InstallAtforkHook);
Brian Silverman71c55c52014-08-19 14:31:59 -0400679
680 my_robust_list::Init();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400681}
682
Brian Silverman71c55c52014-08-19 14:31:59 -0400683FutexAccessorObserver before_observer = nullptr, after_observer = nullptr;
684
685// RAII class which runs before_observer during construction and after_observer
686// during destruction.
687class RunObservers {
688 public:
689 template <class T>
690 RunObservers(T *address, bool write)
691 : address_(static_cast<void *>(
692 const_cast<typename ::std::remove_cv<T>::type *>(address))),
693 write_(write) {
694 if (__builtin_expect(before_observer != nullptr, false)) {
695 before_observer(address_, write_);
696 }
697 }
698 ~RunObservers() {
699 if (__builtin_expect(after_observer != nullptr, false)) {
700 after_observer(address_, write_);
701 }
702 }
703
704 private:
705 void *const address_;
706 const bool write_;
707
708 DISALLOW_COPY_AND_ASSIGN(RunObservers);
709};
710
711// Finishes the locking of a mutex by potentially clearing FUTEX_OWNER_DIED in
712// the futex and returning the correct value.
713inline int mutex_finish_lock(aos_mutex *m) {
714 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_ACQUIRE);
715 if (__builtin_expect((value & FUTEX_OWNER_DIED) != 0, false)) {
716 __atomic_and_fetch(&m->futex, ~FUTEX_OWNER_DIED, __ATOMIC_RELAXED);
717 force_lock_pthread_mutex(m);
718 return 1;
719 } else {
720 lock_pthread_mutex(m);
721 return 0;
722 }
723}
724
725// Split out separately from mutex_get so condition_wait can call it and use its
726// own my_robust_list::Adder.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400727inline int mutex_do_get(aos_mutex *m, bool signals_fail,
Brian Silverman71c55c52014-08-19 14:31:59 -0400728 const struct timespec *timeout, uint32_t tid) {
729 RunObservers run_observers(m, true);
730 if (kPrintOperations) {
731 printf("%" PRId32 ": %p do_get\n", tid, m);
732 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400733
734 while (true) {
735 // If the atomic 0->TID transition fails.
736 if (!compare_and_swap(&m->futex, 0, tid)) {
737 // Wait in the kernel, which handles atomically ORing in FUTEX_WAITERS
738 // before actually sleeping.
739 const int ret = sys_futex_wait(FUTEX_LOCK_PI, &m->futex, 1, timeout);
740 if (ret != 0) {
741 if (timeout != NULL && ret == -ETIMEDOUT) {
742 return 3;
743 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400744 if (__builtin_expect(ret == -EINTR, true)) {
Brian Silvermandc1eb272014-08-19 14:25:59 -0400745 if (signals_fail) {
746 return 2;
747 } else {
748 continue;
749 }
750 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400751 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700752 CHECK_NE(ret, -EDEADLK) << ": multiple lock of " << m << " by " << tid;
753
754 errno = -ret;
755 PLOG(FATAL) << "FUTEX_LOCK_PI(" << &m->futex
756 << "(=" << __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST)
757 << "), 1, " << timeout << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400758 } else {
Brian Silverman71c55c52014-08-19 14:31:59 -0400759 if (kLockDebug) {
760 printf("%" PRId32 ": %p kernel lock done\n", tid, m);
761 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400762 // The kernel already handled setting the value to our TID (ish).
763 break;
764 }
765 } else {
Brian Silverman71c55c52014-08-19 14:31:59 -0400766 if (kLockDebug) {
767 printf("%" PRId32 ": %p fast lock done\n", tid, m);
768 }
769 lock_pthread_mutex(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400770 // Fastpath succeeded, so no need to call into the kernel.
Brian Silverman71c55c52014-08-19 14:31:59 -0400771 // Because this is the fastpath, it's a good idea to avoid even having to
772 // load the value again down below.
773 return 0;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400774 }
775 }
776
Brian Silverman71c55c52014-08-19 14:31:59 -0400777 return mutex_finish_lock(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400778}
779
780// The common implementation for everything that wants to lock a mutex.
781// If signals_fail is false, the function will try again if the wait syscall is
782// interrupted by a signal.
783// timeout can be NULL for no timeout.
784inline int mutex_get(aos_mutex *m, bool signals_fail,
785 const struct timespec *timeout) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400786 const uint32_t tid = get_tid();
787 my_robust_list::Adder adder(m);
788 const int r = mutex_do_get(m, signals_fail, timeout, tid);
789 if (r == 0 || r == 1) adder.Add();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400790 return r;
791}
792
793// The common implementation for broadcast and signal.
794// number_requeue is the number of waiters to requeue (probably INT_MAX or 0). 1
795// will always be woken.
Brian Silverman71c55c52014-08-19 14:31:59 -0400796void condition_wake(aos_condition *c, aos_mutex *m, int number_requeue) {
797 RunObservers run_observers(c, true);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400798 // Make it so that anybody just going to sleep won't.
799 // This is where we might accidentally wake more than just 1 waiter with 1
800 // signal():
801 // 1 already sleeping will be woken but n might never actually make it to
802 // sleep in the kernel because of this.
Brian Silverman71c55c52014-08-19 14:31:59 -0400803 uint32_t new_value = __atomic_add_fetch(c, 1, __ATOMIC_SEQ_CST);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400804
Brian2a4294f2019-06-12 20:23:32 -0700805 while (true) {
806 // This really wants to be FUTEX_REQUEUE_PI, but the kernel doesn't have
807 // that... However, the code to support that is in the kernel, so it might
808 // be a good idea to patch it to support that and use it iff it's there.
809 const int ret =
810 sys_futex_cmp_requeue_pi(c, 1, number_requeue, &m->futex, new_value);
811 if (ret < 0) {
812 // If the value got changed out from under us (aka somebody else did a
813 // condition_wake).
814 if (__builtin_expect(ret == -EAGAIN, true)) {
815 // If we're doing a broadcast, the other guy might have done a signal
816 // instead, so we have to try again.
817 // If we're doing a signal, we have to go again to make sure that 2
818 // signals wake 2 processes.
819 new_value = __atomic_load_n(c, __ATOMIC_RELAXED);
820 continue;
Brian Silverman71c55c52014-08-19 14:31:59 -0400821 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400822 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700823 errno = -ret;
824 PLOG(FATAL) << "FUTEX_CMP_REQUEUE_PI(" << c << ", 1, " << number_requeue
825 << ", " << &m->futex << ", *" << c << ") failed";
Brian2a4294f2019-06-12 20:23:32 -0700826 } else {
827 return;
Brian Silverman71c55c52014-08-19 14:31:59 -0400828 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400829 }
830}
831
832} // namespace
833
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700834int mutex_lock(aos_mutex *m) { return mutex_get(m, true, NULL); }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400835int mutex_lock_timeout(aos_mutex *m, const struct timespec *timeout) {
836 return mutex_get(m, true, timeout);
837}
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700838int mutex_grab(aos_mutex *m) { return mutex_get(m, false, NULL); }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400839
840void mutex_unlock(aos_mutex *m) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400841 RunObservers run_observers(m, true);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400842 const uint32_t tid = get_tid();
Brian Silverman71c55c52014-08-19 14:31:59 -0400843 if (kPrintOperations) {
844 printf("%" PRId32 ": %p unlock\n", tid, m);
845 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400846
847 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST);
Brian Silverman71c55c52014-08-19 14:31:59 -0400848 if (__builtin_expect((value & FUTEX_TID_MASK) != tid, false)) {
849 my_robust_list::robust_head.pending_next = 0;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400850 check_cached_tid(tid);
851 if ((value & FUTEX_TID_MASK) == 0) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700852 LOG(FATAL) << "multiple unlock of aos_mutex " << m << " by " << tid;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400853 } else {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700854 LOG(FATAL) << "aos_mutex " << m << " is locked by "
855 << (value & FUTEX_TID_MASK) << ", not " << tid;
Brian Silvermandc1eb272014-08-19 14:25:59 -0400856 }
857 }
858
Brian Silverman71c55c52014-08-19 14:31:59 -0400859 my_robust_list::Remover remover(m);
860 unlock_pthread_mutex(m);
861
Brian Silvermandc1eb272014-08-19 14:25:59 -0400862 // If the atomic TID->0 transition fails (ie FUTEX_WAITERS is set),
863 if (!compare_and_swap(&m->futex, tid, 0)) {
864 // The kernel handles everything else.
865 const int ret = sys_futex_unlock_pi(&m->futex);
866 if (ret != 0) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400867 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700868 errno = -ret;
869 PLOG(FATAL) << "FUTEX_UNLOCK_PI(" << (&m->futex) << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400870 }
871 } else {
872 // There aren't any waiters, so no need to call into the kernel.
873 }
874}
875
876int mutex_trylock(aos_mutex *m) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400877 RunObservers run_observers(m, true);
878 const uint32_t tid = get_tid();
879 if (kPrintOperations) {
880 printf("%" PRId32 ": %p trylock\n", tid, m);
881 }
882 my_robust_list::Adder adder(m);
883
Brian Silvermandc1eb272014-08-19 14:25:59 -0400884 // Try an atomic 0->TID transition.
Brian Silverman71c55c52014-08-19 14:31:59 -0400885 uint32_t c = compare_and_swap_val(&m->futex, 0, tid);
Brian Silvermandc1eb272014-08-19 14:25:59 -0400886
887 if (c != 0) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400888 if (__builtin_expect((c & FUTEX_OWNER_DIED) == 0, true)) {
889 // Somebody else had it locked; we failed.
890 return 4;
891 } else {
892 // FUTEX_OWNER_DIED was set, so we have to call into the kernel to deal
893 // with resetting it.
894 const int ret = sys_futex_wait(FUTEX_TRYLOCK_PI, &m->futex, 0, NULL);
895 if (ret == 0) {
896 adder.Add();
897 // Only clear the owner died if somebody else didn't do the recovery
898 // and then unlock before our TRYLOCK happened.
899 return mutex_finish_lock(m);
900 } else {
901 // EWOULDBLOCK means that somebody else beat us to it.
902 if (__builtin_expect(ret == -EWOULDBLOCK, true)) {
903 return 4;
904 }
905 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -0700906 errno = -ret;
907 PLOG(FATAL) << "FUTEX_TRYLOCK_PI(" << (&m->futex)
908 << ", 0, NULL) failed";
Brian Silverman71c55c52014-08-19 14:31:59 -0400909 }
910 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400911 }
Brian Silverman71c55c52014-08-19 14:31:59 -0400912
913 lock_pthread_mutex(m);
914 adder.Add();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400915 return 0;
916}
917
918bool mutex_islocked(const aos_mutex *m) {
919 const uint32_t tid = get_tid();
920
921 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_RELAXED);
922 return (value & FUTEX_TID_MASK) == tid;
923}
924
Brian Silverman27af1f62019-11-18 12:04:48 -0800925void death_notification_init(aos_mutex *m) {
926 const uint32_t tid = get_tid();
927 if (kPrintOperations) {
928 printf("%" PRId32 ": %p death_notification start\n", tid, m);
929 }
930 my_robust_list::Adder adder(m);
931 {
932 RunObservers run_observers(m, true);
933 CHECK(compare_and_swap(&m->futex, 0, tid));
934 }
935 adder.Add();
936}
937
938void death_notification_release(aos_mutex *m) {
939 RunObservers run_observers(m, true);
940
941#ifndef NDEBUG
942 // Verify it's "locked", like it should be.
943 {
944 const uint32_t tid = get_tid();
945 if (kPrintOperations) {
946 printf("%" PRId32 ": %p death_notification release\n", tid, m);
947 }
948 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_SEQ_CST);
949 assert((value & ~FUTEX_WAITERS) == tid);
950 }
951#endif
952
953 my_robust_list::Remover remover(m);
954 ANNOTATE_HAPPENS_BEFORE(m);
955 const int ret = sys_futex_unlock_pi(&m->futex);
956 if (ret != 0) {
957 my_robust_list::robust_head.pending_next = 0;
958 errno = -ret;
Tyler Chatowbf0609c2021-07-31 16:13:27 -0700959 PLOG(FATAL) << "FUTEX_UNLOCK_PI(" << &m->futex << ") failed";
Brian Silverman27af1f62019-11-18 12:04:48 -0800960 }
961}
962
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700963int condition_wait(aos_condition *c, aos_mutex *m, struct timespec *end_time) {
Brian Silverman71c55c52014-08-19 14:31:59 -0400964 RunObservers run_observers(c, false);
965 const uint32_t tid = get_tid();
Brian Silvermandc1eb272014-08-19 14:25:59 -0400966 const uint32_t wait_start = __atomic_load_n(c, __ATOMIC_SEQ_CST);
967
968 mutex_unlock(m);
969
Brian Silverman71c55c52014-08-19 14:31:59 -0400970 my_robust_list::Adder adder(m);
971
Brian Silvermandc1eb272014-08-19 14:25:59 -0400972 while (true) {
973 // Wait in the kernel iff the value of it doesn't change (ie somebody else
974 // does a wake) from before we unlocked the mutex.
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700975 int ret = sys_futex_wait_requeue_pi(c, wait_start, end_time, &m->futex);
976
Brian Silvermandc1eb272014-08-19 14:25:59 -0400977 if (ret != 0) {
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700978 // Timed out waiting. Signal that back up to the user.
979 if (__builtin_expect(ret == -ETIMEDOUT, true)) {
980 // We have to relock it ourself because the kernel didn't do it.
981 const int r = mutex_do_get(m, false, nullptr, tid);
982 assert(__builtin_expect(r == 0 || r == 1, true));
983 adder.Add();
984
985 // OWNER_DIED takes priority. Pass it on if we found it.
986 if (r == 1) return r;
987 // Otherwise communicate that we were interrupted.
988 return -1;
989 }
990
Brian Silvermandc1eb272014-08-19 14:25:59 -0400991 // If it failed because somebody else did a wake and changed the value
992 // before we actually made it to sleep.
Brian Silverman71c55c52014-08-19 14:31:59 -0400993 if (__builtin_expect(ret == -EAGAIN, true)) {
994 // There's no need to unconditionally set FUTEX_WAITERS here if we're
995 // using REQUEUE_PI because the kernel automatically does that in the
996 // REQUEUE_PI iff it requeued anybody.
997 // If we're not using REQUEUE_PI, then everything is just normal locks
998 // etc, so there's no need to do anything special there either.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400999
1000 // We have to relock it ourself because the kernel didn't do it.
Brian Silverman71c55c52014-08-19 14:31:59 -04001001 const int r = mutex_do_get(m, false, nullptr, tid);
1002 assert(__builtin_expect(r == 0 || r == 1, true));
1003 adder.Add();
Brian Silvermandc1eb272014-08-19 14:25:59 -04001004 return r;
1005 }
1006 // Try again if it was because of a signal.
Austin Schuh0ad2b6f2019-06-09 21:27:07 -07001007 if (__builtin_expect((ret == -EINTR), true)) {
1008 continue;
1009 }
Brian Silverman71c55c52014-08-19 14:31:59 -04001010 my_robust_list::robust_head.pending_next = 0;
Alex Perrycb7da4b2019-08-28 19:35:56 -07001011 errno = -ret;
1012 PLOG(FATAL) << "FUTEX_WAIT_REQUEUE_PI(" << c << ", " << wait_start << ", "
1013 << (&m->futex) << ") failed";
Brian Silvermandc1eb272014-08-19 14:25:59 -04001014 } else {
Brian2a4294f2019-06-12 20:23:32 -07001015 // Record that the kernel relocked it for us.
1016 lock_pthread_mutex(m);
Brian Silverman71c55c52014-08-19 14:31:59 -04001017
Austin Schuh0ad2b6f2019-06-09 21:27:07 -07001018 // We succeeded in waiting, and the kernel took care of locking the
1019 // mutex
Brian Silverman71c55c52014-08-19 14:31:59 -04001020 // for us and setting FUTEX_WAITERS iff it needed to (for REQUEUE_PI).
1021
1022 adder.Add();
1023
1024 const uint32_t value = __atomic_load_n(&m->futex, __ATOMIC_RELAXED);
1025 if (__builtin_expect((value & FUTEX_OWNER_DIED) != 0, false)) {
1026 __atomic_and_fetch(&m->futex, ~FUTEX_OWNER_DIED, __ATOMIC_RELAXED);
1027 return 1;
1028 } else {
1029 return 0;
1030 }
Brian Silvermandc1eb272014-08-19 14:25:59 -04001031 }
1032 }
1033}
1034
1035void condition_signal(aos_condition *c, aos_mutex *m) {
1036 condition_wake(c, m, 0);
1037}
1038
1039void condition_broadcast(aos_condition *c, aos_mutex *m) {
1040 condition_wake(c, m, INT_MAX);
1041}
1042
1043int futex_wait_timeout(aos_futex *m, const struct timespec *timeout) {
Brian Silverman71c55c52014-08-19 14:31:59 -04001044 RunObservers run_observers(m, false);
Brian Silvermandc1eb272014-08-19 14:25:59 -04001045 const int ret = sys_futex_wait(FUTEX_WAIT, m, 0, timeout);
1046 if (ret != 0) {
1047 if (ret == -EINTR) {
1048 return 1;
1049 } else if (ret == -ETIMEDOUT) {
1050 return 2;
1051 } else if (ret != -EWOULDBLOCK) {
1052 errno = -ret;
1053 return -1;
1054 }
1055 }
Brian Silverman71c55c52014-08-19 14:31:59 -04001056 ANNOTATE_HAPPENS_AFTER(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -04001057 return 0;
1058}
1059
1060int futex_wait(aos_futex *m) { return futex_wait_timeout(m, NULL); }
1061
1062int futex_set_value(aos_futex *m, uint32_t value) {
Brian Silverman71c55c52014-08-19 14:31:59 -04001063 RunObservers run_observers(m, false);
1064 ANNOTATE_HAPPENS_BEFORE(m);
Brian Silvermandc1eb272014-08-19 14:25:59 -04001065 __atomic_store_n(m, value, __ATOMIC_SEQ_CST);
1066 const int r = sys_futex_wake(m, INT_MAX - 4096);
1067 if (__builtin_expect(
Brian Silverman71c55c52014-08-19 14:31:59 -04001068 static_cast<unsigned int>(r) > static_cast<unsigned int>(-4096),
1069 false)) {
Brian Silvermandc1eb272014-08-19 14:25:59 -04001070 errno = -r;
1071 return -1;
1072 } else {
1073 return r;
1074 }
1075}
1076
Tyler Chatowbf0609c2021-07-31 16:13:27 -07001077int futex_set(aos_futex *m) { return futex_set_value(m, 1); }
Brian Silvermandc1eb272014-08-19 14:25:59 -04001078
1079int futex_unset(aos_futex *m) {
1080 return !__atomic_exchange_n(m, 0, __ATOMIC_SEQ_CST);
1081}
Brian Silverman71c55c52014-08-19 14:31:59 -04001082
1083namespace aos {
1084namespace linux_code {
1085namespace ipc_lib {
1086
1087// Sets functions to run befor eand after all futex operations.
1088// This is important when doing robustness testing because the memory has to be
1089// made writable for the whole futex operation, otherwise it never succeeds.
1090void SetFutexAccessorObservers(FutexAccessorObserver before,
1091 FutexAccessorObserver after) {
1092 before_observer = before;
1093 after_observer = after;
1094}
1095
1096// Sets an extra offset between mutexes and the value we use for them in the
1097// robust list (only the forward pointers). This is used to work around a kernel
1098// bug by keeping a second set of mutexes which is always writable so the kernel
1099// won't go into an infinite loop when trying to unlock them.
1100void SetRobustListOffset(ptrdiff_t offset) {
1101 my_robust_list::SetRobustListOffset(offset);
1102}
1103
1104// Returns true iff there are any mutexes locked by the current thread.
1105// This is mainly useful for testing.
Tyler Chatowbf0609c2021-07-31 16:13:27 -07001106bool HaveLockedMutexes() { return my_robust_list::HaveLockedMutexes(); }
Brian Silverman71c55c52014-08-19 14:31:59 -04001107
1108} // namespace ipc_lib
1109} // namespace linux_code
1110} // namespace aos