John Park | 398c74a | 2018-10-20 21:17:39 -0700 | [diff] [blame] | 1 | #ifndef AOS_IPC_LIB_SYNC_H_ |
| 2 | #define AOS_IPC_LIB_SYNC_H_ |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 3 | |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 4 | #include <signal.h> |
Austin Schuh | 0ad2b6f | 2019-06-09 21:27:07 -0700 | [diff] [blame] | 5 | #include <stdbool.h> |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 6 | #include <stddef.h> |
Austin Schuh | 0ad2b6f | 2019-06-09 21:27:07 -0700 | [diff] [blame] | 7 | #include <stdint.h> |
| 8 | #include <stdlib.h> |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 9 | |
| 10 | #ifdef __cplusplus |
| 11 | extern "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 Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 19 | // Have to remember to align structs containing it (recursively) to sizeof(int). |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 20 | // Valid initial values for use with futex_ functions are 0 (unset) and 1 (set). |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 21 | // The value should not be changed after multiple processes have started |
| 22 | // accessing an instance except through the functions declared in this file. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 23 | typedef uint32_t aos_futex __attribute__((aligned(sizeof(int)))); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 24 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 25 | // For use with the condition_ functions. |
| 26 | // No initialization is necessary. |
| 27 | typedef aos_futex aos_condition; |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 28 | |
Brian Silverman | 27af1f6 | 2019-11-18 12:04:48 -0800 | [diff] [blame] | 29 | // For use with the mutex_ or death_notification_ functions. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 30 | // futex must be initialized to 0. |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 31 | // No initialization is necessary for next and previous. |
| 32 | // Under ThreadSanitizer, pthread_mutex_init must be initialized to false. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 33 | // 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. |
| 36 | struct aos_mutex { |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 37 | // 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 Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 45 | aos_futex futex; |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 46 | #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 Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 59 | }; |
| 60 | |
| 61 | // The mutex_ functions are designed to be used as mutexes. A mutex can only be |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 62 | // 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 Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 67 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 68 | // All of these return 1 if the previous owner died with it held, 2 if |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 69 | // 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 Silverman | d41b442 | 2013-09-01 14:02:33 -0700 | [diff] [blame] | 71 | // |
| 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 Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 74 | int mutex_lock(struct aos_mutex *m) __attribute__((warn_unused_result)); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 75 | // Returns 2 if it timed out or 1 if interrupted by a signal. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 76 | int 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). |
| 80 | int mutex_grab(struct aos_mutex *m) __attribute__((warn_unused_result)); |
| 81 | // LOG(FATAL)s for multiple unlocking. |
| 82 | void mutex_unlock(struct aos_mutex *m); |
| 83 | // Does not block waiting for the mutex. |
| 84 | int 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. |
| 90 | bool mutex_islocked(const aos_mutex *m); |
Brian Silverman | 27af1f6 | 2019-11-18 12:04:48 -0800 | [diff] [blame] | 91 | |
| 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. |
Brian Silverman | 2484eea | 2019-12-21 16:48:46 -0800 | [diff] [blame^] | 98 | // This can only be called once after initializing *m to 0. |
Brian Silverman | 27af1f6 | 2019-11-18 12:04:48 -0800 | [diff] [blame] | 99 | void 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). |
| 103 | void 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. |
| 107 | inline bool death_notification_is_held(aos_mutex *m) { |
| 108 | return mutex_islocked(m); |
| 109 | } |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 110 | #endif |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 111 | |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 112 | // The futex_ functions are similar to the mutex_ ones but different. |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 113 | // They are designed for signalling when something happens (possibly to |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 114 | // 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 Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 119 | // |
Brian Silverman | 5f8c492 | 2014-02-11 21:22:38 -0800 | [diff] [blame] | 120 | // Another name for this kind of synchronization mechanism is a "notification". |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 121 | // Python calls it an "event". |
Brian Silverman | 5f8c492 | 2014-02-11 21:22:38 -0800 | [diff] [blame] | 122 | // |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 123 | // 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 Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 129 | // 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. |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 133 | |
Brian Silverman | 408511d | 2016-09-10 16:12:02 -0400 | [diff] [blame] | 134 | // Wait for the futex to be set. Will return immediately if it's already set |
| 135 | // (after a syscall). |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 136 | // Returns 0 if successful or it was already set, 1 if interrupted by a signal, |
Brian Silverman | 408511d | 2016-09-10 16:12:02 -0400 | [diff] [blame] | 137 | // or -1 with an error in errno. Can return 0 spuriously. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 138 | int futex_wait(aos_futex *m) __attribute__((warn_unused_result)); |
| 139 | // The same as futex_wait except returns 2 if it times out. |
| 140 | int futex_wait_timeout(aos_futex *m, const struct timespec *timeout) |
| 141 | __attribute__((warn_unused_result)); |
| 142 | |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 143 | // Set the futex and wake up anybody waiting on it. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 144 | // Returns the number that were woken or -1 with an error in errno. |
Brian Silverman | d41b442 | 2013-09-01 14:02:33 -0700 | [diff] [blame] | 145 | // |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 146 | // This will always wake up all waiters at the same time and set the value to 1. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 147 | int futex_set(aos_futex *m); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 148 | // Same as above except lets something other than 1 be used as the final value. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 149 | int futex_set_value(aos_futex *m, aos_futex value); |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 150 | // Unsets the futex (sets the value to 0). |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 151 | // Returns 0 if it was set before and 1 if it wasn't. |
Brian Silverman | d41b442 | 2013-09-01 14:02:33 -0700 | [diff] [blame] | 152 | // Can not fail. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 153 | int futex_unset(aos_futex *m); |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 154 | |
| 155 | // The condition_ functions implement condition variable support. The API is |
Brian Silverman | 797e71e | 2013-09-06 17:29:39 -0700 | [diff] [blame] | 156 | // similar to the pthreads api and works the same way. The same m argument must |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 157 | // be passed in for all calls to all of the condition_ functions with a given c. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 158 | // They do have the potential for spurious wakeups. |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 159 | |
| 160 | // Wait for the condition variable to be signalled. m will be unlocked |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 161 | // 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 Schuh | 0ad2b6f | 2019-06-09 21:27:07 -0700 | [diff] [blame] | 165 | // 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. |
| 167 | int condition_wait(aos_condition *c, struct aos_mutex *m, |
| 168 | struct timespec *end_time) |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 169 | __attribute__((warn_unused_result)); |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 170 | // If any other processes are condition_waiting on c, wake 1 of them. Does not |
Brian Silverman | 797e71e | 2013-09-06 17:29:39 -0700 | [diff] [blame] | 171 | // require m to be locked. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 172 | // NOTE: There is a small chance that this will wake more than just 1 waiter. |
| 173 | void condition_signal(aos_condition *c, struct aos_mutex *m); |
Brian Silverman | 797e71e | 2013-09-06 17:29:39 -0700 | [diff] [blame] | 174 | // Wakes all processes that are condition_waiting on c. Does not require m to be |
| 175 | // locked. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 176 | void condition_broadcast(aos_condition *c, struct aos_mutex *m); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 177 | |
| 178 | #ifdef __cplusplus |
| 179 | } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 180 | |
| 181 | namespace aos { |
| 182 | namespace linux_code { |
| 183 | namespace ipc_lib { |
| 184 | |
| 185 | typedef void (*FutexAccessorObserver)(void *address, bool write); |
| 186 | |
| 187 | // Set functions which get called before and after all futex operations. |
| 188 | void 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. |
| 195 | void 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. |
| 200 | bool HaveLockedMutexes(); |
| 201 | |
| 202 | } // namespace ipc_lib |
| 203 | } // namespace linux_code |
| 204 | } // namespace aos |
| 205 | |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 206 | #endif // __cplusplus |
| 207 | |
John Park | 398c74a | 2018-10-20 21:17:39 -0700 | [diff] [blame] | 208 | #endif // AOS_IPC_LIB_SYNC_H_ |