Home | History | Annotate | Download | only in foundation
      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);
    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 
    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>
    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>
    179 inline Mutexed<T>::Locked::~Locked() {
    180     if (mLocked) {
    181         mLock.unlock();
    182     }
    183 }
    184 
    185 template<typename T>
    186 inline void Mutexed<T>::Locked::unlock() {
    187     if (mLocked) {
    188         mLocked = false;
    189         mLock.unlock();
    190     }
    191 }
    192 
    193 template<typename T>
    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