xref: /aosp_15_r20/system/media/audio_utils/include/audio_utils/DeferredExecutor.h (revision b9df5ad1c9ac98a7fefaac271a55f7ae3db05414)
1 /*
2  * Copyright (C) 2024 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 <android-base/thread_annotations.h>
20 #include <any>
21 #include <functional>
22 #include <mutex>
23 #include <vector>
24 
25 namespace android::audio_utils {
26 
27 /**
28  * The DeferredExecutor class accumulates objects to dispose
29  * and functors to execute.
30  *
31  * The class is used in a worker thread loop to allow objects
32  * and functors to be accumulated under a mutex,
33  * where such object dtors or functors might cause
34  * deadlocks or order inversion issues when executed.
35  * The process() method is then called outside of the mutex
36  * to dispose any objects and execute any functors accumulated.
37  */
38 
39 class DeferredExecutor {
40 public:
41 
42     /**
43      * \param processInDtor if true calls process in the dtor.
44      *
45      * processInDtor defaults to false to prevent use after free.
46      */
47     explicit DeferredExecutor(bool processInDtor = false)
mProcessInDtor(processInDtor)48         : mProcessInDtor(processInDtor)
49     {}
50 
51     /**
52      * If processInDtor is set true in the ctor, then
53      * deferred functors are executed.  Then any
54      * deferred functors and garbage are deallocated.
55      */
~DeferredExecutor()56     ~DeferredExecutor() {
57         if (mProcessInDtor) process(true /* recursive */);
58     }
59 
60     /**
61      * Delays destruction of an object to the
62      * invocation of process() (generally outside of lock).
63      *
64      * Example Usage:
65      *
66      * std::vector<std::vector<sp<IFoo>>> interfaces;
67      * ...
68      * executor.dispose(std::move(interfaces));
69      */
70     template<typename T>
dispose(T && object)71     void dispose(T&& object) {
72         std::lock_guard lg(mMutex);
73         mGarbage.emplace_back(std::forward<T>(object));
74     }
75 
76     /**
77      * Defers execution of a functor to the invocation
78      * of process() (generally outside of lock).
79      *
80      * Example Usage:
81      *
82      * executor.defer([]{ foo(); });
83      */
84     template<typename F>
defer(F && functor)85     void defer(F&& functor) {
86         std::lock_guard lg(mMutex);
87         mDeferred.emplace_back(std::forward<F>(functor));
88     }
89 
90     /**
91      * Runs deferred functors (in order of adding)
92      * and then dellocates the functors and empties the garbage
93      * (in reverse order of adding).
94      *
95      * \param recursive if set to true, will loop the process
96      *     to ensure no garbage or deferred objects remain.
97      */
98     void process(bool recursive = false) {
99         do {
100             // Note the declaration order of garbage and deferred.
101             std::vector <std::any> garbage;
102             std::vector <std::function<void()>> deferred;
103             {
104                 std::lock_guard lg(mMutex);
105                 if (mGarbage.empty() && mDeferred.empty()) return;
106                 std::swap(garbage, mGarbage);
107                 std::swap(deferred, mDeferred);
108             }
109             // execution in order of adding.
110             // destruction in reverse order of adding.
111             for (const auto& f: deferred) {
112                 f();
113             }
114         } while (recursive);
115     }
116 
117     /**
118      * Skips running any deferred functors and dellocates the functors
119      * and empties the garbage (in reverse order of adding).
120      */
clear()121     void clear() {
122         // Note the declaration order of garbage and deferred.
123         std::vector<std::any> garbage;
124         std::vector<std::function<void()>> deferred;
125         {
126             std::lock_guard lg(mMutex);
127             std::swap(garbage, mGarbage);
128             std::swap(deferred, mDeferred);
129         }
130     }
131 
132     /**
133      * Returns true if there is no garbage and no deferred methods.
134      */
empty()135     bool empty() const {
136         std::lock_guard lg(mMutex);
137         return mGarbage.empty() && mDeferred.empty();
138     }
139 
140 private:
141     const bool mProcessInDtor;
142     mutable std::mutex mMutex;
143     std::vector<std::any> mGarbage GUARDED_BY(mMutex);
144     std::vector<std::function<void()>> mDeferred GUARDED_BY(mMutex);
145 };
146 
147 }  // namespace android::audio_utils
148