1*ec779b8eSAndroid Build Coastguard Worker /* 2*ec779b8eSAndroid Build Coastguard Worker * Copyright (C) 2023 The Android Open Source Project 3*ec779b8eSAndroid Build Coastguard Worker * 4*ec779b8eSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*ec779b8eSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*ec779b8eSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*ec779b8eSAndroid Build Coastguard Worker * 8*ec779b8eSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*ec779b8eSAndroid Build Coastguard Worker * 10*ec779b8eSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*ec779b8eSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*ec779b8eSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*ec779b8eSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*ec779b8eSAndroid Build Coastguard Worker * limitations under the License. 15*ec779b8eSAndroid Build Coastguard Worker */ 16*ec779b8eSAndroid Build Coastguard Worker 17*ec779b8eSAndroid Build Coastguard Worker #pragma once 18*ec779b8eSAndroid Build Coastguard Worker 19*ec779b8eSAndroid Build Coastguard Worker #include <forward_list> 20*ec779b8eSAndroid Build Coastguard Worker #include <mutex> 21*ec779b8eSAndroid Build Coastguard Worker #include <utility> 22*ec779b8eSAndroid Build Coastguard Worker 23*ec779b8eSAndroid Build Coastguard Worker namespace android { 24*ec779b8eSAndroid Build Coastguard Worker 25*ec779b8eSAndroid Build Coastguard Worker // This class implements the "monitor" idiom for providing locked access to a class instance. 26*ec779b8eSAndroid Build Coastguard Worker // This is how it is intended to be used. Let's assume there is a "Main" class which owns 27*ec779b8eSAndroid Build Coastguard Worker // an instance of a "Resource" class, which is protected by a mutex. We add an instance of 28*ec779b8eSAndroid Build Coastguard Worker // "LockedAccessor<Resource>" as a member of "Main": 29*ec779b8eSAndroid Build Coastguard Worker // 30*ec779b8eSAndroid Build Coastguard Worker // class Resource; 31*ec779b8eSAndroid Build Coastguard Worker // 32*ec779b8eSAndroid Build Coastguard Worker // class Main { 33*ec779b8eSAndroid Build Coastguard Worker // Main() : mAccessor(mResource, mLock) {} 34*ec779b8eSAndroid Build Coastguard Worker // private: 35*ec779b8eSAndroid Build Coastguard Worker // std::mutex mLock; 36*ec779b8eSAndroid Build Coastguard Worker // Resource mResource GUARDED_BY(mLock); // owns the resource 37*ec779b8eSAndroid Build Coastguard Worker // LockedAccessor<Resource> mAccessor; 38*ec779b8eSAndroid Build Coastguard Worker // }; 39*ec779b8eSAndroid Build Coastguard Worker // 40*ec779b8eSAndroid Build Coastguard Worker // The accessor is initialized in the constructor when no locking is needed. The accessor 41*ec779b8eSAndroid Build Coastguard Worker // defers locking until the resource is accessed. 42*ec779b8eSAndroid Build Coastguard Worker // 43*ec779b8eSAndroid Build Coastguard Worker // Although "mAccessor" can be used by the methods of "Main" for scoped access to the resource, 44*ec779b8eSAndroid Build Coastguard Worker // its main role is for granting access to the resource to other classes. This is achieved by 45*ec779b8eSAndroid Build Coastguard Worker // making a copy of "mAccessor" and giving it away to another class. This obviously does not 46*ec779b8eSAndroid Build Coastguard Worker // transfer ownership of the resource. The intent is to allow another class to use the resource 47*ec779b8eSAndroid Build Coastguard Worker // with proper locking in a "lazy" fashion: 48*ec779b8eSAndroid Build Coastguard Worker // 49*ec779b8eSAndroid Build Coastguard Worker // class Another { 50*ec779b8eSAndroid Build Coastguard Worker // public: 51*ec779b8eSAndroid Build Coastguard Worker // Another(const LockedAccessor<Resource>& accessor) : mAccessor(accessor) {} 52*ec779b8eSAndroid Build Coastguard Worker // void doItLater() { // Use explicit 'lock' / 'unlock' 53*ec779b8eSAndroid Build Coastguard Worker // auto resource = mAccessor.lock(); 54*ec779b8eSAndroid Build Coastguard Worker // resource.use(); 55*ec779b8eSAndroid Build Coastguard Worker // mAccessor.unlock(); 56*ec779b8eSAndroid Build Coastguard Worker // } 57*ec779b8eSAndroid Build Coastguard Worker // void doItLaterScoped() { // Rely on the scoped accessor do perform unlocking. 58*ec779b8eSAndroid Build Coastguard Worker // LockedAccessor<Resource> scopedAccessor(mAccessor); 59*ec779b8eSAndroid Build Coastguard Worker // auto resource = scopedAccessor.lock(); 60*ec779b8eSAndroid Build Coastguard Worker // resource.use(); 61*ec779b8eSAndroid Build Coastguard Worker // } 62*ec779b8eSAndroid Build Coastguard Worker // private: 63*ec779b8eSAndroid Build Coastguard Worker // LockedAccessor<Resource> mAccessor; 64*ec779b8eSAndroid Build Coastguard Worker // }; 65*ec779b8eSAndroid Build Coastguard Worker // 66*ec779b8eSAndroid Build Coastguard Worker template<class C> 67*ec779b8eSAndroid Build Coastguard Worker class LockedAccessor { 68*ec779b8eSAndroid Build Coastguard Worker public: LockedAccessor(C & instance,std::mutex & mutex)69*ec779b8eSAndroid Build Coastguard Worker LockedAccessor(C& instance, std::mutex& mutex) 70*ec779b8eSAndroid Build Coastguard Worker : mInstance(instance), mMutex(mutex), mLock(mMutex, std::defer_lock) {} LockedAccessor(const LockedAccessor & other)71*ec779b8eSAndroid Build Coastguard Worker LockedAccessor(const LockedAccessor& other) 72*ec779b8eSAndroid Build Coastguard Worker : mInstance(other.mInstance), mMutex(other.mMutex), mLock(mMutex, std::defer_lock) {} ~LockedAccessor()73*ec779b8eSAndroid Build Coastguard Worker ~LockedAccessor() { if (mLock.owns_lock()) mLock.unlock(); } lock()74*ec779b8eSAndroid Build Coastguard Worker C& lock() { mLock.lock(); return mInstance; } unlock()75*ec779b8eSAndroid Build Coastguard Worker void unlock() { mLock.unlock(); } 76*ec779b8eSAndroid Build Coastguard Worker private: 77*ec779b8eSAndroid Build Coastguard Worker C& mInstance; 78*ec779b8eSAndroid Build Coastguard Worker std::mutex& mMutex; 79*ec779b8eSAndroid Build Coastguard Worker std::unique_lock<std::mutex> mLock; 80*ec779b8eSAndroid Build Coastguard Worker }; 81*ec779b8eSAndroid Build Coastguard Worker 82*ec779b8eSAndroid Build Coastguard Worker // This class implements scoped cleanups. A "cleanup" is a call to a method of class "C" which 83*ec779b8eSAndroid Build Coastguard Worker // takes an integer parameter. Cleanups are executed in the reverse order to how they were added. 84*ec779b8eSAndroid Build Coastguard Worker // For executing cleanups, the instance of "C" is retrieved via the provided "LockedAccessor". 85*ec779b8eSAndroid Build Coastguard Worker template<class C> 86*ec779b8eSAndroid Build Coastguard Worker class Cleanups { 87*ec779b8eSAndroid Build Coastguard Worker public: 88*ec779b8eSAndroid Build Coastguard Worker typedef void (C::*Cleaner)(int32_t); // A member function of "C" performing a cleanup action. Cleanups(const LockedAccessor<C> & accessor)89*ec779b8eSAndroid Build Coastguard Worker explicit Cleanups(const LockedAccessor<C>& accessor) : mAccessor(accessor) {} ~Cleanups()90*ec779b8eSAndroid Build Coastguard Worker ~Cleanups() { 91*ec779b8eSAndroid Build Coastguard Worker if (!mCleanups.empty()) { 92*ec779b8eSAndroid Build Coastguard Worker C& c = mAccessor.lock(); 93*ec779b8eSAndroid Build Coastguard Worker for (auto& cleanup : mCleanups) (c.*cleanup.first)(cleanup.second); 94*ec779b8eSAndroid Build Coastguard Worker mAccessor.unlock(); 95*ec779b8eSAndroid Build Coastguard Worker } 96*ec779b8eSAndroid Build Coastguard Worker } add(Cleaner cleaner,int32_t id)97*ec779b8eSAndroid Build Coastguard Worker void add(Cleaner cleaner, int32_t id) { 98*ec779b8eSAndroid Build Coastguard Worker mCleanups.emplace_front(cleaner, id); 99*ec779b8eSAndroid Build Coastguard Worker } disarmAll()100*ec779b8eSAndroid Build Coastguard Worker void disarmAll() { mCleanups.clear(); } 101*ec779b8eSAndroid Build Coastguard Worker private: 102*ec779b8eSAndroid Build Coastguard Worker using Cleanup = std::pair<Cleaner, int32_t>; 103*ec779b8eSAndroid Build Coastguard Worker LockedAccessor<C> mAccessor; 104*ec779b8eSAndroid Build Coastguard Worker std::forward_list<Cleanup> mCleanups; 105*ec779b8eSAndroid Build Coastguard Worker }; 106*ec779b8eSAndroid Build Coastguard Worker 107*ec779b8eSAndroid Build Coastguard Worker } // namespace android 108