• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 #ifndef FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
18 #define FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
19 
20 #include <future>
21 #include <queue>
22 #include <string>
23 
24 #include <android-base/file.h>
25 #include <android-base/macros.h>
26 
27 namespace android {
28 namespace os {
29 namespace dumpstate {
30 
31 class DumpPoolTest;
32 
33 /*
34  * Waits until the task is finished. Dumps the task results to the specified
35  * out_fd.
36  *
37  * |future| The task future.
38  * |title| Dump title string to the out_fd, an empty string for nothing.
39  * |out_fd| The target file to dump the result from the task.
40  */
41 void WaitForTask(std::future<std::string> future, const std::string& title, int out_fd);
42 
43 /*
44  * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
45  */
46 
WaitForTask(std::future<std::string> future)47 inline void WaitForTask(std::future<std::string> future) {
48     WaitForTask(std::move(future), "", STDOUT_FILENO);
49 }
50 
51 /*
52  * A thread pool with the fixed number of threads to execute multiple dump tasks
53  * simultaneously for dumpstate. The dump task is a callable function. It
54  * could include a file descriptor as a parameter to redirect dump results, if
55  * it needs to output results to the bugreport. This can avoid messing up
56  * bugreport's results when multiple dump tasks are running at the same time.
57  * Takes an example below for the usage of the DumpPool:
58  *
59  * void DumpFoo(int out_fd) {
60  *     dprintf(out_fd, "Dump result to out_fd ...");
61  * }
62  * ...
63  * DumpPool pool(tmp_root);
64  * auto task = pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
65  * ...
66  * WaitForTask(task);
67  *
68  * DumpFoo is a callable function included a out_fd parameter. Using the
69  * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
70  * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
71  *
72  * std::futures returned by `enqueueTask*()` must all have their `get` methods
73  * called, or have been destroyed before the DumpPool itself is destroyed.
74  */
75 class DumpPool {
76   friend class android::os::dumpstate::DumpPoolTest;
77 
78   public:
79     /*
80      * Creates a thread pool.
81      *
82      * |tmp_root| A path to a temporary folder for threads to create temporary
83      * files.
84      */
85     explicit DumpPool(const std::string& tmp_root);
86 
87     /*
88      * Will waits until all threads exit the loop. Destroying DumpPool before destroying the
89      * associated std::futures created by `enqueueTask*` will cause an abort on Android because
90      * Android is built with `-fno-exceptions`.
91      */
92     ~DumpPool();
93 
94     /*
95      * Starts the threads in the pool.
96      *
97      * |thread_counts| the number of threads to start.
98      */
99     void start(int thread_counts = MAX_THREAD_COUNT);
100 
101     /*
102      * Adds a task into the queue of the thread pool.
103      *
104      * |duration_title| The name of the task. It's also the title of the
105      * DurationReporter log.
106      * |f| Callable function to execute the task.
107      * |args| A list of arguments.
108      *
109      * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
110      */
111     template<class F, class... Args>
enqueueTask(const std::string & duration_title,F && f,Args &&...args)112     std::future<std::string> enqueueTask(const std::string& duration_title, F&& f, Args&&... args) {
113         std::function<void(void)> func = std::bind(std::forward<F>(f),
114                 std::forward<Args>(args)...);
115         auto future = post(duration_title, func);
116         if (threads_.empty()) {
117             start();
118         }
119         return future;
120     }
121 
122     /*
123      * Adds a task into the queue of the thread pool. The task takes a file
124      * descriptor as a parameter to redirect dump results to a temporary file.
125      *
126      * |duration_title| The title of the DurationReporter log.
127      * |f| Callable function to execute the task.
128      * |args| A list of arguments. A placeholder std::placeholders::_1 as a fd
129      * argument needs to be included here.
130      */
enqueueTaskWithFd(const std::string & duration_title,F && f,Args &&...args)131     template<class F, class... Args> std::future<std::string> enqueueTaskWithFd(
132             const std::string& duration_title, F&& f, Args&&... args) {
133         std::function<void(int)> func = std::bind(std::forward<F>(f),
134                 std::forward<Args>(args)...);
135         auto future = post(duration_title, func);
136         if (threads_.empty()) {
137             start();
138         }
139         return future;
140     }
141 
142     /*
143      * Deletes temporary files created by DumpPool.
144      */
145     void deleteTempFiles();
146 
147     static const std::string PREFIX_TMPFILE_NAME;
148 
149   private:
150     using Task = std::packaged_task<std::string()>;
151 
152     template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
153 
154     template<class T>
post(const std::string & duration_title,T dump_func)155     std::future<std::string> post(const std::string& duration_title, T dump_func) {
156         Task packaged_task([=]() {
157             std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
158             if (!tmp_file_ptr) {
159                 return std::string("");
160             }
161             invokeTask(dump_func, duration_title, tmp_file_ptr->fd.get());
162             fsync(tmp_file_ptr->fd.get());
163             return std::string(tmp_file_ptr->path);
164         });
165         std::unique_lock lock(lock_);
166         auto future = packaged_task.get_future();
167         tasks_.push(std::move(packaged_task));
168         condition_variable_.notify_one();
169         return future;
170     }
171 
172     typedef struct {
173       android::base::unique_fd fd;
174       char path[1024];
175     } TmpFile;
176 
177     std::unique_ptr<TmpFile> createTempFile();
178     void deleteTempFiles(const std::string& folder);
179     void setThreadName(const pthread_t thread, int id);
180     void loop();
181 
182     /*
183      * For test purpose only. Enables or disables logging duration of the task.
184      *
185      * |log_duration| if true, DurationReporter is initiated to log duration of
186      * the task.
187      */
188     void setLogDuration(bool log_duration);
189 
190   private:
191     static const int MAX_THREAD_COUNT = 4;
192 
193     /* A path to a temporary folder for threads to create temporary files. */
194     std::string tmp_root_;
195     bool shutdown_;
196     bool log_duration_; // For test purpose only, the default value is true.
197     std::mutex lock_;  // A lock for the tasks_.
198     std::condition_variable condition_variable_;
199 
200     std::vector<std::thread> threads_;
201     std::queue<Task> tasks_;
202 
203     DISALLOW_COPY_AND_ASSIGN(DumpPool);
204 };
205 
206 }  // namespace dumpstate
207 }  // namespace os
208 }  // namespace android
209 
210 #endif //FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_
211