1 /* 2 * Copyright 2016, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef STAGEFRIGHT_FOUNDATION_MUTEXED_H_ 18 #define STAGEFRIGHT_FOUNDATION_MUTEXED_H_ 19 20 #include <utils/Mutex.h> 21 #include <utils/Condition.h> 22 23 namespace android { 24 25 /* 26 * Wrapper class to programmatically protect a structure using a mutex. 27 * 28 * Mutexed<> objects contain a built-in mutex. Protection is enforced because the structure can 29 * only be accessed by locking the mutex first. 30 * 31 * Usage: 32 * 33 * struct DataToProtect { 34 * State(int var1) : mVar1(var1), mVar2(0) { } 35 * int mVar1; 36 * int mVar2; 37 * Condition mCondition1; 38 * }; 39 * 40 * Mutexed<DataToProtect> mProtectedData; 41 * 42 * // members are inaccessible via mProtectedData directly 43 * 44 * void someFunction() { 45 * Mutexed<DataToProtect>::Locked data(mProtectedData); // access the protected data 46 * 47 * // the mutex is locked here, so accessing the data is safe 48 * 49 * if (data->mVar1 < 5) { 50 * ++data->mVar2; 51 * } 52 * 53 * // if you need to temporarily unlock the mutex, you can use unlock/relock mutex locally 54 * // using the accessor object. 55 * 56 * data.unlock(); 57 * 58 * // data is inaccessible here 59 * 60 * doSomeLongOperation(); 61 * 62 * data.lock(); 63 * 64 * // data is now accessible again. Note: it may have changed since unlock(). 65 * 66 * // you can use the integral mutex to wait for a condition 67 * 68 * data.waitForCondition(data->mCondition1); 69 * 70 * helper(&data); 71 * } 72 * 73 * void trigger() { 74 * Mutexed<DataToProtect>::Locked data(mProtectedData); 75 * data->mCondition1.signal(); 76 * } 77 * 78 * void helper(const Mutexed<DataToProtect>::Locked &data) { 79 * data->mVar1 = 3; 80 * } 81 * 82 */ 83 84 template<typename T> 85 class Mutexed { 86 public: 87 /* 88 * Accessor-guard of the mutex-protected structure. This can be dereferenced to 89 * access the structure (using -> or * operators). 90 * 91 * Upon creation, the mutex is locked. You can use lock()/unlock() methods to 92 * temporarily lock/unlock the mutex. Using any references to the underlying 93 * structure or its members defeats the protection of this class, so don't do 94 * it. 95 * 96 * Note: The accessor-guard itself is not thread-safe. E.g. you should not call 97 * unlock() or lock() from different threads; they must be called from the thread 98 * that locked the original wrapper. 99 * 100 * Also note: Recursive locking/unlocking is not supported by the accessor. This 101 * is as intended, as it allows lenient locking/unlocking via multiple code paths. 102 */ 103 class Locked { 104 public: 105 inline Locked(Mutexed<T> &mParent); 106 inline Locked(Locked &&from) : 107 mLock(from.mLock), 108 mTreasure(from.mTreasure), 109 mLocked(from.mLocked) {} 110 inline ~Locked(); 111 112 // dereference the protected structure. This returns nullptr if the 113 // mutex is not locked by this accessor-guard. 114 inline T* operator->() const { return mLocked ? &mTreasure : nullptr; } 115 inline T& operator*() const { return mLocked ? mTreasure : *(T*)nullptr; } 116 117 // same as * 118 inline T& get() const { return mLocked ? mTreasure : *(T*)nullptr; } 119 // sets structure. this will abort if mLocked is false. 120 inline void set(T& o) const { get() = o; } 121 122 // Wait on the condition variable using lock. Must be locked. 123 inline status_t waitForCondition(Condition &cond) { return cond.wait(mLock); } 124 125 // same with relative timeout 126 inline status_t waitForConditionRelative(Condition &cond, nsecs_t reltime) { 127 return cond.waitRelative(mLock, reltime); 128 } 129 130 // unlocks the integral mutex. No-op if the mutex was already unlocked. 131 inline void unlock(); 132 133 // locks the integral mutex. No-op if the mutex was already locked. 134 inline void lock(); 135 136 private: 137 Mutex &mLock; 138 T &mTreasure; 139 bool mLocked; 140 141 // disable copy constructors 142 Locked(const Locked&) = delete; 143 void operator=(const Locked&) = delete; 144 }; 145 146 // Wrap all constructors of the underlying structure 147 template<typename ...Args> 148 Mutexed(Args... args) : mTreasure(args...) { } 149 150 ~Mutexed() { } 151 152 // Lock the mutex, and create an accessor-guard (a Locked object) to access the underlying 153 // structure. This returns an object that dereferences to the wrapped structure when the mutex 154 // is locked by it, or otherwise to "null". 155 // This is just a shorthand for Locked() constructor to avoid specifying the template type. 156 inline Locked lock() { 157 return Locked(*this); 158 } 159 160 private: 161 friend class Locked; 162 Mutex mLock; 163 T mTreasure; 164 165 // disable copy constructors 166 Mutexed(const Mutexed<T>&) = delete; 167 void operator=(const Mutexed<T>&) = delete; 168 }; 169 170 template<typename T> 171 inline Mutexed<T>::Locked::Locked(Mutexed<T> &mParent) 172 : mLock(mParent.mLock), 173 mTreasure(mParent.mTreasure), 174 mLocked(true) { 175 mLock.lock(); 176 } 177 178 template<typename T> 179 inline Mutexed<T>::Locked::~Locked() { 180 if (mLocked) { 181 mLock.unlock(); 182 } 183 } 184 185 template<typename T> 186 inline void Mutexed<T>::Locked::unlock() { 187 if (mLocked) { 188 mLocked = false; 189 mLock.unlock(); 190 } 191 } 192 193 template<typename T> 194 inline void Mutexed<T>::Locked::lock() { 195 if (!mLocked) { 196 mLock.lock(); 197 mLocked = true; 198 } 199 } 200 201 } // namespace android 202 203 #endif 204