blob: c40917aa40c313e57db27a1cd3b8ee0fa27a16a2 [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
Stephan Pleines36fc0402024-05-30 20:28:02 -07004#include <ostream>
5
Austin Schuh99f7c6a2024-06-25 22:07:44 -07006#include "absl/log/check.h"
7#include "absl/log/log.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07008
Alex Perrycb7da4b2019-08-28 19:35:56 -07009#include "aos/ipc_lib/aos_sync.h"
John Park33858a32018-09-28 23:05:48 -070010#include "aos/macros.h"
11#include "aos/type_traits/type_traits.h"
brians343bc112013-02-10 01:53:46 +000012
13namespace aos {
Brian Silvermandc1eb272014-08-19 14:25:59 -040014// An abstraction of a mutex that is easy to implement for environments other
15// than Linux too.
16// If there are multiple threads or processes contending for the mutex,
brians343bc112013-02-10 01:53:46 +000017// higher priority ones will succeed in locking first,
18// and tasks of equal priorities have the same chance of getting the lock.
Brian Silvermandc1eb272014-08-19 14:25:59 -040019// To deal with priority inversion, the linux implementation does priority
20// inheritance.
21// Before destroying a mutex, it is important to make sure it isn't locked.
22// Otherwise, the destructor will LOG(FATAL).
brians343bc112013-02-10 01:53:46 +000023class Mutex {
24 public:
Daniel Petti88a15662015-04-12 17:42:22 -040025 // States that signify the result of TryLock.
Brian Silvermandc1eb272014-08-19 14:25:59 -040026 enum class State {
Daniel Petti88a15662015-04-12 17:42:22 -040027 // The mutex was acquired successfully.
28 kLocked,
29 // TryLock tried to grab the mutex and failed.
Brian Silverman71c55c52014-08-19 14:31:59 -040030 kLockFailed,
31 // The previous owner of the mutex died.
32 kOwnerDied,
Brian Silvermandc1eb272014-08-19 14:25:59 -040033 };
34
brians343bc112013-02-10 01:53:46 +000035 // Creates an unlocked mutex.
Austin Schuhceb0e772018-01-03 21:10:57 -080036 Mutex() : impl_() {
37 static_assert(shm_ok<Mutex>::value,
38 "Mutex is not safe for use in shared memory.");
39 }
Brian Silvermandc1eb272014-08-19 14:25:59 -040040 // Verifies that it isn't locked.
41 //
42 // This is important because freeing a locked mutex means there is freed
43 // memory in the middle of the robust list, which breaks things horribly.
Austin Schuhceb0e772018-01-03 21:10:57 -080044 ~Mutex() = default;
Brian Silvermandc1eb272014-08-19 14:25:59 -040045
brians343bc112013-02-10 01:53:46 +000046 // Locks the mutex. If it fails, it calls LOG(FATAL).
Brian Silverman71c55c52014-08-19 14:31:59 -040047 // Returns true if the previous owner died instead of unlocking nicely.
Brian Silvermandc1eb272014-08-19 14:25:59 -040048 bool Lock() __attribute__((warn_unused_result));
brians343bc112013-02-10 01:53:46 +000049 // Unlocks the mutex. Fails like Lock.
Brian Silverman6da04272014-05-18 18:47:48 -070050 // Multiple unlocking is undefined.
brians343bc112013-02-10 01:53:46 +000051 void Unlock();
52 // Locks the mutex unless it is already locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -040053 // Returns the new state of the mutex.
brians343bc112013-02-10 01:53:46 +000054 // Doesn't wait for the mutex to be unlocked if it is locked.
Brian Silvermandc1eb272014-08-19 14:25:59 -040055 State TryLock() __attribute__((warn_unused_result));
brians343bc112013-02-10 01:53:46 +000056
Brian Silverman1dfe48b2014-09-06 16:13:02 -040057 // Returns true iff the current task has this mutex locked.
58 // This is mainly for IPCRecursiveMutexLocker to use.
59 bool OwnedBySelf() const;
60
brians343bc112013-02-10 01:53:46 +000061 private:
Brian Silvermandc1eb272014-08-19 14:25:59 -040062 aos_mutex impl_;
Brian Silverman08661c72013-09-01 17:24:38 -070063
64 friend class Condition; // for access to impl_
brians343bc112013-02-10 01:53:46 +000065};
66
67// A class that locks a Mutex when constructed and unlocks it when destructed.
68// Designed to be used as a local variable so that
69// the mutex will be unlocked when the scope is exited.
Brian Silvermandc1eb272014-08-19 14:25:59 -040070// This one immediately Dies if the previous owner died. This makes it a good
71// choice for mutexes that are only used within a single process, but NOT for
72// mutexes shared by multiple processes. For those, use IPCMutexLocker.
brians343bc112013-02-10 01:53:46 +000073class MutexLocker {
74 public:
75 explicit MutexLocker(Mutex *mutex) : mutex_(mutex) {
Brian Silvermandc1eb272014-08-19 14:25:59 -040076 if (__builtin_expect(mutex_->Lock(), false)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070077 LOG(FATAL) << "previous owner of mutex " << this
78 << " died but it shouldn't be able to";
Brian Silvermandc1eb272014-08-19 14:25:59 -040079 }
brians343bc112013-02-10 01:53:46 +000080 }
Austin Schuh60e77942022-05-16 17:48:24 -070081 ~MutexLocker() { mutex_->Unlock(); }
brians343bc112013-02-10 01:53:46 +000082
83 private:
Brian Silvermandc1eb272014-08-19 14:25:59 -040084 Mutex *const mutex_;
85
brians343bc112013-02-10 01:53:46 +000086 DISALLOW_COPY_AND_ASSIGN(MutexLocker);
87};
Brian Silvermandc1eb272014-08-19 14:25:59 -040088
89// A version of MutexLocker which reports the previous owner dying instead of
90// immediately LOG(FATAL)ing.
91class IPCMutexLocker {
Brian Silvermand41b4422013-09-01 14:02:33 -070092 public:
Brian Silvermandc1eb272014-08-19 14:25:59 -040093 explicit IPCMutexLocker(Mutex *mutex)
94 : mutex_(mutex), owner_died_(mutex_->Lock()) {}
95 ~IPCMutexLocker() {
96 if (__builtin_expect(!owner_died_checked_, false)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -070097 LOG(FATAL) << "nobody checked if the previous owner of mutex " << this
98 << " died";
Brian Silvermandc1eb272014-08-19 14:25:59 -040099 }
Brian Silvermand41b4422013-09-01 14:02:33 -0700100 mutex_->Unlock();
101 }
Brian Silvermandc1eb272014-08-19 14:25:59 -0400102
103 // Whether or not the previous owner died. If this is not called at least
Alex Perrycb7da4b2019-08-28 19:35:56 -0700104 // once, the destructor will LOG(FATAL)
Brian Silvermandc1eb272014-08-19 14:25:59 -0400105 __attribute__((warn_unused_result)) bool owner_died() {
106 owner_died_checked_ = true;
107 return __builtin_expect(owner_died_, false);
Brian Silvermand41b4422013-09-01 14:02:33 -0700108 }
109
110 private:
Brian Silvermandc1eb272014-08-19 14:25:59 -0400111 Mutex *const mutex_;
112 const bool owner_died_;
113 bool owner_died_checked_ = false;
114
115 DISALLOW_COPY_AND_ASSIGN(IPCMutexLocker);
Brian Silvermand41b4422013-09-01 14:02:33 -0700116};
brians343bc112013-02-10 01:53:46 +0000117
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400118// A version of IPCMutexLocker which only locks (and unlocks) the mutex if the
119// current task does not already hold it.
120class IPCRecursiveMutexLocker {
121 public:
122 explicit IPCRecursiveMutexLocker(Mutex *mutex)
123 : mutex_(mutex),
124 locked_(!mutex_->OwnedBySelf()),
125 owner_died_(locked_ ? mutex_->Lock() : false) {}
126 ~IPCRecursiveMutexLocker() {
127 if (__builtin_expect(!owner_died_checked_, false)) {
Alex Perrycb7da4b2019-08-28 19:35:56 -0700128 LOG(FATAL) << "nobody checked if the previous owner of mutex " << this
129 << " died";
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400130 }
131 if (locked_) mutex_->Unlock();
132 }
133
134 // Whether or not the previous owner died. If this is not called at least
Alex Perrycb7da4b2019-08-28 19:35:56 -0700135 // once, the destructor will LOG(FATAL)
Brian Silverman1dfe48b2014-09-06 16:13:02 -0400136 __attribute__((warn_unused_result)) bool owner_died() {
137 owner_died_checked_ = true;
138 return __builtin_expect(owner_died_, false);
139 }
140
141 private:
142 Mutex *const mutex_;
143 const bool locked_, owner_died_;
144 bool owner_died_checked_ = false;
145
146 DISALLOW_COPY_AND_ASSIGN(IPCRecursiveMutexLocker);
147};
148
brians343bc112013-02-10 01:53:46 +0000149} // namespace aos
150
John Park33858a32018-09-28 23:05:48 -0700151#endif // AOS_MUTEX_H_