Mutexed.h revision ee0eba046f666303741a5a5f70afad17030cc8b1
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 23namespace 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 84template<typename T> 85class Mutexed { 86public: 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(); 107 108 // dereference the protected structure. This returns nullptr if the 109 // mutex is not locked by this accessor-guard. 110 inline T* operator->() const { return mLocked ? &mTreasure : nullptr; } 111 inline T& operator*() const { return mLocked ? mTreasure : *(T*)nullptr; } 112 113 // same as * 114 inline T& get() const { return mLocked ? mTreasure : *(T*)nullptr; } 115 // sets structure. this will abort if mLocked is false. 116 inline void set(T& o) const { get() = o; } 117 118 // Wait on the condition variable using lock. Must be locked. 119 inline status_t waitForCondition(Condition &cond) { return cond.wait(mLock); } 120 121 // same with relative timeout 122 inline status_t waitForConditionRelative(Condition &cond, nsecs_t reltime) { 123 return cond.waitRelative(mLock, reltime); 124 } 125 126 // unlocks the integral mutex. No-op if the mutex was already unlocked. 127 inline void unlock(); 128 129 // locks the integral mutex. No-op if the mutex was already locked. 130 inline void lock(); 131 132 private: 133 Mutex &mLock; 134 T &mTreasure; 135 bool mLocked; 136 137 // disable copy constructors 138 Locked(const Locked&) = delete; 139 void operator=(const Locked&) = delete; 140 }; 141 142 // Wrap all constructors of the underlying structure 143 template<typename ...Args> 144 Mutexed(Args... args) : mTreasure(args...) { } 145 146 ~Mutexed() { } 147 148 // Lock the mutex, and create an accessor-guard (a Locked object) to access the underlying 149 // structure. This returns an object that dereferences to the wrapped structure when the mutex 150 // is locked by it, or otherwise to "null". 151 inline Locked&& lock() { 152 // use rvalue as Locked has no copy constructor 153 return std::move(Locked(*this)); 154 } 155 156private: 157 friend class Locked; 158 Mutex mLock; 159 T mTreasure; 160 161 // disable copy constructors 162 Mutexed(const Mutexed<T>&) = delete; 163 void operator=(const Mutexed<T>&) = delete; 164}; 165 166template<typename T> 167inline Mutexed<T>::Locked::Locked(Mutexed<T> &mParent) 168 : mLock(mParent.mLock), 169 mTreasure(mParent.mTreasure), 170 mLocked(true) { 171 mLock.lock(); 172 173} 174 175template<typename T> 176inline Mutexed<T>::Locked::~Locked() { 177 if (mLocked) { 178 mLock.unlock(); 179 } 180} 181 182template<typename T> 183inline void Mutexed<T>::Locked::unlock() { 184 if (mLocked) { 185 mLocked = false; 186 mLock.unlock(); 187 } 188} 189 190template<typename T> 191inline void Mutexed<T>::Locked::lock() { 192 if (!mLocked) { 193 mLock.lock(); 194 mLocked = true; 195 } 196} 197 198} // namespace android 199 200#endif 201