• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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) noexcept :
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