• 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 <map>
22 #include <queue>
23 #include <string>
24 
25 #include <android-base/file.h>
26 #include <android-base/macros.h>
27 
28 namespace android {
29 namespace os {
30 namespace dumpstate {
31 
32 class DumpPoolTest;
33 
34 /*
35  * A thread pool with the fixed number of threads to execute multiple dump tasks
36  * 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:
41  *
42  * void DumpFoo(int out_fd) {
43  *     dprintf(out_fd, "Dump result to out_fd ...");
44  * }
45  * ...
46  * DumpPool pool(tmp_root);
47  * pool.enqueueTaskWithFd("TaskName", &DumpFoo, std::placeholders::_1);
48  * ...
49  * pool.waitForTask("TaskName");
50  *
51  * 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.
54  */
55 class DumpPool {
56   friend class android::os::dumpstate::DumpPoolTest;
57 
58   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     /*
81      * Adds a task into the queue of the thread pool.
82      *
83      * |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.
86      * |args| A list of arguments.
87      *
88      * TODO(b/164369078): remove this api to have just one enqueueTask for consistency.
89      */
enqueueTask(const std::string & task_name,F && f,Args &&...args)90     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      */
enqueueTaskWithFd(const std::string & task_name,F && f,Args &&...args)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);
115         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      */
waitForTask(const std::string & task_name)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 
137     /*
138      * Deletes temporary files created by DumpPool.
139      */
140     void deleteTempFiles();
141 
142     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 
148     template<class T> void invokeTask(T dump_func, const std::string& duration_title, int out_fd);
149 
post(const std::string & task_name,T dump_func)150     template<class T> Future post(const std::string& task_name, T dump_func) {
151         Task packaged_task([=]() {
152             std::unique_ptr<TmpFile> tmp_file_ptr = createTempFile();
153             if (!tmp_file_ptr) {
154                 return std::string("");
155             }
156             invokeTask(dump_func, task_name, tmp_file_ptr->fd.get());
157             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 
177     /*
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 
185   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_;
191     bool log_duration_; // For test purpose only, the default value is true.
192     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_
207