blob: a313da1cf28439aaba329d0609a896fdce70f0e4 [file] [log] [blame]
Mikhail Naganovac9d4e72023-10-23 12:00:09 -07001/*
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
23namespace 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//
66template<class C>
67class LockedAccessor {
68 public:
69 LockedAccessor(C& instance, std::mutex& mutex)
70 : mInstance(instance), mMutex(mutex), mLock(mMutex, std::defer_lock) {}
71 LockedAccessor(const LockedAccessor& other)
72 : mInstance(other.mInstance), mMutex(other.mMutex), mLock(mMutex, std::defer_lock) {}
73 ~LockedAccessor() { if (mLock.owns_lock()) mLock.unlock(); }
74 C& lock() { mLock.lock(); return mInstance; }
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".
85template<class C>
86class Cleanups {
87 public:
88 typedef void (C::*Cleaner)(int32_t); // A member function of "C" performing a cleanup action.
89 explicit Cleanups(const LockedAccessor<C>& accessor) : mAccessor(accessor) {}
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 }
97 void add(Cleaner cleaner, int32_t id) {
98 mCleanups.emplace_front(cleaner, id);
99 }
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