blob: 9fb0fcc83cc89b83f5705dd3f5d9245f2aacbaef [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>
Rhed Jao27077b12020-07-14 18:38:08 +080021#include <queue>
22#include <string>
23
24#include <android-base/file.h>
25#include <android-base/macros.h>
26
27namespace android {
28namespace os {
29namespace dumpstate {
30
Rhed Jao1c855122020-07-16 17:37:39 +080031class DumpPoolTest;
32
Rhed Jao27077b12020-07-14 18:38:08 +080033/*
Chris Morinbc223142022-02-04 14:17:11 -080034 * 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 */
41void 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
47inline void WaitForTask(std::future<std::string> future) {
48 WaitForTask(std::move(future), "", STDOUT_FILENO);
49}
50
51/*
Rhed Jao27077b12020-07-14 18:38:08 +080052 * A thread pool with the fixed number of threads to execute multiple dump tasks
Chris Morinbc223142022-02-04 14:17:11 -080053 * simultaneously for dumpstate. The dump task is a callable function. It
Rhed Jao1c855122020-07-16 17:37:39 +080054 * 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:
Rhed Jao27077b12020-07-14 18:38:08 +080058 *
Rhed Jao1c855122020-07-16 17:37:39 +080059 * void DumpFoo(int out_fd) {
Rhed Jao27077b12020-07-14 18:38:08 +080060 * dprintf(out_fd, "Dump result to out_fd ...");
61 * }
62 * ...
63 * DumpPool pool(tmp_root);
Chris Morinbc223142022-02-04 14:17:11 -080064 * auto task = pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
Rhed Jao27077b12020-07-14 18:38:08 +080065 * ...
Chris Morinbc223142022-02-04 14:17:11 -080066 * WaitForTask(task);
Rhed Jao27077b12020-07-14 18:38:08 +080067 *
Rhed Jao1c855122020-07-16 17:37:39 +080068 * 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.
Chris Morinbc223142022-02-04 14:17:11 -080071 *
72 * std::futures returned by `enqueueTask*()` must all have their `get` methods
73 * called, or have been destroyed before the DumpPool itself is destroyed.
Rhed Jao27077b12020-07-14 18:38:08 +080074 */
75class DumpPool {
Rhed Jao1c855122020-07-16 17:37:39 +080076 friend class android::os::dumpstate::DumpPoolTest;
77
Rhed Jao27077b12020-07-14 18:38:08 +080078 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);
Chris Morinbc223142022-02-04 14:17:11 -080086
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 */
Rhed Jao27077b12020-07-14 18:38:08 +080092 ~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 /*
Rhed Jao1c855122020-07-16 17:37:39 +0800102 * Adds a task into the queue of the thread pool.
Rhed Jao27077b12020-07-14 18:38:08 +0800103 *
Chris Morinbc223142022-02-04 14:17:11 -0800104 * |duration_title| The name of the task. It's also the title of the
Rhed Jao1c855122020-07-16 17:37:39 +0800105 * DurationReporter log.
106 * |f| Callable function to execute the task.
Rhed Jao27077b12020-07-14 18:38:08 +0800107 * |args| A list of arguments.
Rhed Jao1c855122020-07-16 17:37:39 +0800108 *
109 * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
Rhed Jao27077b12020-07-14 18:38:08 +0800110 */
Chris Morinbc223142022-02-04 14:17:11 -0800111 template<class F, class... Args>
112 std::future<std::string> enqueueTask(const std::string& duration_title, F&& f, Args&&... args) {
Rhed Jao1c855122020-07-16 17:37:39 +0800113 std::function<void(void)> func = std::bind(std::forward<F>(f),
114 std::forward<Args>(args)...);
Chris Morinbc223142022-02-04 14:17:11 -0800115 auto future = post(duration_title, func);
Rhed Jao1c855122020-07-16 17:37:39 +0800116 if (threads_.empty()) {
117 start();
118 }
Chris Morinbc223142022-02-04 14:17:11 -0800119 return future;
Rhed Jao1c855122020-07-16 17:37:39 +0800120 }
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 *
Chris Morinbc223142022-02-04 14:17:11 -0800126 * |duration_title| The title of the DurationReporter log.
Rhed Jao1c855122020-07-16 17:37:39 +0800127 * |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 */
Chris Morinbc223142022-02-04 14:17:11 -0800131 template<class F, class... Args> std::future<std::string> enqueueTaskWithFd(
132 const std::string& duration_title, F&& f, Args&&... args) {
Rhed Jao1c855122020-07-16 17:37:39 +0800133 std::function<void(int)> func = std::bind(std::forward<F>(f),
134 std::forward<Args>(args)...);
Chris Morinbc223142022-02-04 14:17:11 -0800135 auto future = post(duration_title, func);
Rhed Jao27077b12020-07-14 18:38:08 +0800136 if (threads_.empty()) {
137 start();
138 }
Chris Morinbc223142022-02-04 14:17:11 -0800139 return future;
Rhed Jao27077b12020-07-14 18:38:08 +0800140 }
141
142 /*
Rhed Jaoe96bcd52020-08-21 14:48:20 +0800143 * Deletes temporary files created by DumpPool.
144 */
145 void deleteTempFiles();
146
Rhed Jao27077b12020-07-14 18:38:08 +0800147 static const std::string PREFIX_TMPFILE_NAME;
148
149 private:
150 using Task = std::packaged_task<std::string()>;
Rhed Jao27077b12020-07-14 18:38:08 +0800151
Rhed Jao1c855122020-07-16 17:37:39 +0800152 template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
153
Chris Morinbc223142022-02-04 14:17:11 -0800154 template<class T>
155 std::future<std::string> post(const std::string& duration_title, T dump_func) {
Rhed Jao27077b12020-07-14 18:38:08 +0800156 Task packaged_task([=]() {
157 std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
158 if (!tmp_file_ptr) {
159 return std::string("");
160 }
Chris Morinbc223142022-02-04 14:17:11 -0800161 invokeTask(dump_func, duration_title, tmp_file_ptr->fd.get());
Rhed Jao27077b12020-07-14 18:38:08 +0800162 fsync(tmp_file_ptr->fd.get());
163 return std::string(tmp_file_ptr->path);
164 });
165 std::unique_lock lock(lock_);
Chris Morinbc223142022-02-04 14:17:11 -0800166 auto future = packaged_task.get_future();
Rhed Jao27077b12020-07-14 18:38:08 +0800167 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
Rhed Jao1c855122020-07-16 17:37:39 +0800182 /*
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
Rhed Jao27077b12020-07-14 18:38:08 +0800190 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_;
Rhed Jao1c855122020-07-16 17:37:39 +0800196 bool log_duration_; // For test purpose only, the default value is true.
Rhed Jao27077b12020-07-14 18:38:08 +0800197 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_;
Rhed Jao27077b12020-07-14 18:38:08 +0800202
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_