xref: /aosp_15_r20/frameworks/av/media/libaudiohal/impl/Cleanups.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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