blob: a2af7ae50eca5e30591c09280698af13e941a19a [file] [log] [blame]
John Park33858a32018-09-28 23:05:48 -07001#ifndef AOS_MUTEX_H_
2#define AOS_MUTEX_H_
brians343bc112013-02-10 01:53:46 +00003
Alex Perrycb7da4b2019-08-28 19:35:56 -07004#include "aos/ipc_lib/aos_sync.h"
John Park33858a32018-09-28 23:05:48 -07005#include "aos/macros.h"
6#include "aos/type_traits/type_traits.h"
Alex Perrycb7da4b2019-08-28 19:35:56 -07007#include "glog/logging.h"
brians343bc112013-02-10 01:53:46 +00008
9namespace aos {
10
Brian Silverman08661c72013-09-01 17:24:38 -070011class Condition;
12
Brian Silvermandc1eb272014-08-19 14:25:59 -040013// An abstraction of a mutex that is easy to implement for environments other
14// than Linux too.
15// If there are multiple threads or processes contending for the mutex,
brians343bc112013-02-10 01:53:46 +000016// higher priority ones will succeed in locking first,
17// and tasks of equal priorities have the same chance of getting the lock.
Brian Silvermandc1eb272014-08-19 14:25:59 -040018// To deal with priority inversion, the linux implementation does priority
19// inheritance.
20// Before destroying a mutex, it is important to make sure it isn't locked.
21// Otherwise, the destructor will LOG(FATAL).
brians343bc112013-02-10 01:53:46 +000022class Mutex {
23 public:
Daniel Petti88a15662015-04-12 17:42:22 -040024 // States that signify the result of TryLock.
Brian Silvermandc1eb272014-08-19 14:25:59 -040025 enum class State {
Daniel Petti88a15662015-04-12 17:42:22 -040026 // The mutex was acquired successfully.
27 kLocked,
28 // TryLock tried to grab the mutex and failed.
Brian Silverman71c55c52014-08-19 14:31:59 -040029 kLockFailed,
30 // The previous owner of the mutex died.
31 kOwnerDied,
Brian Silvermandc1eb272014-08-19 14:25:59 -040032 };
33
brians343bc112013-02-10 01:53:46 +000034 // Creates an unlocked mutex.
Austin Schuhceb0e772018-01-03 21:10:57 -080035 Mutex() : impl_() {
36 static_assert(shm_ok<Mutex>::value,
37 "Mutex is not safe for use in shared memory.");
38 }
Brian Silvermandc1eb272014-08-19 14:25:59 -040039 // Verifies that it isn't locked.
40 //
41 // This is important because freeing a locked mutex means there is freed
42 // memory in the middle of the robust list, which breaks things horribly.
Austin Schuhceb0e772018-01-03 21:10:57 -080043 ~Mutex() = default;
Brian Silvermandc1eb272014-08-19 14:25:59 -040044
brians343bc112013-02-10 01:53:46 +000045 // Locks the mutex. If it fails, it calls LOG(FATAL).
Brian Silverman71c55c52014-08-19 14:31:59 -040046 // Returns true if the previous owner died instead of unlocking nicely.
Brian Silvermandc1eb272014-08-19 14:25:59 -040047 bool Lock() __attribute__((warn_unused_result));
brians343bc112013-02-10 01:53:46 +000048 // Unlocks the mutex. Fails like Lock.
Brian Silverman6da04272014-05-18 18:47:48 -070049 // Multiple unlocking is undefined.
brians343bc112013-02-10 01:53:46 +000050 void Unlock();
51 // Locks the mutex unless it is already locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -040052 // Returns the new state of the mutex.
brians343bc112013-02-10 01:53:46 +000053 // Doesn't wait for the mutex to be unlocked if it is locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -040054 State TryLock() __attribute__((warn_unused_result));
brians343bc112013-02-10 01:53:46 +000055
Brian Silverman1dfe48b2014-09-06 16:13:02 -040056 // Returns true iff the current task has this mutex locked.
57 // This is mainly for IPCRecursiveMutexLocker to use.
58 bool OwnedBySelf() const;
59
brians343bc112013-02-10 01:53:46 +000060 private:
Brian Silvermandc1eb272014-08-19 14:25:59 -040061 aos_mutex impl_;
Brian Silverman08661c72013-09-01 17:24:38 -070062
63 friend class Condition; // for access to impl_
brians343bc112013-02-10 01:53:46 +000064};
65
66// A class that locks a Mutex when constructed and unlocks it when destructed.
67// Designed to be used as a local variable so that
68// the mutex will be unlocked when the scope is exited.
Brian Silvermandc1eb272014-08-19 14:25:59 -040069// This one immediately Dies if the previous owner died. This makes it a good
70// choice for mutexes that are only used within a single process, but NOT for
71// mutexes shared by multiple processes. For those, use IPCMutexLocker.
brians343bc112013-02-10 01:53:46 +000072class MutexLocker {
73 public:
74 explicit MutexLocker(Mutex *mutex) : mutex_(mutex) {
Brian Silvermandc1eb272014-08-19 14:25:59 -040075 if (__builtin_expect(mutex_->Lock(), false)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070076 LOG(FATAL) << "previous owner of mutex " << this
77 << " died but it shouldn't be able to";
Brian Silvermandc1eb272014-08-19 14:25:59 -040078 }
brians343bc112013-02-10 01:53:46 +000079 }
80 ~MutexLocker() {
81 mutex_->Unlock();
82 }
83
84 private:
Brian Silvermandc1eb272014-08-19 14:25:59 -040085 Mutex *const mutex_;
86
brians343bc112013-02-10 01:53:46 +000087 DISALLOW_COPY_AND_ASSIGN(MutexLocker);
88};
Brian Silvermandc1eb272014-08-19 14:25:59 -040089
90// A version of MutexLocker which reports the previous owner dying instead of
91// immediately LOG(FATAL)ing.
92class IPCMutexLocker {
Brian Silvermand41b4422013-09-01 14:02:33 -070093 public:
Brian Silvermandc1eb272014-08-19 14:25:59 -040094 explicit IPCMutexLocker(Mutex *mutex)
95 : mutex_(mutex), owner_died_(mutex_->Lock()) {}
96 ~IPCMutexLocker() {
97 if (__builtin_expect(!owner_died_checked_, false)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070098 LOG(FATAL) << "nobody checked if the previous owner of mutex " << this
99 << " died";
Brian Silvermandc1eb272014-08-19 14:25:59 -0400100 }
Brian Silvermand41b4422013-09-01 14:02:33 -0700101 mutex_->Unlock();
102 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400103
104 // Whether or not the previous owner died. If this is not called at least
Alex Perrycb7da4b2019-08-28 19:35:56 -0700105 // once, the destructor will LOG(FATAL)
Brian Silvermandc1eb272014-08-19 14:25:59 -0400106 __attribute__((warn_unused_result)) bool owner_died() {
107 owner_died_checked_ = true;
108 return __builtin_expect(owner_died_, false);
Brian Silvermand41b4422013-09-01 14:02:33 -0700109 }
110
111 private:
Brian Silvermandc1eb272014-08-19 14:25:59 -0400112 Mutex *const mutex_;
113 const bool owner_died_;
114 bool owner_died_checked_ = false;
115
116 DISALLOW_COPY_AND_ASSIGN(IPCMutexLocker);
Brian Silvermand41b4422013-09-01 14:02:33 -0700117};
brians343bc112013-02-10 01:53:46 +0000118
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400119// A version of IPCMutexLocker which only locks (and unlocks) the mutex if the
120// current task does not already hold it.
121class IPCRecursiveMutexLocker {
122 public:
123 explicit IPCRecursiveMutexLocker(Mutex *mutex)
124 : mutex_(mutex),
125 locked_(!mutex_->OwnedBySelf()),
126 owner_died_(locked_ ? mutex_->Lock() : false) {}
127 ~IPCRecursiveMutexLocker() {
128 if (__builtin_expect(!owner_died_checked_, false)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700129 LOG(FATAL) << "nobody checked if the previous owner of mutex " << this
130 << " died";
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400131 }
132 if (locked_) mutex_->Unlock();
133 }
134
135 // Whether or not the previous owner died. If this is not called at least
Alex Perrycb7da4b2019-08-28 19:35:56 -0700136 // once, the destructor will LOG(FATAL)
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400137 __attribute__((warn_unused_result)) bool owner_died() {
138 owner_died_checked_ = true;
139 return __builtin_expect(owner_died_, false);
140 }
141
142 private:
143 Mutex *const mutex_;
144 const bool locked_, owner_died_;
145 bool owner_died_checked_ = false;
146
147 DISALLOW_COPY_AND_ASSIGN(IPCRecursiveMutexLocker);
148};
149
brians343bc112013-02-10 01:53:46 +0000150} // namespace aos
151
John Park33858a32018-09-28 23:05:48 -0700152#endif // AOS_MUTEX_H_