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(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
160private:
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
170template<typename T>
171inline Mutexed<T>::Locked::Locked(Mutexed<T> &mParent)
172    : mLock(mParent.mLock),
173      mTreasure(mParent.mTreasure),
174      mLocked(true) {
175    mLock.lock();
176}
177
178template<typename T>
179inline Mutexed<T>::Locked::~Locked() {
180    if (mLocked) {
181        mLock.unlock();
182    }
183}
184
185template<typename T>
186inline void Mutexed<T>::Locked::unlock() {
187    if (mLocked) {
188        mLocked = false;
189        mLock.unlock();
190    }
191}
192
193template<typename T>
194inline void Mutexed<T>::Locked::lock() {
195    if (!mLocked) {
196        mLock.lock();
197        mLocked = true;
198    }
199}
200
201} // namespace android
202
203#endif
204