blob: 0c3c2cc0d70cba9d9b955fbdc3704d94bb9bd438 [file] [log] [blame]
Rhed Jao27077b12020-07-14 18:38:08 +08001/*
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 <map>
22#include <queue>
23#include <string>
24
25#include <android-base/file.h>
26#include <android-base/macros.h>
27
28namespace android {
29namespace os {
30namespace dumpstate {
31
Rhed Jao1c855122020-07-16 17:37:39 +080032class DumpPoolTest;
33
Rhed Jao27077b12020-07-14 18:38:08 +080034/*
35 * A thread pool with the fixed number of threads to execute multiple dump tasks
Rhed Jao1c855122020-07-16 17:37:39 +080036 * simultaneously for the dumpstate. The dump task is a callable function. It
37 * could include a file descriptor as a parameter to redirect dump results, if
38 * it needs to output results to the bugreport. This can avoid messing up
39 * bugreport's results when multiple dump tasks are running at the same time.
40 * Takes an example below for the usage of the DumpPool:
Rhed Jao27077b12020-07-14 18:38:08 +080041 *
Rhed Jao1c855122020-07-16 17:37:39 +080042 * void DumpFoo(int out_fd) {
Rhed Jao27077b12020-07-14 18:38:08 +080043 * dprintf(out_fd, "Dump result to out_fd ...");
44 * }
45 * ...
46 * DumpPool pool(tmp_root);
Rhed Jao1c855122020-07-16 17:37:39 +080047 * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
Rhed Jao27077b12020-07-14 18:38:08 +080048 * ...
49 * pool.waitForTask("TaskName");
50 *
Rhed Jao1c855122020-07-16 17:37:39 +080051 * DumpFoo is a callable function included a out_fd parameter. Using the
52 * enqueueTaskWithFd method in DumpPool to enqueue the task to the pool. The
53 * std::placeholders::_1 is a placeholder for DumpPool to pass a fd argument.
Rhed Jao27077b12020-07-14 18:38:08 +080054 */
55class DumpPool {
Rhed Jao1c855122020-07-16 17:37:39 +080056 friend class android::os::dumpstate::DumpPoolTest;
57
Rhed Jao27077b12020-07-14 18:38:08 +080058 public:
59 /*
60 * Creates a thread pool.
61 *
62 * |tmp_root| A path to a temporary folder for threads to create temporary
63 * files.
64 */
65 explicit DumpPool(const std::string& tmp_root);
66 ~DumpPool();
67
68 /*
69 * Starts the threads in the pool.
70 *
71 * |thread_counts| the number of threads to start.
72 */
73 void start(int thread_counts = MAX_THREAD_COUNT);
74
75 /*
76 * Requests to shutdown the pool and waits until all threads exit the loop.
77 */
78 void shutdown();
79
80 /*
Rhed Jao1c855122020-07-16 17:37:39 +080081 * Adds a task into the queue of the thread pool.
Rhed Jao27077b12020-07-14 18:38:08 +080082 *
Rhed Jao1c855122020-07-16 17:37:39 +080083 * |task_name| The name of the task. It's also the title of the
84 * DurationReporter log.
85 * |f| Callable function to execute the task.
Rhed Jao27077b12020-07-14 18:38:08 +080086 * |args| A list of arguments.
Rhed Jao1c855122020-07-16 17:37:39 +080087 *
88 * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
Rhed Jao27077b12020-07-14 18:38:08 +080089 */
Rhed Jao1c855122020-07-16 17:37:39 +080090 template<class F, class... Args> void enqueueTask(const std::string& task_name, F&& f,
91 Args&&... args) {
92 std::function<void(void)> func = std::bind(std::forward<F>(f),
93 std::forward<Args>(args)...);
94 futures_map_[task_name] = post(task_name, func);
95 if (threads_.empty()) {
96 start();
97 }
98 }
99
100 /*
101 * Adds a task into the queue of the thread pool. The task takes a file
102 * descriptor as a parameter to redirect dump results to a temporary file.
103 *
104 * |task_name| 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. A placeholder std::placeholders::_1 as a fd
108 * argument needs to be included here.
109 */
110 template<class F, class... Args> void enqueueTaskWithFd(const std::string& task_name, F&& f,
111 Args&&... args) {
112 std::function<void(int)> func = std::bind(std::forward<F>(f),
113 std::forward<Args>(args)...);
114 futures_map_[task_name] = post(task_name, func);
Rhed Jao27077b12020-07-14 18:38:08 +0800115 if (threads_.empty()) {
116 start();
117 }
118 }
119
120 /*
121 * Waits until the task is finished. Dumps the task results to the STDOUT_FILENO.
122 */
123 void waitForTask(const std::string& task_name) {
124 waitForTask(task_name, "", STDOUT_FILENO);
125 }
126
127 /*
128 * Waits until the task is finished. Dumps the task results to the specified
129 * out_fd.
130 *
131 * |task_name| The name of the task.
132 * |title| Dump title string to the out_fd, an empty string for nothing.
133 * |out_fd| The target file to dump the result from the task.
134 */
135 void waitForTask(const std::string& task_name, const std::string& title, int out_fd);
136
Rhed Jaoe96bcd52020-08-21 14:48:20 +0800137 /*
138 * Deletes temporary files created by DumpPool.
139 */
140 void deleteTempFiles();
141
Rhed Jao27077b12020-07-14 18:38:08 +0800142 static const std::string PREFIX_TMPFILE_NAME;
143
144 private:
145 using Task = std::packaged_task<std::string()>;
146 using Future = std::shared_future<std::string>;
147
Rhed Jao1c855122020-07-16 17:37:39 +0800148 template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
149
150 template<class T> Future post(const std::string& task_name, T dump_func) {
Rhed Jao27077b12020-07-14 18:38:08 +0800151 Task packaged_task([=]() {
152 std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
153 if (!tmp_file_ptr) {
154 return std::string("");
155 }
Rhed Jao1c855122020-07-16 17:37:39 +0800156 invokeTask(dump_func, task_name, tmp_file_ptr->fd.get());
Rhed Jao27077b12020-07-14 18:38:08 +0800157 fsync(tmp_file_ptr->fd.get());
158 return std::string(tmp_file_ptr->path);
159 });
160 std::unique_lock lock(lock_);
161 auto future = packaged_task.get_future().share();
162 tasks_.push(std::move(packaged_task));
163 condition_variable_.notify_one();
164 return future;
165 }
166
167 typedef struct {
168 android::base::unique_fd fd;
169 char path[1024];
170 } TmpFile;
171
172 std::unique_ptr<TmpFile> createTempFile();
173 void deleteTempFiles(const std::string& folder);
174 void setThreadName(const pthread_t thread, int id);
175 void loop();
176
Rhed Jao1c855122020-07-16 17:37:39 +0800177 /*
178 * For test purpose only. Enables or disables logging duration of the task.
179 *
180 * |log_duration| if true, DurationReporter is initiated to log duration of
181 * the task.
182 */
183 void setLogDuration(bool log_duration);
184
Rhed Jao27077b12020-07-14 18:38:08 +0800185 private:
186 static const int MAX_THREAD_COUNT = 4;
187
188 /* A path to a temporary folder for threads to create temporary files. */
189 std::string tmp_root_;
190 bool shutdown_;
Rhed Jao1c855122020-07-16 17:37:39 +0800191 bool log_duration_; // For test purpose only, the default value is true.
Rhed Jao27077b12020-07-14 18:38:08 +0800192 std::mutex lock_; // A lock for the tasks_.
193 std::condition_variable condition_variable_;
194
195 std::vector<std::thread> threads_;
196 std::queue<Task> tasks_;
197 std::map<std::string, Future> futures_map_;
198
199 DISALLOW_COPY_AND_ASSIGN(DumpPool);
200};
201
202} // namespace dumpstate
203} // namespace os
204} // namespace android
205
206#endif //FRAMEWORK_NATIVE_CMD_DUMPPOOL_H_