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 | |
| 4 | #include <stdlib.h> |
| 5 | #include <signal.h> |
| 6 | #include <stdint.h> |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 7 | #include <stddef.h> |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 8 | |
| 9 | #ifdef __cplusplus |
| 10 | extern "C" { |
| 11 | #endif // __cplusplus |
| 12 | |
| 13 | // TODO(brians) add client requests to make helgrind useful with this code |
| 14 | // <http://www.valgrind.org/docs/manual/hg-manual.html#hg-manual.client-requests> |
| 15 | // and <http://www.valgrind.org/docs/manual/drd-manual.html#drd-manual.clientreqs> |
| 16 | // list the interesting ones |
| 17 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 18 | // Have to remember to align structs containing it (recursively) to sizeof(int). |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 19 | // 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] | 20 | // The value should not be changed after multiple processes have started |
| 21 | // accessing an instance except through the functions declared in this file. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 22 | typedef uint32_t aos_futex __attribute__((aligned(sizeof(int)))); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 23 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 24 | // For use with the condition_ functions. |
| 25 | // No initialization is necessary. |
| 26 | typedef aos_futex aos_condition; |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 27 | |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 28 | // For use with the mutex_ functions. |
| 29 | // futex must be initialized to 0. |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 30 | // No initialization is necessary for next and previous. |
| 31 | // Under ThreadSanitizer, pthread_mutex_init must be initialized to false. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 32 | // The recommended way to initialize one of these is by memset(3)ing the whole |
| 33 | // thing to 0 or using C++ () initialization to avoid depending on the |
| 34 | // implementation. |
| 35 | struct aos_mutex { |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 36 | // 2 links to get O(1) adds and removes. |
| 37 | // This is &next of another element. |
| 38 | // next (might) have stuff |ed into it to indicate PI futexes and might also |
| 39 | // have an offset (see SetRobustListOffset); previous is an actual pointer |
| 40 | // without any of that. |
| 41 | // next has to stay the first element of this structure. |
| 42 | uintptr_t next; |
| 43 | struct aos_mutex *previous; |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 44 | aos_futex futex; |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 45 | #ifdef AOS_SANITIZER_thread |
| 46 | // Internal pthread mutex which is kept in sync with the actual mutex so tsan |
| 47 | // can understand what's happening and help catch bugs. |
| 48 | pthread_mutex_t pthread_mutex; |
| 49 | #ifndef __cplusplus |
| 50 | // TODO(brian): Remove this once the stupid C code is gone... |
| 51 | #define bool uint8_t |
| 52 | #endif |
| 53 | bool pthread_mutex_init; |
| 54 | #ifndef __cplusplus |
| 55 | #undef bool |
| 56 | #endif |
| 57 | #endif |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 58 | }; |
| 59 | |
| 60 | // 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] | 61 | // unlocked from the same task which originally locked it. Also, if a task dies |
| 62 | // while holding a mutex, the next person who locks it will be notified. After a |
| 63 | // fork(2), any mutexes held will be held ONLY in the parent process. Attempting |
| 64 | // to unlock them from the child will give errors. |
| 65 | // Priority inheritance (aka priority inversion protection) is enabled. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 66 | |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 67 | // 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] | 68 | // interrupted by a signal, 3 if timed out, or 4 if an optional lock fails. Some |
| 69 | // of them (obviously) can never return some of those values. |
Brian Silverman | d41b442 | 2013-09-01 14:02:33 -0700 | [diff] [blame] | 70 | // |
| 71 | // One of the highest priority processes blocked on a given mutex will be the |
| 72 | // one to lock it when it is unlocked. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 73 | int mutex_lock(struct aos_mutex *m) __attribute__((warn_unused_result)); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 74 | // 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] | 75 | int mutex_lock_timeout(struct aos_mutex *m, const struct timespec *timeout) |
| 76 | __attribute__((warn_unused_result)); |
| 77 | // Ignores signals (retries until something other than getting a signal |
| 78 | // happens). |
| 79 | int mutex_grab(struct aos_mutex *m) __attribute__((warn_unused_result)); |
| 80 | // LOG(FATAL)s for multiple unlocking. |
| 81 | void mutex_unlock(struct aos_mutex *m); |
| 82 | // Does not block waiting for the mutex. |
| 83 | int mutex_trylock(struct aos_mutex *m) __attribute__((warn_unused_result)); |
| 84 | #ifdef __cplusplus |
| 85 | // Returns whether or not the mutex is locked by this thread. |
| 86 | // There aren't very many valid uses for this function; the main ones are |
| 87 | // checking mutexes as they are destroyed to catch problems with that early and |
| 88 | // stack-based recursive mutex locking. |
| 89 | bool mutex_islocked(const aos_mutex *m); |
| 90 | #endif |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 91 | |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 92 | // The futex_ functions are similar to the mutex_ ones but different. |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 93 | // They are designed for signalling when something happens (possibly to |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 94 | // multiple listeners). A aos_futex manipulated with them can only be set or |
| 95 | // unset. Also, they can be set/unset/waited on from any task independently of |
| 96 | // who did something first and have no priority inversion protection. |
| 97 | // They return -1 for other error (which will be in errno from futex(2)). |
| 98 | // They have no spurious wakeups (because everybody always gets woken up). |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 99 | // |
Brian Silverman | 5f8c492 | 2014-02-11 21:22:38 -0800 | [diff] [blame] | 100 | // Another name for this kind of synchronization mechanism is a "notification". |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 101 | // Python calls it an "event". |
Brian Silverman | 5f8c492 | 2014-02-11 21:22:38 -0800 | [diff] [blame] | 102 | // |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 103 | // They are different from the condition_ functions in that they do NOT work |
| 104 | // correctly as standard condition variables. While it is possible to keep |
| 105 | // track of the "condition" using the value part of the futex_* functions, the |
| 106 | // obvious implementation has basically the same race condition that condition |
| 107 | // variables are designed to prevent between somebody else grabbing the mutex |
| 108 | // 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] | 109 | // futex's value. A futex is effectively a resettable condition variable with |
| 110 | // the condition being "has it been set"; if you have some other condition (for |
| 111 | // example messages are available to read on a queue), use the condition_ |
| 112 | // functions or there will be race conditions. |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 113 | |
Brian Silverman | 408511d | 2016-09-10 16:12:02 -0400 | [diff] [blame] | 114 | // Wait for the futex to be set. Will return immediately if it's already set |
| 115 | // (after a syscall). |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 116 | // 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] | 117 | // or -1 with an error in errno. Can return 0 spuriously. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 118 | int futex_wait(aos_futex *m) __attribute__((warn_unused_result)); |
| 119 | // The same as futex_wait except returns 2 if it times out. |
| 120 | int futex_wait_timeout(aos_futex *m, const struct timespec *timeout) |
| 121 | __attribute__((warn_unused_result)); |
| 122 | |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 123 | // Set the futex and wake up anybody waiting on it. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 124 | // 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] | 125 | // |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 126 | // 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] | 127 | int futex_set(aos_futex *m); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 128 | // 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] | 129 | int futex_set_value(aos_futex *m, aos_futex value); |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 130 | // Unsets the futex (sets the value to 0). |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 131 | // 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] | 132 | // Can not fail. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 133 | int futex_unset(aos_futex *m); |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 134 | |
| 135 | // The condition_ functions implement condition variable support. The API is |
Brian Silverman | 797e71e | 2013-09-06 17:29:39 -0700 | [diff] [blame] | 136 | // 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] | 137 | // 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] | 138 | // They do have the potential for spurious wakeups. |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 139 | |
| 140 | // Wait for the condition variable to be signalled. m will be unlocked |
Brian Silverman | eeb62ca | 2013-09-11 15:08:03 -0700 | [diff] [blame] | 141 | // atomically with actually starting to wait. m is guaranteed to be locked when |
| 142 | // this function returns. |
| 143 | // NOTE: The relocking of m is not atomic with stopping the actual wait and |
| 144 | // other process(es) may lock (+unlock) the mutex first. |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 145 | // Returns 0 on success or 1 if the previous owner died. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 146 | int condition_wait(aos_condition *c, struct aos_mutex *m) |
| 147 | __attribute__((warn_unused_result)); |
Brian Silverman | af221b8 | 2013-09-01 13:57:50 -0700 | [diff] [blame] | 148 | // 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] | 149 | // require m to be locked. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 150 | // NOTE: There is a small chance that this will wake more than just 1 waiter. |
| 151 | void condition_signal(aos_condition *c, struct aos_mutex *m); |
Brian Silverman | 797e71e | 2013-09-06 17:29:39 -0700 | [diff] [blame] | 152 | // Wakes all processes that are condition_waiting on c. Does not require m to be |
| 153 | // locked. |
Brian Silverman | dc1eb27 | 2014-08-19 14:25:59 -0400 | [diff] [blame] | 154 | void condition_broadcast(aos_condition *c, struct aos_mutex *m); |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 155 | |
| 156 | #ifdef __cplusplus |
| 157 | } |
Brian Silverman | 71c55c5 | 2014-08-19 14:31:59 -0400 | [diff] [blame] | 158 | |
| 159 | namespace aos { |
| 160 | namespace linux_code { |
| 161 | namespace ipc_lib { |
| 162 | |
| 163 | typedef void (*FutexAccessorObserver)(void *address, bool write); |
| 164 | |
| 165 | // Set functions which get called before and after all futex operations. |
| 166 | void SetFutexAccessorObservers(FutexAccessorObserver before, |
| 167 | FutexAccessorObserver after); |
| 168 | |
| 169 | // Set the offset to use for putting addresses into the robust list. |
| 170 | // This is necessary to work around a kernel bug where it hangs when trying to |
| 171 | // deal with a futex on the robust list when its memory has been changed to |
| 172 | // read-only. |
| 173 | void SetRobustListOffset(ptrdiff_t offset); |
| 174 | |
| 175 | // Returns true if there are any mutexes still locked by this task. |
| 176 | // This is mainly useful for verifying tests don't mess up other ones by leaving |
| 177 | // now-freed but still locked mutexes around. |
| 178 | bool HaveLockedMutexes(); |
| 179 | |
| 180 | } // namespace ipc_lib |
| 181 | } // namespace linux_code |
| 182 | } // namespace aos |
| 183 | |
brians | 343bc11 | 2013-02-10 01:53:46 +0000 | [diff] [blame] | 184 | #endif // __cplusplus |
| 185 | |
John Park | 398c74a | 2018-10-20 21:17:39 -0700 | [diff] [blame] | 186 | #endif // AOS_IPC_LIB_SYNC_H_ |