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