blob: 4b2c3f9bedab09e249197f2e2a1152de663d36a0 [file] [log] [blame]
Sami Kyostila73d41c82017-11-24 18:38:46 +00001/*
2 * Copyright (C) 2017 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#include "base/android_task_runner.h"
18
19#include <sys/eventfd.h>
20#include <sys/timerfd.h>
21
22namespace perfetto {
23namespace base {
24
25AndroidTaskRunner::AndroidTaskRunner()
26 : looper_(ALooper_prepare(0 /* require callbacks */)),
27 immediate_event_(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)),
28 delayed_timer_(
29 timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC)) {
30 ALooper_acquire(looper_);
Sami Kyostila7ae34482017-11-28 13:17:07 +000031 PERFETTO_CHECK(immediate_event_);
32 PERFETTO_CHECK(delayed_timer_);
Sami Kyostila73d41c82017-11-24 18:38:46 +000033 AddFileDescriptorWatch(immediate_event_.get(),
34 std::bind(&AndroidTaskRunner::RunImmediateTask, this));
35 AddFileDescriptorWatch(delayed_timer_.get(),
36 std::bind(&AndroidTaskRunner::RunDelayedTask, this));
37}
38
39AndroidTaskRunner::~AndroidTaskRunner() {
Sami Kyostila7ae34482017-11-28 13:17:07 +000040 PERFETTO_DCHECK_THREAD(thread_checker_);
Sami Kyostila73d41c82017-11-24 18:38:46 +000041 std::lock_guard<std::mutex> lock(lock_);
Sami Kyostila7ae34482017-11-28 13:17:07 +000042 for (const auto& watch : watch_tasks_) {
43 // ALooper doesn't guarantee that each watch doesn't run one last time if
44 // the file descriptor was already signalled. To guard against this point
45 // the watch to a no-op callback.
46 ALooper_addFd(
47 looper_, watch.first, ALOOPER_POLL_CALLBACK,
48 ALOOPER_EVENT_INPUT | ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP,
49 [](int, int, void*) -> int { return 0; }, nullptr);
Sami Kyostila73d41c82017-11-24 18:38:46 +000050 ALooper_removeFd(looper_, watch.first);
Sami Kyostila7ae34482017-11-28 13:17:07 +000051 }
Sami Kyostila73d41c82017-11-24 18:38:46 +000052 ALooper_release(looper_);
53
54 struct itimerspec time = {};
Sami Kyostila73d41c82017-11-24 18:38:46 +000055 timerfd_settime(delayed_timer_.get(), TFD_TIMER_ABSTIME, &time, nullptr);
56}
57
58void AndroidTaskRunner::Run() {
59 quit_ = false;
60 while (true) {
61 {
62 std::lock_guard<std::mutex> lock(lock_);
63 if (quit_)
64 break;
65 }
66 ALooper_pollOnce(-1 /* timeout */, nullptr, nullptr, nullptr);
67 }
68}
69
70void AndroidTaskRunner::Quit() {
71 std::lock_guard<std::mutex> lock(lock_);
72 quit_ = true;
73 ALooper_wake(looper_);
74}
75
76bool AndroidTaskRunner::IsIdleForTesting() {
Sami Kyostila7ae34482017-11-28 13:17:07 +000077 PERFETTO_DCHECK_THREAD(thread_checker_);
Sami Kyostila73d41c82017-11-24 18:38:46 +000078 std::lock_guard<std::mutex> lock(lock_);
79 return immediate_tasks_.empty();
80}
81
82AndroidTaskRunner::TimePoint AndroidTaskRunner::GetTime() const {
83 static_assert(sizeof(TimePoint) == sizeof(struct timespec),
84 "TimePoint layout must match struct timespec");
85 TimePoint now;
86 if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
87 PERFETTO_DPLOG("clock_gettime");
88 return now;
89}
90
91void AndroidTaskRunner::RunImmediateTask() {
Sami Kyostila7ae34482017-11-28 13:17:07 +000092 uint64_t unused = 0;
93 if (read(immediate_event_.get(), &unused, sizeof(unused)) != sizeof(unused) &&
Sami Kyostila73d41c82017-11-24 18:38:46 +000094 errno != EAGAIN) {
95 PERFETTO_DPLOG("read");
96 }
97
98 // TODO(skyostil): Add a separate work queue in case in case locking overhead
99 // becomes an issue.
100 bool has_next;
101 std::function<void()> immediate_task;
102 {
103 std::lock_guard<std::mutex> lock(lock_);
104 if (immediate_tasks_.empty())
105 return;
106 immediate_task = std::move(immediate_tasks_.front());
107 immediate_tasks_.pop_front();
108 has_next = !immediate_tasks_.empty();
109 }
110 // Do another pass through the event loop even if we have immediate tasks to
111 // run for fairness.
112 if (has_next)
113 ScheduleImmediateWakeUp();
114 immediate_task();
115}
116
117void AndroidTaskRunner::RunDelayedTask() {
Sami Kyostila7ae34482017-11-28 13:17:07 +0000118 uint64_t unused = 0;
119 if (read(delayed_timer_.get(), &unused, sizeof(unused)) != sizeof(unused) &&
Sami Kyostila73d41c82017-11-24 18:38:46 +0000120 errno != EAGAIN) {
121 PERFETTO_DPLOG("read");
122 }
123
124 std::function<void()> delayed_task;
125 TimePoint next_wake_up;
Sami Kyostila73d41c82017-11-24 18:38:46 +0000126 {
Sami Kyostila7ae34482017-11-28 13:17:07 +0000127 std::lock_guard<std::mutex> lock(lock_);
Sami Kyostila73d41c82017-11-24 18:38:46 +0000128 if (delayed_tasks_.empty())
129 return;
130 auto it = delayed_tasks_.begin();
Sami Kyostila7ae34482017-11-28 13:17:07 +0000131 PERFETTO_DCHECK(!(GetTime() < it->first));
Sami Kyostila73d41c82017-11-24 18:38:46 +0000132 delayed_task = std::move(it->second);
133 delayed_tasks_.erase(it);
134 if (!delayed_tasks_.empty())
135 next_wake_up = delayed_tasks_.begin()->first;
136 }
137 if (next_wake_up)
138 ScheduleDelayedWakeUp(next_wake_up);
139 delayed_task();
140}
141
142void AndroidTaskRunner::ScheduleImmediateWakeUp() {
143 uint64_t value = 1;
144 if (write(immediate_event_.get(), &value, sizeof(value)) == -1 &&
145 errno != EAGAIN) {
146 PERFETTO_DPLOG("write");
147 }
148}
149
150void AndroidTaskRunner::ScheduleDelayedWakeUp(const TimePoint& time) {
151 PERFETTO_DCHECK(time);
152 struct itimerspec wake_up = {};
153 wake_up.it_value = time;
154 if (timerfd_settime(delayed_timer_.get(), TFD_TIMER_ABSTIME, &wake_up,
155 nullptr) == -1) {
156 PERFETTO_DPLOG("timerfd_settime");
157 }
158}
159
160void AndroidTaskRunner::PostTask(std::function<void()> task) {
161 bool was_empty;
162 {
163 std::lock_guard<std::mutex> lock(lock_);
164 was_empty = immediate_tasks_.empty();
165 immediate_tasks_.push_back(std::move(task));
166 }
167 if (was_empty)
168 ScheduleImmediateWakeUp();
169}
170
171void AndroidTaskRunner::PostDelayedTask(std::function<void()> task,
172 int delay_ms) {
173 PERFETTO_DCHECK(delay_ms >= 0);
174 auto runtime = GetTime().AdvanceByMs(delay_ms);
175 bool is_next = false;
176 {
177 std::lock_guard<std::mutex> lock(lock_);
178 auto it = delayed_tasks_.insert(std::make_pair(runtime, std::move(task)));
179 if (it == delayed_tasks_.begin())
180 is_next = true;
181 }
182 if (is_next)
183 ScheduleDelayedWakeUp(runtime);
184}
185
186void AndroidTaskRunner::AddFileDescriptorWatch(int fd,
187 std::function<void()> task) {
188 PERFETTO_DCHECK(fd >= 0);
189 {
190 std::lock_guard<std::mutex> lock(lock_);
191 PERFETTO_DCHECK(!watch_tasks_.count(fd));
192 watch_tasks_[fd] = std::move(task);
193 }
Sami Kyostila7ae34482017-11-28 13:17:07 +0000194 // It's safe for the callback to hang on to |this| as everything is
195 // unregistered in the destructor.
Sami Kyostila73d41c82017-11-24 18:38:46 +0000196 auto callback = [](int signalled_fd, int events, void* data) -> int {
197 AndroidTaskRunner* task_runner = reinterpret_cast<AndroidTaskRunner*>(data);
198 return task_runner->OnFileDescriptorEvent(signalled_fd, events) ? 1 : 0;
199 };
Sami Kyostila7ae34482017-11-28 13:17:07 +0000200 PERFETTO_CHECK(ALooper_addFd(looper_, fd, ALOOPER_POLL_CALLBACK,
201 ALOOPER_EVENT_INPUT | ALOOPER_EVENT_ERROR |
202 ALOOPER_EVENT_HANGUP,
203 std::move(callback), this) != -1);
Sami Kyostila73d41c82017-11-24 18:38:46 +0000204}
205
206bool AndroidTaskRunner::OnFileDescriptorEvent(int signalled_fd, int events) {
207 PERFETTO_DCHECK_THREAD(thread_checker_);
208 if (!(events & (ALOOPER_EVENT_INPUT | ALOOPER_EVENT_ERROR |
Sami Kyostila7ae34482017-11-28 13:17:07 +0000209 ALOOPER_EVENT_HANGUP | ALOOPER_EVENT_INVALID))) {
Sami Kyostila73d41c82017-11-24 18:38:46 +0000210 return true;
Sami Kyostila7ae34482017-11-28 13:17:07 +0000211 }
Sami Kyostila73d41c82017-11-24 18:38:46 +0000212 std::function<void()> task;
213 {
214 std::lock_guard<std::mutex> lock(lock_);
215 auto it = watch_tasks_.find(signalled_fd);
216 if (it == watch_tasks_.end())
217 return false;
218 task = it->second;
219 }
220 task();
221 return true;
222}
223
224void AndroidTaskRunner::RemoveFileDescriptorWatch(int fd) {
225 PERFETTO_DCHECK(fd >= 0);
226 {
227 std::lock_guard<std::mutex> lock(lock_);
228 PERFETTO_DCHECK(watch_tasks_.count(fd));
229 watch_tasks_.erase(fd);
230 }
231 ALooper_removeFd(looper_, fd);
232}
233
234} // namespace base
235} // namespace perfetto