blob: 2824516ce59e7904410e1f7834bc1248d57f5aff [file] [log] [blame]
John Park398c74a2018-10-20 21:17:39 -07001#ifndef AOS_IPC_LIB_SYNC_H_
2#define AOS_IPC_LIB_SYNC_H_
brians343bc112013-02-10 01:53:46 +00003
brians343bc112013-02-10 01:53:46 +00004#include <signal.h>
Austin Schuh0ad2b6f2019-06-09 21:27:07 -07005#include <stdbool.h>
Brian Silverman71c55c52014-08-19 14:31:59 -04006#include <stddef.h>
Austin Schuh0ad2b6f2019-06-09 21:27:07 -07007#include <stdint.h>
8#include <stdlib.h>
brians343bc112013-02-10 01:53:46 +00009
10#ifdef __cplusplus
11extern "C" {
12#endif // __cplusplus
13
14// TODO(brians) add client requests to make helgrind useful with this code
15// <http://www.valgrind.org/docs/manual/hg-manual.html#hg-manual.client-requests>
16// and <http://www.valgrind.org/docs/manual/drd-manual.html#drd-manual.clientreqs>
17// list the interesting ones
18
Brian Silvermandc1eb272014-08-19 14:25:59 -040019// Have to remember to align structs containing it (recursively) to sizeof(int).
Brian Silvermanaf221b82013-09-01 13:57:50 -070020// Valid initial values for use with futex_ functions are 0 (unset) and 1 (set).
brians343bc112013-02-10 01:53:46 +000021// The value should not be changed after multiple processes have started
22// accessing an instance except through the functions declared in this file.
Brian Silvermandc1eb272014-08-19 14:25:59 -040023typedef uint32_t aos_futex __attribute__((aligned(sizeof(int))));
brians343bc112013-02-10 01:53:46 +000024
Brian Silvermandc1eb272014-08-19 14:25:59 -040025// For use with the condition_ functions.
26// No initialization is necessary.
27typedef aos_futex aos_condition;
brians343bc112013-02-10 01:53:46 +000028
Brian Silvermandc1eb272014-08-19 14:25:59 -040029// For use with the mutex_ functions.
30// futex must be initialized to 0.
Brian Silverman71c55c52014-08-19 14:31:59 -040031// No initialization is necessary for next and previous.
32// Under ThreadSanitizer, pthread_mutex_init must be initialized to false.
Brian Silvermandc1eb272014-08-19 14:25:59 -040033// The recommended way to initialize one of these is by memset(3)ing the whole
34// thing to 0 or using C++ () initialization to avoid depending on the
35// implementation.
36struct aos_mutex {
Brian Silverman71c55c52014-08-19 14:31:59 -040037 // 2 links to get O(1) adds and removes.
38 // This is &next of another element.
39 // next (might) have stuff |ed into it to indicate PI futexes and might also
40 // have an offset (see SetRobustListOffset); previous is an actual pointer
41 // without any of that.
42 // next has to stay the first element of this structure.
43 uintptr_t next;
44 struct aos_mutex *previous;
Brian Silvermandc1eb272014-08-19 14:25:59 -040045 aos_futex futex;
Brian Silverman71c55c52014-08-19 14:31:59 -040046#ifdef AOS_SANITIZER_thread
47 // Internal pthread mutex which is kept in sync with the actual mutex so tsan
48 // can understand what's happening and help catch bugs.
49 pthread_mutex_t pthread_mutex;
50#ifndef __cplusplus
51 // TODO(brian): Remove this once the stupid C code is gone...
52#define bool uint8_t
53#endif
54 bool pthread_mutex_init;
55#ifndef __cplusplus
56#undef bool
57#endif
58#endif
Brian Silvermandc1eb272014-08-19 14:25:59 -040059};
60
61// The mutex_ functions are designed to be used as mutexes. A mutex can only be
Brian Silverman71c55c52014-08-19 14:31:59 -040062// unlocked from the same task which originally locked it. Also, if a task dies
63// while holding a mutex, the next person who locks it will be notified. After a
64// fork(2), any mutexes held will be held ONLY in the parent process. Attempting
65// to unlock them from the child will give errors.
66// Priority inheritance (aka priority inversion protection) is enabled.
Brian Silvermandc1eb272014-08-19 14:25:59 -040067
Brian Silverman71c55c52014-08-19 14:31:59 -040068// All of these return 1 if the previous owner died with it held, 2 if
Brian Silvermandc1eb272014-08-19 14:25:59 -040069// interrupted by a signal, 3 if timed out, or 4 if an optional lock fails. Some
70// of them (obviously) can never return some of those values.
Brian Silvermand41b4422013-09-01 14:02:33 -070071//
72// One of the highest priority processes blocked on a given mutex will be the
73// one to lock it when it is unlocked.
Brian Silvermandc1eb272014-08-19 14:25:59 -040074int mutex_lock(struct aos_mutex *m) __attribute__((warn_unused_result));
brians343bc112013-02-10 01:53:46 +000075// Returns 2 if it timed out or 1 if interrupted by a signal.
Brian Silvermandc1eb272014-08-19 14:25:59 -040076int mutex_lock_timeout(struct aos_mutex *m, const struct timespec *timeout)
77 __attribute__((warn_unused_result));
78// Ignores signals (retries until something other than getting a signal
79// happens).
80int mutex_grab(struct aos_mutex *m) __attribute__((warn_unused_result));
81// LOG(FATAL)s for multiple unlocking.
82void mutex_unlock(struct aos_mutex *m);
83// Does not block waiting for the mutex.
84int mutex_trylock(struct aos_mutex *m) __attribute__((warn_unused_result));
85#ifdef __cplusplus
86// Returns whether or not the mutex is locked by this thread.
87// There aren't very many valid uses for this function; the main ones are
88// checking mutexes as they are destroyed to catch problems with that early and
89// stack-based recursive mutex locking.
90bool mutex_islocked(const aos_mutex *m);
91#endif
brians343bc112013-02-10 01:53:46 +000092
Brian Silvermanaf221b82013-09-01 13:57:50 -070093// The futex_ functions are similar to the mutex_ ones but different.
brians343bc112013-02-10 01:53:46 +000094// They are designed for signalling when something happens (possibly to
Brian Silvermandc1eb272014-08-19 14:25:59 -040095// multiple listeners). A aos_futex manipulated with them can only be set or
96// unset. Also, they can be set/unset/waited on from any task independently of
97// who did something first and have no priority inversion protection.
98// They return -1 for other error (which will be in errno from futex(2)).
99// They have no spurious wakeups (because everybody always gets woken up).
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700100//
Brian Silverman5f8c4922014-02-11 21:22:38 -0800101// Another name for this kind of synchronization mechanism is a "notification".
Brian Silvermandc1eb272014-08-19 14:25:59 -0400102// Python calls it an "event".
Brian Silverman5f8c4922014-02-11 21:22:38 -0800103//
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700104// They are different from the condition_ functions in that they do NOT work
105// correctly as standard condition variables. While it is possible to keep
106// track of the "condition" using the value part of the futex_* functions, the
107// obvious implementation has basically the same race condition that condition
108// variables are designed to prevent between somebody else grabbing the mutex
109// and changing whether it's set or not and the futex_ function changing the
Brian Silvermandc1eb272014-08-19 14:25:59 -0400110// futex's value. A futex is effectively a resettable condition variable with
111// the condition being "has it been set"; if you have some other condition (for
112// example messages are available to read on a queue), use the condition_
113// functions or there will be race conditions.
brians343bc112013-02-10 01:53:46 +0000114
Brian Silverman408511d2016-09-10 16:12:02 -0400115// Wait for the futex to be set. Will return immediately if it's already set
116// (after a syscall).
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700117// Returns 0 if successful or it was already set, 1 if interrupted by a signal,
Brian Silverman408511d2016-09-10 16:12:02 -0400118// or -1 with an error in errno. Can return 0 spuriously.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400119int futex_wait(aos_futex *m) __attribute__((warn_unused_result));
120// The same as futex_wait except returns 2 if it times out.
121int futex_wait_timeout(aos_futex *m, const struct timespec *timeout)
122 __attribute__((warn_unused_result));
123
Brian Silvermanaf221b82013-09-01 13:57:50 -0700124// Set the futex and wake up anybody waiting on it.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400125// Returns the number that were woken or -1 with an error in errno.
Brian Silvermand41b4422013-09-01 14:02:33 -0700126//
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700127// This will always wake up all waiters at the same time and set the value to 1.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400128int futex_set(aos_futex *m);
brians343bc112013-02-10 01:53:46 +0000129// Same as above except lets something other than 1 be used as the final value.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400130int futex_set_value(aos_futex *m, aos_futex value);
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700131// Unsets the futex (sets the value to 0).
brians343bc112013-02-10 01:53:46 +0000132// Returns 0 if it was set before and 1 if it wasn't.
Brian Silvermand41b4422013-09-01 14:02:33 -0700133// Can not fail.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400134int futex_unset(aos_futex *m);
Brian Silvermanaf221b82013-09-01 13:57:50 -0700135
136// The condition_ functions implement condition variable support. The API is
Brian Silverman797e71e2013-09-06 17:29:39 -0700137// similar to the pthreads api and works the same way. The same m argument must
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700138// be passed in for all calls to all of the condition_ functions with a given c.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400139// They do have the potential for spurious wakeups.
Brian Silvermanaf221b82013-09-01 13:57:50 -0700140
141// Wait for the condition variable to be signalled. m will be unlocked
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700142// atomically with actually starting to wait. m is guaranteed to be locked when
143// this function returns.
144// NOTE: The relocking of m is not atomic with stopping the actual wait and
145// other process(es) may lock (+unlock) the mutex first.
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700146// Returns 0 on success, 1 if the previous owner died or -1 if we timed out.
147// Will only return -1 on timeout if end_time is not null.
148int condition_wait(aos_condition *c, struct aos_mutex *m,
149 struct timespec *end_time)
Brian Silvermandc1eb272014-08-19 14:25:59 -0400150 __attribute__((warn_unused_result));
Brian Silvermanaf221b82013-09-01 13:57:50 -0700151// If any other processes are condition_waiting on c, wake 1 of them. Does not
Brian Silverman797e71e2013-09-06 17:29:39 -0700152// require m to be locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400153// NOTE: There is a small chance that this will wake more than just 1 waiter.
154void condition_signal(aos_condition *c, struct aos_mutex *m);
Brian Silverman797e71e2013-09-06 17:29:39 -0700155// Wakes all processes that are condition_waiting on c. Does not require m to be
156// locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400157void condition_broadcast(aos_condition *c, struct aos_mutex *m);
brians343bc112013-02-10 01:53:46 +0000158
159#ifdef __cplusplus
160}
Brian Silverman71c55c52014-08-19 14:31:59 -0400161
162namespace aos {
163namespace linux_code {
164namespace ipc_lib {
165
166typedef void (*FutexAccessorObserver)(void *address, bool write);
167
168// Set functions which get called before and after all futex operations.
169void SetFutexAccessorObservers(FutexAccessorObserver before,
170 FutexAccessorObserver after);
171
172// Set the offset to use for putting addresses into the robust list.
173// This is necessary to work around a kernel bug where it hangs when trying to
174// deal with a futex on the robust list when its memory has been changed to
175// read-only.
176void SetRobustListOffset(ptrdiff_t offset);
177
178// Returns true if there are any mutexes still locked by this task.
179// This is mainly useful for verifying tests don't mess up other ones by leaving
180// now-freed but still locked mutexes around.
181bool HaveLockedMutexes();
182
183} // namespace ipc_lib
184} // namespace linux_code
185} // namespace aos
186
brians343bc112013-02-10 01:53:46 +0000187#endif // __cplusplus
188
John Park398c74a2018-10-20 21:17:39 -0700189#endif // AOS_IPC_LIB_SYNC_H_