blob: cc180f5e29abbb78a4180b813b507c535f67a99d [file] [log] [blame]
brians343bc112013-02-10 01:53:46 +00001#ifndef AOS_COMMON_MUTEX_H_
2#define AOS_COMMON_MUTEX_H_
3
brians343bc112013-02-10 01:53:46 +00004#include "aos/common/macros.h"
Brian Silvermandc1eb272014-08-19 14:25:59 -04005#include "aos/common/die.h"
Brian Silverman92c3f1e2015-12-08 20:21:31 -05006#include "aos/linux_code/ipc_lib/aos_sync.h"
brians343bc112013-02-10 01:53:46 +00007
8namespace aos {
9
Brian Silverman08661c72013-09-01 17:24:38 -070010class Condition;
11
Brian Silvermandc1eb272014-08-19 14:25:59 -040012// An abstraction of a mutex that is easy to implement for environments other
13// than Linux too.
14// If there are multiple threads or processes contending for the mutex,
brians343bc112013-02-10 01:53:46 +000015// higher priority ones will succeed in locking first,
16// and tasks of equal priorities have the same chance of getting the lock.
Brian Silvermandc1eb272014-08-19 14:25:59 -040017// To deal with priority inversion, the linux implementation does priority
18// inheritance.
19// Before destroying a mutex, it is important to make sure it isn't locked.
20// Otherwise, the destructor will LOG(FATAL).
brians343bc112013-02-10 01:53:46 +000021class Mutex {
22 public:
Daniel Petti88a15662015-04-12 17:42:22 -040023 // States that signify the result of TryLock.
Brian Silvermandc1eb272014-08-19 14:25:59 -040024 enum class State {
Daniel Petti88a15662015-04-12 17:42:22 -040025 // The mutex was acquired successfully.
26 kLocked,
27 // TryLock tried to grab the mutex and failed.
Brian Silverman71c55c52014-08-19 14:31:59 -040028 kLockFailed,
29 // The previous owner of the mutex died.
30 kOwnerDied,
Brian Silvermandc1eb272014-08-19 14:25:59 -040031 };
32
brians343bc112013-02-10 01:53:46 +000033 // Creates an unlocked mutex.
34 Mutex();
Brian Silvermandc1eb272014-08-19 14:25:59 -040035 // Verifies that it isn't locked.
36 //
37 // This is important because freeing a locked mutex means there is freed
38 // memory in the middle of the robust list, which breaks things horribly.
brians343bc112013-02-10 01:53:46 +000039 ~Mutex();
Brian Silvermandc1eb272014-08-19 14:25:59 -040040
brians343bc112013-02-10 01:53:46 +000041 // Locks the mutex. If it fails, it calls LOG(FATAL).
Brian Silverman71c55c52014-08-19 14:31:59 -040042 // Returns true if the previous owner died instead of unlocking nicely.
Brian Silvermandc1eb272014-08-19 14:25:59 -040043 bool Lock() __attribute__((warn_unused_result));
brians343bc112013-02-10 01:53:46 +000044 // Unlocks the mutex. Fails like Lock.
Brian Silverman6da04272014-05-18 18:47:48 -070045 // Multiple unlocking is undefined.
brians343bc112013-02-10 01:53:46 +000046 void Unlock();
47 // Locks the mutex unless it is already locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -040048 // Returns the new state of the mutex.
brians343bc112013-02-10 01:53:46 +000049 // Doesn't wait for the mutex to be unlocked if it is locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -040050 State TryLock() __attribute__((warn_unused_result));
brians343bc112013-02-10 01:53:46 +000051
Brian Silverman1dfe48b2014-09-06 16:13:02 -040052 // Returns true iff the current task has this mutex locked.
53 // This is mainly for IPCRecursiveMutexLocker to use.
54 bool OwnedBySelf() const;
55
brians343bc112013-02-10 01:53:46 +000056 private:
Brian Silvermandc1eb272014-08-19 14:25:59 -040057 aos_mutex impl_;
Brian Silverman08661c72013-09-01 17:24:38 -070058
59 friend class Condition; // for access to impl_
brians343bc112013-02-10 01:53:46 +000060};
61
62// A class that locks a Mutex when constructed and unlocks it when destructed.
63// Designed to be used as a local variable so that
64// the mutex will be unlocked when the scope is exited.
Brian Silvermandc1eb272014-08-19 14:25:59 -040065// This one immediately Dies if the previous owner died. This makes it a good
66// choice for mutexes that are only used within a single process, but NOT for
67// mutexes shared by multiple processes. For those, use IPCMutexLocker.
brians343bc112013-02-10 01:53:46 +000068class MutexLocker {
69 public:
70 explicit MutexLocker(Mutex *mutex) : mutex_(mutex) {
Brian Silvermandc1eb272014-08-19 14:25:59 -040071 if (__builtin_expect(mutex_->Lock(), false)) {
72 ::aos::Die("previous owner of mutex %p died but it shouldn't be able to",
73 this);
74 }
brians343bc112013-02-10 01:53:46 +000075 }
76 ~MutexLocker() {
77 mutex_->Unlock();
78 }
79
80 private:
Brian Silvermandc1eb272014-08-19 14:25:59 -040081 Mutex *const mutex_;
82
brians343bc112013-02-10 01:53:46 +000083 DISALLOW_COPY_AND_ASSIGN(MutexLocker);
84};
Brian Silvermandc1eb272014-08-19 14:25:59 -040085
86// A version of MutexLocker which reports the previous owner dying instead of
87// immediately LOG(FATAL)ing.
88class IPCMutexLocker {
Brian Silvermand41b4422013-09-01 14:02:33 -070089 public:
Brian Silvermandc1eb272014-08-19 14:25:59 -040090 explicit IPCMutexLocker(Mutex *mutex)
91 : mutex_(mutex), owner_died_(mutex_->Lock()) {}
92 ~IPCMutexLocker() {
93 if (__builtin_expect(!owner_died_checked_, false)) {
94 ::aos::Die("nobody checked if the previous owner of mutex %p died", this);
95 }
Brian Silvermand41b4422013-09-01 14:02:33 -070096 mutex_->Unlock();
97 }
Brian Silvermandc1eb272014-08-19 14:25:59 -040098
99 // Whether or not the previous owner died. If this is not called at least
100 // once, the destructor will ::aos::Die.
101 __attribute__((warn_unused_result)) bool owner_died() {
102 owner_died_checked_ = true;
103 return __builtin_expect(owner_died_, false);
Brian Silvermand41b4422013-09-01 14:02:33 -0700104 }
105
106 private:
Brian Silvermandc1eb272014-08-19 14:25:59 -0400107 Mutex *const mutex_;
108 const bool owner_died_;
109 bool owner_died_checked_ = false;
110
111 DISALLOW_COPY_AND_ASSIGN(IPCMutexLocker);
Brian Silvermand41b4422013-09-01 14:02:33 -0700112};
brians343bc112013-02-10 01:53:46 +0000113
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400114// A version of IPCMutexLocker which only locks (and unlocks) the mutex if the
115// current task does not already hold it.
116class IPCRecursiveMutexLocker {
117 public:
118 explicit IPCRecursiveMutexLocker(Mutex *mutex)
119 : mutex_(mutex),
120 locked_(!mutex_->OwnedBySelf()),
121 owner_died_(locked_ ? mutex_->Lock() : false) {}
122 ~IPCRecursiveMutexLocker() {
123 if (__builtin_expect(!owner_died_checked_, false)) {
124 ::aos::Die("nobody checked if the previous owner of mutex %p died", this);
125 }
126 if (locked_) mutex_->Unlock();
127 }
128
129 // Whether or not the previous owner died. If this is not called at least
130 // once, the destructor will ::aos::Die.
131 __attribute__((warn_unused_result)) bool owner_died() {
132 owner_died_checked_ = true;
133 return __builtin_expect(owner_died_, false);
134 }
135
136 private:
137 Mutex *const mutex_;
138 const bool locked_, owner_died_;
139 bool owner_died_checked_ = false;
140
141 DISALLOW_COPY_AND_ASSIGN(IPCRecursiveMutexLocker);
142};
143
brians343bc112013-02-10 01:53:46 +0000144} // namespace aos
145
146#endif // AOS_COMMON_MUTEX_H_