blob: 8290faa0df1889bda098d6930a3ca58cd0a60283 [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 Silverman27af1f62019-11-18 12:04:48 -080029// For use with the mutex_ or death_notification_ functions.
Brian Silvermandc1eb272014-08-19 14:25:59 -040030// 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);
Brian Silverman27af1f62019-11-18 12:04:48 -080091
92// The death_notification_ functions are designed for one thread to wait for
93// another thread (possibly in a different process) to end. This can mean
94// exiting gracefully or dying at any point. They use a standard aos_mutex, but
95// this mutex may not be used with any of the mutex_ functions.
96
97// Initializes a variable which can be used to wait for this thread to die.
98// This can only be called once after initializing *m.
99void death_notification_init(aos_mutex *m);
100
101// Manually triggers a death notification for this thread.
102// This thread must have previously called death_notification_init(m).
103void death_notification_release(aos_mutex *m);
104
105// Returns whether or not m is held by the current thread.
106// This is mainly useful as a debug assertion.
107inline bool death_notification_is_held(aos_mutex *m) {
108 return mutex_islocked(m);
109}
Brian Silvermandc1eb272014-08-19 14:25:59 -0400110#endif
brians343bc112013-02-10 01:53:46 +0000111
Brian Silvermanaf221b82013-09-01 13:57:50 -0700112// The futex_ functions are similar to the mutex_ ones but different.
brians343bc112013-02-10 01:53:46 +0000113// They are designed for signalling when something happens (possibly to
Brian Silvermandc1eb272014-08-19 14:25:59 -0400114// multiple listeners). A aos_futex manipulated with them can only be set or
115// unset. Also, they can be set/unset/waited on from any task independently of
116// who did something first and have no priority inversion protection.
117// They return -1 for other error (which will be in errno from futex(2)).
118// They have no spurious wakeups (because everybody always gets woken up).
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700119//
Brian Silverman5f8c4922014-02-11 21:22:38 -0800120// Another name for this kind of synchronization mechanism is a "notification".
Brian Silvermandc1eb272014-08-19 14:25:59 -0400121// Python calls it an "event".
Brian Silverman5f8c4922014-02-11 21:22:38 -0800122//
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700123// They are different from the condition_ functions in that they do NOT work
124// correctly as standard condition variables. While it is possible to keep
125// track of the "condition" using the value part of the futex_* functions, the
126// obvious implementation has basically the same race condition that condition
127// variables are designed to prevent between somebody else grabbing the mutex
128// and changing whether it's set or not and the futex_ function changing the
Brian Silvermandc1eb272014-08-19 14:25:59 -0400129// futex's value. A futex is effectively a resettable condition variable with
130// the condition being "has it been set"; if you have some other condition (for
131// example messages are available to read on a queue), use the condition_
132// functions or there will be race conditions.
brians343bc112013-02-10 01:53:46 +0000133
Brian Silverman408511d2016-09-10 16:12:02 -0400134// Wait for the futex to be set. Will return immediately if it's already set
135// (after a syscall).
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700136// Returns 0 if successful or it was already set, 1 if interrupted by a signal,
Brian Silverman408511d2016-09-10 16:12:02 -0400137// or -1 with an error in errno. Can return 0 spuriously.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400138int futex_wait(aos_futex *m) __attribute__((warn_unused_result));
139// The same as futex_wait except returns 2 if it times out.
140int futex_wait_timeout(aos_futex *m, const struct timespec *timeout)
141 __attribute__((warn_unused_result));
142
Brian Silvermanaf221b82013-09-01 13:57:50 -0700143// Set the futex and wake up anybody waiting on it.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400144// Returns the number that were woken or -1 with an error in errno.
Brian Silvermand41b4422013-09-01 14:02:33 -0700145//
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700146// This will always wake up all waiters at the same time and set the value to 1.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400147int futex_set(aos_futex *m);
brians343bc112013-02-10 01:53:46 +0000148// Same as above except lets something other than 1 be used as the final value.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400149int futex_set_value(aos_futex *m, aos_futex value);
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700150// Unsets the futex (sets the value to 0).
brians343bc112013-02-10 01:53:46 +0000151// Returns 0 if it was set before and 1 if it wasn't.
Brian Silvermand41b4422013-09-01 14:02:33 -0700152// Can not fail.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400153int futex_unset(aos_futex *m);
Brian Silvermanaf221b82013-09-01 13:57:50 -0700154
155// The condition_ functions implement condition variable support. The API is
Brian Silverman797e71e2013-09-06 17:29:39 -0700156// similar to the pthreads api and works the same way. The same m argument must
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700157// be passed in for all calls to all of the condition_ functions with a given c.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400158// They do have the potential for spurious wakeups.
Brian Silvermanaf221b82013-09-01 13:57:50 -0700159
160// Wait for the condition variable to be signalled. m will be unlocked
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700161// atomically with actually starting to wait. m is guaranteed to be locked when
162// this function returns.
163// NOTE: The relocking of m is not atomic with stopping the actual wait and
164// other process(es) may lock (+unlock) the mutex first.
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700165// Returns 0 on success, 1 if the previous owner died or -1 if we timed out.
166// Will only return -1 on timeout if end_time is not null.
167int condition_wait(aos_condition *c, struct aos_mutex *m,
168 struct timespec *end_time)
Brian Silvermandc1eb272014-08-19 14:25:59 -0400169 __attribute__((warn_unused_result));
Brian Silvermanaf221b82013-09-01 13:57:50 -0700170// If any other processes are condition_waiting on c, wake 1 of them. Does not
Brian Silverman797e71e2013-09-06 17:29:39 -0700171// require m to be locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400172// NOTE: There is a small chance that this will wake more than just 1 waiter.
173void condition_signal(aos_condition *c, struct aos_mutex *m);
Brian Silverman797e71e2013-09-06 17:29:39 -0700174// Wakes all processes that are condition_waiting on c. Does not require m to be
175// locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400176void condition_broadcast(aos_condition *c, struct aos_mutex *m);
brians343bc112013-02-10 01:53:46 +0000177
178#ifdef __cplusplus
179}
Brian Silverman71c55c52014-08-19 14:31:59 -0400180
181namespace aos {
182namespace linux_code {
183namespace ipc_lib {
184
185typedef void (*FutexAccessorObserver)(void *address, bool write);
186
187// Set functions which get called before and after all futex operations.
188void SetFutexAccessorObservers(FutexAccessorObserver before,
189 FutexAccessorObserver after);
190
191// Set the offset to use for putting addresses into the robust list.
192// This is necessary to work around a kernel bug where it hangs when trying to
193// deal with a futex on the robust list when its memory has been changed to
194// read-only.
195void SetRobustListOffset(ptrdiff_t offset);
196
197// Returns true if there are any mutexes still locked by this task.
198// This is mainly useful for verifying tests don't mess up other ones by leaving
199// now-freed but still locked mutexes around.
200bool HaveLockedMutexes();
201
202} // namespace ipc_lib
203} // namespace linux_code
204} // namespace aos
205
brians343bc112013-02-10 01:53:46 +0000206#endif // __cplusplus
207
John Park398c74a2018-10-20 21:17:39 -0700208#endif // AOS_IPC_LIB_SYNC_H_