1 /* 2 * Copyright (C) 2023 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 #pragma once 18 19 #include <forward_list> 20 #include <mutex> 21 #include <utility> 22 23 namespace android { 24 25 // This class implements the "monitor" idiom for providing locked access to a class instance. 26 // This is how it is intended to be used. Let's assume there is a "Main" class which owns 27 // an instance of a "Resource" class, which is protected by a mutex. We add an instance of 28 // "LockedAccessor<Resource>" as a member of "Main": 29 // 30 // class Resource; 31 // 32 // class Main { 33 // Main() : mAccessor(mResource, mLock) {} 34 // private: 35 // std::mutex mLock; 36 // Resource mResource GUARDED_BY(mLock); // owns the resource 37 // LockedAccessor<Resource> mAccessor; 38 // }; 39 // 40 // The accessor is initialized in the constructor when no locking is needed. The accessor 41 // defers locking until the resource is accessed. 42 // 43 // Although "mAccessor" can be used by the methods of "Main" for scoped access to the resource, 44 // its main role is for granting access to the resource to other classes. This is achieved by 45 // making a copy of "mAccessor" and giving it away to another class. This obviously does not 46 // transfer ownership of the resource. The intent is to allow another class to use the resource 47 // with proper locking in a "lazy" fashion: 48 // 49 // class Another { 50 // public: 51 // Another(const LockedAccessor<Resource>& accessor) : mAccessor(accessor) {} 52 // void doItLater() { // Use explicit 'lock' / 'unlock' 53 // auto resource = mAccessor.lock(); 54 // resource.use(); 55 // mAccessor.unlock(); 56 // } 57 // void doItLaterScoped() { // Rely on the scoped accessor do perform unlocking. 58 // LockedAccessor<Resource> scopedAccessor(mAccessor); 59 // auto resource = scopedAccessor.lock(); 60 // resource.use(); 61 // } 62 // private: 63 // LockedAccessor<Resource> mAccessor; 64 // }; 65 // 66 template<class C> 67 class LockedAccessor { 68 public: LockedAccessor(C & instance,std::mutex & mutex)69 LockedAccessor(C& instance, std::mutex& mutex) 70 : mInstance(instance), mMutex(mutex), mLock(mMutex, std::defer_lock) {} LockedAccessor(const LockedAccessor & other)71 LockedAccessor(const LockedAccessor& other) 72 : mInstance(other.mInstance), mMutex(other.mMutex), mLock(mMutex, std::defer_lock) {} ~LockedAccessor()73 ~LockedAccessor() { if (mLock.owns_lock()) mLock.unlock(); } lock()74 C& lock() { mLock.lock(); return mInstance; } unlock()75 void unlock() { mLock.unlock(); } 76 private: 77 C& mInstance; 78 std::mutex& mMutex; 79 std::unique_lock<std::mutex> mLock; 80 }; 81 82 // This class implements scoped cleanups. A "cleanup" is a call to a method of class "C" which 83 // takes an integer parameter. Cleanups are executed in the reverse order to how they were added. 84 // For executing cleanups, the instance of "C" is retrieved via the provided "LockedAccessor". 85 template<class C> 86 class Cleanups { 87 public: 88 typedef void (C::*Cleaner)(int32_t); // A member function of "C" performing a cleanup action. Cleanups(const LockedAccessor<C> & accessor)89 explicit Cleanups(const LockedAccessor<C>& accessor) : mAccessor(accessor) {} ~Cleanups()90 ~Cleanups() { 91 if (!mCleanups.empty()) { 92 C& c = mAccessor.lock(); 93 for (auto& cleanup : mCleanups) (c.*cleanup.first)(cleanup.second); 94 mAccessor.unlock(); 95 } 96 } add(Cleaner cleaner,int32_t id)97 void add(Cleaner cleaner, int32_t id) { 98 mCleanups.emplace_front(cleaner, id); 99 } disarmAll()100 void disarmAll() { mCleanups.clear(); } 101 private: 102 using Cleanup = std::pair<Cleaner, int32_t>; 103 LockedAccessor<C> mAccessor; 104 std::forward_list<Cleanup> mCleanups; 105 }; 106 107 } // namespace android 108