blob: 157c1b224d53e3f9b2d48d94d568f789c9c85fc7 [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>
Austin Schuh60e77942022-05-16 17:48:24 -070016// and
17// <http://www.valgrind.org/docs/manual/drd-manual.html#drd-manual.clientreqs>
brians343bc112013-02-10 01:53:46 +000018// list the interesting ones
19
Brian Silvermandc1eb272014-08-19 14:25:59 -040020// Have to remember to align structs containing it (recursively) to sizeof(int).
Brian Silvermanaf221b82013-09-01 13:57:50 -070021// Valid initial values for use with futex_ functions are 0 (unset) and 1 (set).
brians343bc112013-02-10 01:53:46 +000022// The value should not be changed after multiple processes have started
23// accessing an instance except through the functions declared in this file.
Brian Silvermandc1eb272014-08-19 14:25:59 -040024typedef uint32_t aos_futex __attribute__((aligned(sizeof(int))));
brians343bc112013-02-10 01:53:46 +000025
Brian Silvermandc1eb272014-08-19 14:25:59 -040026// For use with the condition_ functions.
27// No initialization is necessary.
28typedef aos_futex aos_condition;
brians343bc112013-02-10 01:53:46 +000029
Brian Silverman27af1f62019-11-18 12:04:48 -080030// For use with the mutex_ or death_notification_ functions.
Brian Silvermandc1eb272014-08-19 14:25:59 -040031// futex must be initialized to 0.
Brian Silverman71c55c52014-08-19 14:31:59 -040032// No initialization is necessary for next and previous.
33// Under ThreadSanitizer, pthread_mutex_init must be initialized to false.
Brian Silvermandc1eb272014-08-19 14:25:59 -040034// The recommended way to initialize one of these is by memset(3)ing the whole
35// thing to 0 or using C++ () initialization to avoid depending on the
36// implementation.
37struct aos_mutex {
Brian Silverman71c55c52014-08-19 14:31:59 -040038 // 2 links to get O(1) adds and removes.
39 // This is &next of another element.
40 // next (might) have stuff |ed into it to indicate PI futexes and might also
41 // have an offset (see SetRobustListOffset); previous is an actual pointer
42 // without any of that.
43 // next has to stay the first element of this structure.
44 uintptr_t next;
45 struct aos_mutex *previous;
Brian Silvermandc1eb272014-08-19 14:25:59 -040046 aos_futex futex;
Brian Silverman71c55c52014-08-19 14:31:59 -040047#ifdef AOS_SANITIZER_thread
48 // Internal pthread mutex which is kept in sync with the actual mutex so tsan
49 // can understand what's happening and help catch bugs.
50 pthread_mutex_t pthread_mutex;
51#ifndef __cplusplus
52 // TODO(brian): Remove this once the stupid C code is gone...
53#define bool uint8_t
54#endif
55 bool pthread_mutex_init;
56#ifndef __cplusplus
57#undef bool
58#endif
59#endif
Brian Silvermandc1eb272014-08-19 14:25:59 -040060};
61
62// The mutex_ functions are designed to be used as mutexes. A mutex can only be
Brian Silverman71c55c52014-08-19 14:31:59 -040063// unlocked from the same task which originally locked it. Also, if a task dies
64// while holding a mutex, the next person who locks it will be notified. After a
65// fork(2), any mutexes held will be held ONLY in the parent process. Attempting
66// to unlock them from the child will give errors.
67// Priority inheritance (aka priority inversion protection) is enabled.
Brian Silvermandc1eb272014-08-19 14:25:59 -040068
Brian Silverman71c55c52014-08-19 14:31:59 -040069// All of these return 1 if the previous owner died with it held, 2 if
Brian Silvermandc1eb272014-08-19 14:25:59 -040070// interrupted by a signal, 3 if timed out, or 4 if an optional lock fails. Some
71// of them (obviously) can never return some of those values.
Brian Silvermand41b4422013-09-01 14:02:33 -070072//
73// One of the highest priority processes blocked on a given mutex will be the
74// one to lock it when it is unlocked.
Brian Silvermandc1eb272014-08-19 14:25:59 -040075int mutex_lock(struct aos_mutex *m) __attribute__((warn_unused_result));
brians343bc112013-02-10 01:53:46 +000076// Returns 2 if it timed out or 1 if interrupted by a signal.
Brian Silvermandc1eb272014-08-19 14:25:59 -040077int mutex_lock_timeout(struct aos_mutex *m, const struct timespec *timeout)
78 __attribute__((warn_unused_result));
79// Ignores signals (retries until something other than getting a signal
80// happens).
81int mutex_grab(struct aos_mutex *m) __attribute__((warn_unused_result));
82// LOG(FATAL)s for multiple unlocking.
83void mutex_unlock(struct aos_mutex *m);
84// Does not block waiting for the mutex.
85int mutex_trylock(struct aos_mutex *m) __attribute__((warn_unused_result));
86#ifdef __cplusplus
87// Returns whether or not the mutex is locked by this thread.
88// There aren't very many valid uses for this function; the main ones are
89// checking mutexes as they are destroyed to catch problems with that early and
90// stack-based recursive mutex locking.
91bool mutex_islocked(const aos_mutex *m);
Brian Silverman27af1f62019-11-18 12:04:48 -080092
93// The death_notification_ functions are designed for one thread to wait for
94// another thread (possibly in a different process) to end. This can mean
95// exiting gracefully or dying at any point. They use a standard aos_mutex, but
96// this mutex may not be used with any of the mutex_ functions.
97
98// Initializes a variable which can be used to wait for this thread to die.
Brian Silverman2484eea2019-12-21 16:48:46 -080099// This can only be called once after initializing *m to 0.
Brian Silverman27af1f62019-11-18 12:04:48 -0800100void death_notification_init(aos_mutex *m);
101
102// Manually triggers a death notification for this thread.
103// This thread must have previously called death_notification_init(m).
104void death_notification_release(aos_mutex *m);
105
106// Returns whether or not m is held by the current thread.
107// This is mainly useful as a debug assertion.
108inline bool death_notification_is_held(aos_mutex *m) {
109 return mutex_islocked(m);
110}
Brian Silvermandc1eb272014-08-19 14:25:59 -0400111#endif
brians343bc112013-02-10 01:53:46 +0000112
Brian Silvermanaf221b82013-09-01 13:57:50 -0700113// The futex_ functions are similar to the mutex_ ones but different.
brians343bc112013-02-10 01:53:46 +0000114// They are designed for signalling when something happens (possibly to
Brian Silvermandc1eb272014-08-19 14:25:59 -0400115// multiple listeners). A aos_futex manipulated with them can only be set or
116// unset. Also, they can be set/unset/waited on from any task independently of
117// who did something first and have no priority inversion protection.
118// They return -1 for other error (which will be in errno from futex(2)).
119// They have no spurious wakeups (because everybody always gets woken up).
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700120//
Brian Silverman5f8c4922014-02-11 21:22:38 -0800121// Another name for this kind of synchronization mechanism is a "notification".
Brian Silvermandc1eb272014-08-19 14:25:59 -0400122// Python calls it an "event".
Brian Silverman5f8c4922014-02-11 21:22:38 -0800123//
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700124// They are different from the condition_ functions in that they do NOT work
125// correctly as standard condition variables. While it is possible to keep
126// track of the "condition" using the value part of the futex_* functions, the
127// obvious implementation has basically the same race condition that condition
128// variables are designed to prevent between somebody else grabbing the mutex
129// and changing whether it's set or not and the futex_ function changing the
Brian Silvermandc1eb272014-08-19 14:25:59 -0400130// futex's value. A futex is effectively a resettable condition variable with
131// the condition being "has it been set"; if you have some other condition (for
132// example messages are available to read on a queue), use the condition_
133// functions or there will be race conditions.
brians343bc112013-02-10 01:53:46 +0000134
Brian Silverman408511d2016-09-10 16:12:02 -0400135// Wait for the futex to be set. Will return immediately if it's already set
136// (after a syscall).
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700137// Returns 0 if successful or it was already set, 1 if interrupted by a signal,
Brian Silverman408511d2016-09-10 16:12:02 -0400138// or -1 with an error in errno. Can return 0 spuriously.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400139int futex_wait(aos_futex *m) __attribute__((warn_unused_result));
140// The same as futex_wait except returns 2 if it times out.
141int futex_wait_timeout(aos_futex *m, const struct timespec *timeout)
Austin Schuh60e77942022-05-16 17:48:24 -0700142 __attribute__((warn_unused_result));
Brian Silvermandc1eb272014-08-19 14:25:59 -0400143
Brian Silvermanaf221b82013-09-01 13:57:50 -0700144// Set the futex and wake up anybody waiting on it.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400145// Returns the number that were woken or -1 with an error in errno.
Brian Silvermand41b4422013-09-01 14:02:33 -0700146//
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700147// This will always wake up all waiters at the same time and set the value to 1.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400148int futex_set(aos_futex *m);
brians343bc112013-02-10 01:53:46 +0000149// Same as above except lets something other than 1 be used as the final value.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400150int futex_set_value(aos_futex *m, aos_futex value);
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700151// Unsets the futex (sets the value to 0).
brians343bc112013-02-10 01:53:46 +0000152// Returns 0 if it was set before and 1 if it wasn't.
Brian Silvermand41b4422013-09-01 14:02:33 -0700153// Can not fail.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400154int futex_unset(aos_futex *m);
Brian Silvermanaf221b82013-09-01 13:57:50 -0700155
156// The condition_ functions implement condition variable support. The API is
Brian Silverman797e71e2013-09-06 17:29:39 -0700157// similar to the pthreads api and works the same way. The same m argument must
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700158// be passed in for all calls to all of the condition_ functions with a given c.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400159// They do have the potential for spurious wakeups.
Brian Silvermanaf221b82013-09-01 13:57:50 -0700160
161// Wait for the condition variable to be signalled. m will be unlocked
Brian Silvermaneeb62ca2013-09-11 15:08:03 -0700162// atomically with actually starting to wait. m is guaranteed to be locked when
163// this function returns.
164// NOTE: The relocking of m is not atomic with stopping the actual wait and
165// other process(es) may lock (+unlock) the mutex first.
Austin Schuh0ad2b6f2019-06-09 21:27:07 -0700166// Returns 0 on success, 1 if the previous owner died or -1 if we timed out.
167// Will only return -1 on timeout if end_time is not null.
168int condition_wait(aos_condition *c, struct aos_mutex *m,
169 struct timespec *end_time)
Brian Silvermandc1eb272014-08-19 14:25:59 -0400170 __attribute__((warn_unused_result));
Brian Silvermanaf221b82013-09-01 13:57:50 -0700171// If any other processes are condition_waiting on c, wake 1 of them. Does not
Brian Silverman797e71e2013-09-06 17:29:39 -0700172// require m to be locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400173// NOTE: There is a small chance that this will wake more than just 1 waiter.
174void condition_signal(aos_condition *c, struct aos_mutex *m);
Brian Silverman797e71e2013-09-06 17:29:39 -0700175// Wakes all processes that are condition_waiting on c. Does not require m to be
176// locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -0400177void condition_broadcast(aos_condition *c, struct aos_mutex *m);
brians343bc112013-02-10 01:53:46 +0000178
179#ifdef __cplusplus
180}
Brian Silverman71c55c52014-08-19 14:31:59 -0400181
182namespace aos {
183namespace linux_code {
184namespace ipc_lib {
185
Brian Silverman71c55c52014-08-19 14:31:59 -0400186// Set the offset to use for putting addresses into the robust list.
187// This is necessary to work around a kernel bug where it hangs when trying to
188// deal with a futex on the robust list when its memory has been changed to
189// read-only.
190void SetRobustListOffset(ptrdiff_t offset);
191
192// Returns true if there are any mutexes still locked by this task.
193// This is mainly useful for verifying tests don't mess up other ones by leaving
194// now-freed but still locked mutexes around.
195bool HaveLockedMutexes();
196
197} // namespace ipc_lib
198} // namespace linux_code
199} // namespace aos
200
brians343bc112013-02-10 01:53:46 +0000201#endif // __cplusplus
202
John Park398c74a2018-10-20 21:17:39 -0700203#endif // AOS_IPC_LIB_SYNC_H_