1 // Copyright 2015 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <brillo/message_loops/base_message_loop.h>
6
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <sys/sysmacros.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #ifndef __APPLE__
14 #include <sys/sysmacros.h>
15 #endif
16
17 #ifndef __ANDROID_HOST__
18 // Used for MISC_MAJOR. Only required for the target and not always available
19 // for the host.
20 #include <linux/major.h>
21 #endif
22
23 #include <utility>
24 #include <vector>
25
26 #include <base/bind.h>
27 #include <base/bind_helpers.h>
28 #include <base/files/file_path.h>
29 #include <base/files/file_util.h>
30 #include <base/run_loop.h>
31 #include <base/strings/string_number_conversions.h>
32 #include <base/strings/string_split.h>
33 #include <base/threading/thread_task_runner_handle.h>
34
35 #include <brillo/location_logging.h>
36 #include <brillo/strings/string_utils.h>
37
38 namespace {
39
40 const char kMiscMinorPath[] = "/proc/misc";
41 const char kBinderDriverName[] = "binder";
42
43 } // namespace
44
45 namespace brillo {
46
47 const int BaseMessageLoop::kInvalidMinor = -1;
48 const int BaseMessageLoop::kUninitializedMinor = -2;
49
BaseMessageLoop()50 BaseMessageLoop::BaseMessageLoop() {
51 CHECK(!base::ThreadTaskRunnerHandle::IsSet())
52 << "You can't create a base::MessageLoopForIO when another "
53 "base::MessageLoop is already created for this thread.";
54 owned_base_loop_.reset(new base::MessageLoopForIO());
55 base_loop_ = owned_base_loop_.get();
56 watcher_ = std::make_unique<base::FileDescriptorWatcher>(base_loop_);
57 }
58
BaseMessageLoop(base::MessageLoopForIO * base_loop)59 BaseMessageLoop::BaseMessageLoop(base::MessageLoopForIO* base_loop)
60 : base_loop_(base_loop),
61 watcher_(std::make_unique<base::FileDescriptorWatcher>(base_loop_)) {}
62
~BaseMessageLoop()63 BaseMessageLoop::~BaseMessageLoop() {
64 // Note all pending canceled delayed tasks when destroying the message loop.
65 size_t lazily_deleted_tasks = 0;
66 for (const auto& delayed_task : delayed_tasks_) {
67 if (delayed_task.second.closure.is_null()) {
68 lazily_deleted_tasks++;
69 } else {
70 DVLOG_LOC(delayed_task.second.location, 1)
71 << "Removing delayed task_id " << delayed_task.first
72 << " leaked on BaseMessageLoop, scheduled from this location.";
73 }
74 }
75 if (lazily_deleted_tasks) {
76 LOG(INFO) << "Leaking " << lazily_deleted_tasks << " canceled tasks.";
77 }
78 }
79
PostDelayedTask(const base::Location & from_here,base::OnceClosure task,base::TimeDelta delay)80 MessageLoop::TaskId BaseMessageLoop::PostDelayedTask(
81 const base::Location& from_here,
82 base::OnceClosure task,
83 base::TimeDelta delay) {
84 TaskId task_id = NextTaskId();
85 bool base_scheduled = base_loop_->task_runner()->PostDelayedTask(
86 from_here,
87 base::BindOnce(&BaseMessageLoop::OnRanPostedTask,
88 weak_ptr_factory_.GetWeakPtr(), task_id),
89 delay);
90 DVLOG_LOC(from_here, 1) << "Scheduling delayed task_id " << task_id
91 << " to run in " << delay << ".";
92 if (!base_scheduled)
93 return MessageLoop::kTaskIdNull;
94
95 delayed_tasks_.emplace(task_id,
96 DelayedTask{from_here, task_id, std::move(task)});
97 return task_id;
98 }
99
CancelTask(TaskId task_id)100 bool BaseMessageLoop::CancelTask(TaskId task_id) {
101 if (task_id == kTaskIdNull)
102 return false;
103 auto delayed_task_it = delayed_tasks_.find(task_id);
104 if (delayed_task_it == delayed_tasks_.end())
105 return false;
106
107 // A DelayedTask was found for this task_id at this point.
108
109 // Check if the callback was already canceled but we have the entry in
110 // delayed_tasks_ since it didn't fire yet in the message loop.
111 if (delayed_task_it->second.closure.is_null())
112 return false;
113
114 DVLOG_LOC(delayed_task_it->second.location, 1)
115 << "Removing task_id " << task_id << " scheduled from this location.";
116 // We reset to closure to a null OnceClosure to release all the resources
117 // used by this closure at this point, but we don't remove the task_id from
118 // delayed_tasks_ since we can't tell base::MessageLoopForIO to not run it.
119 delayed_task_it->second.closure.Reset();
120
121 return true;
122 }
123
RunOnce(bool may_block)124 bool BaseMessageLoop::RunOnce(bool may_block) {
125 run_once_ = true;
126 base::RunLoop run_loop; // Uses the base::MessageLoopForIO implicitly.
127 base_run_loop_ = &run_loop;
128 if (!may_block)
129 run_loop.RunUntilIdle();
130 else
131 run_loop.Run();
132 base_run_loop_ = nullptr;
133 // If the flag was reset to false, it means a closure was run.
134 if (!run_once_)
135 return true;
136
137 run_once_ = false;
138 return false;
139 }
140
Run()141 void BaseMessageLoop::Run() {
142 base::RunLoop run_loop; // Uses the base::MessageLoopForIO implicitly.
143 base_run_loop_ = &run_loop;
144 run_loop.Run();
145 base_run_loop_ = nullptr;
146 }
147
BreakLoop()148 void BaseMessageLoop::BreakLoop() {
149 if (base_run_loop_ == nullptr) {
150 DVLOG(1) << "Message loop not running, ignoring BreakLoop().";
151 return; // Message loop not running, nothing to do.
152 }
153 base_run_loop_->Quit();
154 }
155
QuitClosure() const156 base::RepeatingClosure BaseMessageLoop::QuitClosure() const {
157 if (base_run_loop_ == nullptr)
158 return base::DoNothing();
159 return base_run_loop_->QuitClosure();
160 }
161
NextTaskId()162 MessageLoop::TaskId BaseMessageLoop::NextTaskId() {
163 TaskId res;
164 do {
165 res = ++last_id_;
166 // We would run out of memory before we run out of task ids.
167 } while (!res ||
168 delayed_tasks_.find(res) != delayed_tasks_.end());
169 return res;
170 }
171
OnRanPostedTask(MessageLoop::TaskId task_id)172 void BaseMessageLoop::OnRanPostedTask(MessageLoop::TaskId task_id) {
173 auto task_it = delayed_tasks_.find(task_id);
174 DCHECK(task_it != delayed_tasks_.end());
175 if (!task_it->second.closure.is_null()) {
176 DVLOG_LOC(task_it->second.location, 1)
177 << "Running delayed task_id " << task_id
178 << " scheduled from this location.";
179 // Mark the task as canceled while we are running it so CancelTask returns
180 // false.
181 std::move(task_it->second.closure).Run();
182
183 // If the |run_once_| flag is set, it is because we are instructed to run
184 // only once callback.
185 if (run_once_) {
186 run_once_ = false;
187 BreakLoop();
188 }
189 }
190 delayed_tasks_.erase(task_it);
191 }
192
ParseBinderMinor(const std::string & file_contents)193 int BaseMessageLoop::ParseBinderMinor(
194 const std::string& file_contents) {
195 int result = kInvalidMinor;
196 // Split along '\n', then along the ' '. Note that base::SplitString trims all
197 // white spaces at the beginning and end after splitting.
198 std::vector<std::string> lines =
199 base::SplitString(file_contents, "\n", base::TRIM_WHITESPACE,
200 base::SPLIT_WANT_ALL);
201 for (const std::string& line : lines) {
202 if (line.empty())
203 continue;
204 std::string number;
205 std::string name;
206 if (!string_utils::SplitAtFirst(line, " ", &number, &name, false))
207 continue;
208
209 if (name == kBinderDriverName && base::StringToInt(number, &result))
210 break;
211 }
212 return result;
213 }
214
GetBinderMinor()215 unsigned int BaseMessageLoop::GetBinderMinor() {
216 if (binder_minor_ != kUninitializedMinor)
217 return binder_minor_;
218
219 std::string proc_misc;
220 if (!base::ReadFileToString(base::FilePath(kMiscMinorPath), &proc_misc))
221 return binder_minor_;
222 binder_minor_ = ParseBinderMinor(proc_misc);
223 return binder_minor_;
224 }
225
226 } // namespace brillo
227