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);
Locked(Locked && from)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 *
get()118 inline T& get() const { return mLocked ? mTreasure : *(T*)nullptr; }
119 // sets structure. this will abort if mLocked is false.
set(T & o)120 inline void set(T& o) const { get() = o; }
121
122 // Wait on the condition variable using lock. Must be locked.
waitForCondition(Condition & cond)123 inline status_t waitForCondition(Condition &cond) { return cond.wait(mLock); }
124
125 // same with relative timeout
waitForConditionRelative(Condition & cond,nsecs_t reltime)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>
Mutexed(Args...args)148 Mutexed(Args... args) : mTreasure(args...) { }
149
~Mutexed()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.
lock()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>
Locked(Mutexed<T> & mParent)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>
~Locked()179 inline Mutexed<T>::Locked::~Locked() {
180 if (mLocked) {
181 mLock.unlock();
182 }
183 }
184
185 template<typename T>
unlock()186 inline void Mutexed<T>::Locked::unlock() {
187 if (mLocked) {
188 mLocked = false;
189 mLock.unlock();
190 }
191 }
192
193 template<typename T>
lock()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