blob: eb8e89766b612e7bea92a791b3fefe9e1e54706a [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[email protected]61c86c62011-08-02 16:11:162// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]59e69e742013-06-18 20:27:525#include "base/message_loop/message_pump_android.h"
[email protected]61c86c62011-08-02 16:11:166
Michael Thiessend7ae7352018-07-10 00:57:137#include <android/looper.h>
8#include <errno.h>
9#include <fcntl.h>
[email protected]61c86c62011-08-02 16:11:1610#include <jni.h>
Stephen Nusko408b9a92022-09-15 10:03:5711#include <linux/time.h>
Michael Thiessend7ae7352018-07-10 00:57:1312#include <sys/eventfd.h>
13#include <sys/syscall.h>
14#include <sys/types.h>
15#include <unistd.h>
16#include <utility>
[email protected]61c86c62011-08-02 16:11:1617
18#include "base/android/jni_android.h"
[email protected]449019ca2012-03-14 22:17:0019#include "base/android/scoped_java_ref.h"
Hans Wennborgc3cffa62020-04-27 10:09:1220#include "base/check_op.h"
Avi Drissman63e1f992023-01-13 18:54:4321#include "base/functional/callback_helpers.h"
[email protected]449019ca2012-03-14 22:17:0022#include "base/lazy_instance.h"
Hans Wennborgc3cffa62020-04-27 10:09:1223#include "base/notreached.h"
Peter Kasting2f61c8b2022-07-19 23:43:4624#include "base/numerics/safe_conversions.h"
[email protected]8e937c1e2012-06-28 22:57:3025#include "base/run_loop.h"
Stephen Nusko408b9a92022-09-15 10:03:5726#include "base/task/task_features.h"
27#include "base/trace_event/base_tracing.h"
Michael Thiessen6fd43902018-08-30 23:14:1528#include "build/build_config.h"
Michael Thiessend7ae7352018-07-10 00:57:1329
30// Android stripped sys/timerfd.h out of their platform headers, so we have to
31// use syscall to make use of timerfd. Once the min API level is 20, we can
32// directly use timerfd.h.
33#ifndef __NR_timerfd_create
34#error "Unable to find syscall for __NR_timerfd_create"
35#endif
36
37#ifndef TFD_TIMER_ABSTIME
38#define TFD_TIMER_ABSTIME (1 << 0)
39#endif
[email protected]61c86c62011-08-02 16:11:1640
torne86560112016-08-04 15:59:0441using base::android::JavaParamRef;
[email protected]449019ca2012-03-14 22:17:0042using base::android::ScopedJavaLocalRef;
[email protected]61c86c62011-08-02 16:11:1643
Michael Thiessen781ddeb2017-11-15 17:07:2344namespace base {
45
Michael Thiessend7ae7352018-07-10 00:57:1346namespace {
Michael Thiessen781ddeb2017-11-15 17:07:2347
Michael Thiessend7ae7352018-07-10 00:57:1348// See sys/timerfd.h
Peter Kasting2f61c8b2022-07-19 23:43:4649long timerfd_create(int clockid, int flags) {
Michael Thiessend7ae7352018-07-10 00:57:1350 return syscall(__NR_timerfd_create, clockid, flags);
Michael Thiessen781ddeb2017-11-15 17:07:2351}
52
Michael Thiessend7ae7352018-07-10 00:57:1353// See sys/timerfd.h
Peter Kasting2f61c8b2022-07-19 23:43:4654long timerfd_settime(int ufc,
55 int flags,
56 const struct itimerspec* utmr,
57 struct itimerspec* otmr) {
Michael Thiessend7ae7352018-07-10 00:57:1358 return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
59}
Michael Thiessen781ddeb2017-11-15 17:07:2360
Michael Thiessen6fd43902018-08-30 23:14:1561// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
62// into our code due to the inconsistent ABI on older Android OS versions.
63#if defined(ARCH_CPU_X86)
64#define STACK_ALIGN __attribute__((force_align_arg_pointer))
65#else
66#define STACK_ALIGN
67#endif
68
69STACK_ALIGN int NonDelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1370 if (events & ALOOPER_EVENT_HANGUP)
71 return 0;
72
73 DCHECK(events & ALOOPER_EVENT_INPUT);
74 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
75 pump->OnNonDelayedLooperCallback();
76 return 1; // continue listening for events
77}
78
Michael Thiessen6fd43902018-08-30 23:14:1579STACK_ALIGN int DelayedLooperCallback(int fd, int events, void* data) {
Michael Thiessend7ae7352018-07-10 00:57:1380 if (events & ALOOPER_EVENT_HANGUP)
81 return 0;
82
83 DCHECK(events & ALOOPER_EVENT_INPUT);
84 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
85 pump->OnDelayedLooperCallback();
86 return 1; // continue listening for events
87}
88
Stephen Nusko408b9a92022-09-15 10:03:5789STACK_ALIGN int ResumeNonDelayedLooperCallback(int fd, int events, void* data) {
90 if (events & ALOOPER_EVENT_HANGUP)
91 return 0;
92
93 DCHECK(events & ALOOPER_EVENT_INPUT);
94 MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
95 pump->OnResumeNonDelayedLooperCallback();
96 return 1; // continue listening for events
97}
98
Aaron Colwell48c4d5072020-11-13 16:45:0399// A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
Gabriel Charette300f16792022-07-06 20:03:30100// native work below.
101constexpr uint64_t kTryNativeWorkBeforeIdleBit = uint64_t(1) << 32;
Michael Thiessend7ae7352018-07-10 00:57:13102} // namespace
103
Stephen Nusko408b9a92022-09-15 10:03:57104void ClearTimerFD(int timer_fd) {
105 // Clear the fd.
106 uint64_t value;
107 long ret = read(timer_fd, &value, sizeof(value));
108
109 // TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
110 // According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
111 // EAGAIN only happens if no timer has expired. Also according to the man page
112 // poll only returns readable when a timer has expired. So this function will
113 // only be called when a timer has expired, but reading reveals no timer has
114 // expired...
115 // The functions that use ClearTimerFD are OnResumeNonDelayedLooperCallback(),
116 // OnDelayedLooperCallback(). For Resume only Quit() and
117 // ScheduleWorkWithDelay() touch its timer, and similarly for Delayed only
118 // Quit() and ScheduleDelayedWork() do, but all of them run on the same thread
119 // as this callback so there are no obvious timing or multi-threading related
120 // issues.
121 DPCHECK(ret >= 0 || errno == EAGAIN);
122}
123
124itimerspec GetTimerSpecDelay(base::TimeDelta yield_duration) {
125 struct itimerspec ts;
126 int64_t nanos = yield_duration.InNanoseconds();
127 ts.it_interval.tv_sec = 0; // Don't repeat.
128 ts.it_interval.tv_nsec = 0;
129 ts.it_value.tv_sec =
130 static_cast<time_t>(nanos / TimeTicks::kNanosecondsPerSecond);
131 ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
132 return ts;
133}
134
Torne (Richard Coles)c6993a032020-02-19 13:23:47135MessagePumpForUI::MessagePumpForUI()
François Doray253a3062022-10-24 16:45:29136 : env_(base::android::AttachCurrentThread()) {
Michael Thiessend7ae7352018-07-10 00:57:13137 // The Android native ALooper uses epoll to poll our file descriptors and wake
138 // us up. We use a simple level-triggered eventfd to signal that non-delayed
139 // work is available, and a timerfd to signal when delayed work is ready to
140 // be run.
141 non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
142 CHECK_NE(non_delayed_fd_, -1);
143 DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
144
Stephen Nusko408b9a92022-09-15 10:03:57145 resume_after_yielding_non_delayed_fd_ = checked_cast<int>(
146 timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC));
147 CHECK_NE(resume_after_yielding_non_delayed_fd_, -1);
Michael Thiessend7ae7352018-07-10 00:57:13148 // We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
149 // include timerfd.h. See comments above on __NR_timerfd_create. It looks like
150 // they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
151 // fine.
Peter Kasting2f61c8b2022-07-19 23:43:46152 delayed_fd_ = checked_cast<int>(
153 timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC));
Michael Thiessend7ae7352018-07-10 00:57:13154 CHECK_NE(delayed_fd_, -1);
155
156 looper_ = ALooper_prepare(0);
157 DCHECK(looper_);
158 // Add a reference to the looper so it isn't deleted on us.
159 ALooper_acquire(looper_);
160 ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
161 &NonDelayedLooperCallback, reinterpret_cast<void*>(this));
Stephen Nusko408b9a92022-09-15 10:03:57162 ALooper_addFd(looper_, resume_after_yielding_non_delayed_fd_, 0,
163 ALOOPER_EVENT_INPUT, &ResumeNonDelayedLooperCallback,
164 reinterpret_cast<void*>(this));
Michael Thiessend7ae7352018-07-10 00:57:13165 ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
166 &DelayedLooperCallback, reinterpret_cast<void*>(this));
167}
168
169MessagePumpForUI::~MessagePumpForUI() {
170 DCHECK_EQ(ALooper_forThread(), looper_);
171 ALooper_removeFd(looper_, non_delayed_fd_);
Stephen Nusko408b9a92022-09-15 10:03:57172 ALooper_removeFd(looper_, resume_after_yielding_non_delayed_fd_);
Michael Thiessend7ae7352018-07-10 00:57:13173 ALooper_removeFd(looper_, delayed_fd_);
174 ALooper_release(looper_);
175 looper_ = nullptr;
176
177 close(non_delayed_fd_);
Stephen Nusko408b9a92022-09-15 10:03:57178 close(resume_after_yielding_non_delayed_fd_);
Michael Thiessend7ae7352018-07-10 00:57:13179 close(delayed_fd_);
180}
181
182void MessagePumpForUI::OnDelayedLooperCallback() {
Stephen Nusko408b9a92022-09-15 10:03:57183 TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("base"),
184 "MessagePumpForUI::OnDelayedLooperCallback");
Torne (Richard Coles)c6993a032020-02-19 13:23:47185 // There may be non-Chromium callbacks on the same ALooper which may have left
186 // a pending exception set, and ALooper does not check for this between
187 // callbacks. Check here, and if there's already an exception, just skip this
188 // iteration without clearing the fd. If the exception ends up being non-fatal
189 // then we'll just get called again on the next polling iteration.
190 if (base::android::HasException(env_))
191 return;
192
Gabriel Charette9d44a9b2019-04-29 16:35:56193 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
194 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13195 if (ShouldQuit())
Michael Thiessenfc7067fe2017-11-01 22:33:01196 return;
197
Stephen Nusko408b9a92022-09-15 10:03:57198 ClearTimerFD(delayed_fd_);
Michael Thiessen7c36083d2018-08-10 20:24:54199
Aaron Colwell48c4d5072020-11-13 16:45:03200 DoDelayedLooperWork();
201}
Michael Thiessen7c36083d2018-08-10 20:24:54202
Aaron Colwell48c4d5072020-11-13 16:45:03203void MessagePumpForUI::DoDelayedLooperWork() {
Gabriel Charette9d44a9b2019-04-29 16:35:56204 delayed_scheduled_time_.reset();
[email protected]61c86c62011-08-02 16:11:16205
Etienne Pierre-doray2163f3012020-04-02 21:37:14206 Delegate::NextWorkInfo next_work_info = delegate_->DoWork();
Gabriel Charette9d44a9b2019-04-29 16:35:56207
Michael Thiessend7ae7352018-07-10 00:57:13208 if (ShouldQuit())
209 return;
Gabriel Charette9d44a9b2019-04-29 16:35:56210
211 if (next_work_info.is_immediate()) {
212 ScheduleWork();
213 return;
214 }
215
216 DoIdleWork();
217 if (!next_work_info.delayed_run_time.is_max())
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33218 ScheduleDelayedWork(next_work_info);
Michael Thiessend7ae7352018-07-10 00:57:13219}
220
Stephen Nusko408b9a92022-09-15 10:03:57221void MessagePumpForUI::OnResumeNonDelayedLooperCallback() {
222 TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("base"),
223 "MessagePumpForUI::OnResumeNonDelayedLooperCallback");
224 // There may be non-Chromium callbacks on the same ALooper which may have left
225 // a pending exception set, and ALooper does not check for this between
226 // callbacks. Check here, and if there's already an exception, just skip this
227 // iteration without clearing the fd. If the exception ends up being non-fatal
228 // then we'll just get called again on the next polling iteration.
229 if (base::android::HasException(env_))
230 return;
231
232 // ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
233 // resulted in Quit() in the same round.
234 if (ShouldQuit())
235 return;
236
237 ClearTimerFD(resume_after_yielding_non_delayed_fd_);
238
239 // Resume the execution of the non delayed fd normally after the yield.
240 DoNonDelayedLooperWork(false);
241}
242
Michael Thiessend7ae7352018-07-10 00:57:13243void MessagePumpForUI::OnNonDelayedLooperCallback() {
Stephen Nusko408b9a92022-09-15 10:03:57244 TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("base"),
245 "MessagePumpForUI::OnNonDelayedLooperCallback");
Torne (Richard Coles)c6993a032020-02-19 13:23:47246 // There may be non-Chromium callbacks on the same ALooper which may have left
247 // a pending exception set, and ALooper does not check for this between
248 // callbacks. Check here, and if there's already an exception, just skip this
249 // iteration without clearing the fd. If the exception ends up being non-fatal
250 // then we'll just get called again on the next polling iteration.
251 if (base::android::HasException(env_))
252 return;
253
Gabriel Charette9d44a9b2019-04-29 16:35:56254 // ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
255 // resulted in Quit() in the same round.
Michael Thiessend7ae7352018-07-10 00:57:13256 if (ShouldQuit())
257 return;
258
Gabriel Charette9d44a9b2019-04-29 16:35:56259 // We're about to process all the work requested by ScheduleWork().
260 // MessagePump users are expected to do their best not to invoke
Etienne Pierre-doray2163f3012020-04-02 21:37:14261 // ScheduleWork() again before DoWork() returns a non-immediate
Gabriel Charette9d44a9b2019-04-29 16:35:56262 // NextWorkInfo below. Hence, capturing the file descriptor's value now and
263 // resetting its contents to 0 should be okay. The value currently stored
264 // should be greater than 0 since work having been scheduled is the reason
265 // we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
Aaron Colwell48c4d5072020-11-13 16:45:03266 uint64_t value = 0;
Peter Kasting2f61c8b2022-07-19 23:43:46267 long ret = read(non_delayed_fd_, &value, sizeof(value));
Gabriel Charette9d44a9b2019-04-29 16:35:56268 DPCHECK(ret >= 0);
Aaron Colwell48c4d5072020-11-13 16:45:03269 DCHECK_GT(value, 0U);
Gabriel Charette300f16792022-07-06 20:03:30270 bool do_idle_work = value == kTryNativeWorkBeforeIdleBit;
Aaron Colwell48c4d5072020-11-13 16:45:03271 DoNonDelayedLooperWork(do_idle_work);
272}
Gabriel Charette9d44a9b2019-04-29 16:35:56273
Aaron Colwell48c4d5072020-11-13 16:45:03274void MessagePumpForUI::DoNonDelayedLooperWork(bool do_idle_work) {
275 // Note: We can't skip DoWork() even if |do_idle_work| is true here (i.e. no
276 // additional ScheduleWork() since yielding to native) as delayed tasks might
277 // have come in and we need to re-sample |next_work_info|.
Gabriel Charette9d44a9b2019-04-29 16:35:56278
279 // Runs all application tasks scheduled to run.
280 Delegate::NextWorkInfo next_work_info;
281 do {
282 if (ShouldQuit())
283 return;
284
Etienne Pierre-doray2163f3012020-04-02 21:37:14285 next_work_info = delegate_->DoWork();
Stephen Nusko133b4e42021-06-01 22:06:23286 // If we are prioritizing native, and the next work would normally run
Gabriel Charette300f16792022-07-06 20:03:30287 // immediately, skip the next work and let the native work items have a
288 // chance to run. This is useful when user input is waiting for native to
289 // have a chance to run.
Stephen Nusko133b4e42021-06-01 22:06:23290 if (next_work_info.is_immediate() && next_work_info.yield_to_native) {
Stephen Nusko408b9a92022-09-15 10:03:57291 // We should delay calling the DoNonDelayedLooperWork for a some time
292 // because epoll doesn't guarantee the event handling in the order of
293 // arrival, by delaying the call back by yield_duration_, we increase
294 // the likelihood of picking a different starved fd.
295 ScheduleWorkWithDelay();
Stephen Nusko133b4e42021-06-01 22:06:23296 return;
297 }
Gabriel Charette9d44a9b2019-04-29 16:35:56298 } while (next_work_info.is_immediate());
299
300 // Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
301 // allow nesting so needing to resume in an outer loop is not an issue
302 // either).
303 if (ShouldQuit())
304 return;
305
Gabriel Charette300f16792022-07-06 20:03:30306 // Before declaring this loop idle, yield to native work items and arrange to
Stephen Nusko408b9a92022-09-15 10:03:57307 // be called again (unless we're already in that second call).non_delayed_fd_
Aaron Colwell48c4d5072020-11-13 16:45:03308 if (!do_idle_work) {
309 ScheduleWorkInternal(/*do_idle_work=*/true);
Gabriel Charette9d44a9b2019-04-29 16:35:56310 return;
Michael Thiessend7ae7352018-07-10 00:57:13311 }
Gabriel Charette9d44a9b2019-04-29 16:35:56312
Gabriel Charette300f16792022-07-06 20:03:30313 // We yielded to native work items already and they didn't generate a
Gabriel Charette1c6e0a22019-04-30 11:15:14314 // ScheduleWork() request so we can declare idleness. It's possible for a
315 // ScheduleWork() request to come in racily while this method unwinds, this is
316 // fine and will merely result in it being re-invoked shortly after it
317 // returns.
318 // TODO(scheduler-dev): this doesn't account for tasks that don't ever call
319 // SchedulerWork() but still keep the system non-idle (e.g., the Java Handler
320 // API). It would be better to add an API to query the presence of native
Gabriel Charette300f16792022-07-06 20:03:30321 // tasks instead of relying on yielding once + kTryNativeWorkBeforeIdleBit.
Aaron Colwell48c4d5072020-11-13 16:45:03322 DCHECK(do_idle_work);
Gabriel Charette9d44a9b2019-04-29 16:35:56323
324 if (ShouldQuit())
325 return;
326
327 // At this point, the java looper might not be idle - it's impossible to know
328 // pre-Android-M, so we may end up doing Idle work while java tasks are still
329 // queued up. Note that this won't cause us to fail to run java tasks using
330 // QuitWhenIdle, as the JavaHandlerThread will finish running all currently
331 // scheduled tasks before it quits. Also note that we can't just add an idle
332 // callback to the java looper, as that will fire even if application tasks
333 // are still queued up.
334 DoIdleWork();
Stephen Nusko408b9a92022-09-15 10:03:57335 if (!next_work_info.delayed_run_time.is_max()) {
336 TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("base"), "ScheduleWorkIsMax");
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33337 ScheduleDelayedWork(next_work_info);
Stephen Nusko408b9a92022-09-15 10:03:57338 }
Michael Thiessend7ae7352018-07-10 00:57:13339}
340
341void MessagePumpForUI::DoIdleWork() {
342 if (delegate_->DoIdleWork()) {
343 // If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
344 // the loop here because we may in fact be idle after doing idle work
345 // without any new tasks being queued.
Stephen Nusko408b9a92022-09-15 10:03:57346 TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("base"), "DoIdleWorkScheduleWork");
Michael Thiessend7ae7352018-07-10 00:57:13347 ScheduleWork();
348 }
[email protected]61c86c62011-08-02 16:11:16349}
350
[email protected]61c86c62011-08-02 16:11:16351void MessagePumpForUI::Run(Delegate* delegate) {
Aaron Colwelld34116aaa2020-11-11 02:15:37352 CHECK(false) << "Unexpected call to Run()";
[email protected]61c86c62011-08-02 16:11:16353}
354
Ran Ji3d6ec662018-07-09 21:18:30355void MessagePumpForUI::Attach(Delegate* delegate) {
Michael Thiessendbeca242017-08-28 21:10:08356 DCHECK(!quit_);
Michael Thiessend7ae7352018-07-10 00:57:13357
358 // Since the Looper is controlled by the UI thread or JavaHandlerThread, we
359 // can't use Run() like we do on other platforms or we would prevent Java
360 // tasks from running. Instead we create and initialize a run loop here, then
361 // return control back to the Looper.
362
363 SetDelegate(delegate);
Michael Thiessen781ddeb2017-11-15 17:07:23364 run_loop_ = std::make_unique<RunLoop>();
[email protected]8e937c1e2012-06-28 22:57:30365 // Since the RunLoop was just created above, BeforeRun should be guaranteed to
366 // return true (it only returns false if the RunLoop has been Quit already).
367 if (!run_loop_->BeforeRun())
368 NOTREACHED();
[email protected]61c86c62011-08-02 16:11:16369}
370
371void MessagePumpForUI::Quit() {
Michael Thiessend7ae7352018-07-10 00:57:13372 if (quit_)
373 return;
374
Michael Thiessendbeca242017-08-28 21:10:08375 quit_ = true;
Michael Thiessen781ddeb2017-11-15 17:07:23376
Michael Thiessend7ae7352018-07-10 00:57:13377 int64_t value;
378 // Clear any pending timer.
379 read(delayed_fd_, &value, sizeof(value));
Stephen Nusko408b9a92022-09-15 10:03:57380 // Clear any pending timer.
381 read(resume_after_yielding_non_delayed_fd_, &value, sizeof(value));
Michael Thiessend7ae7352018-07-10 00:57:13382 // Clear the eventfd.
383 read(non_delayed_fd_, &value, sizeof(value));
[email protected]61c86c62011-08-02 16:11:16384
[email protected]8e937c1e2012-06-28 22:57:30385 if (run_loop_) {
386 run_loop_->AfterRun();
Michael Thiessen781ddeb2017-11-15 17:07:23387 run_loop_ = nullptr;
[email protected]61c86c62011-08-02 16:11:16388 }
Michael Thiessend7ae7352018-07-10 00:57:13389 if (on_quit_callback_) {
390 std::move(on_quit_callback_).Run();
391 }
[email protected]61c86c62011-08-02 16:11:16392}
393
394void MessagePumpForUI::ScheduleWork() {
Aaron Colwell48c4d5072020-11-13 16:45:03395 ScheduleWorkInternal(/*do_idle_work=*/false);
396}
397
398void MessagePumpForUI::ScheduleWorkInternal(bool do_idle_work) {
399 // Write (add) |value| to the eventfd. This tells the Looper to wake up and
400 // call our callback, allowing us to run tasks. This also allows us to detect,
401 // when we clear the fd, whether additional work was scheduled after we
402 // finished performing work, but before we cleared the fd, as we'll read back
403 // >=2 instead of 1 in that case. See the eventfd man pages
Michael Thiessend7ae7352018-07-10 00:57:13404 // (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
405 // the read and write APIs for this file descriptor work, specifically without
406 // EFD_SEMAPHORE.
Aaron Colwell48c4d5072020-11-13 16:45:03407 // Note: Calls with |do_idle_work| set to true may race with potential calls
Stephen Nusko408b9a92022-09-15 10:03:57408 // where the parameter is false. This is fine as write() is adding
409 // |value|,do_idle_work have their values added together. Since idle work is
410 // only executed when the value read equals kTryNativeWorkBeforeIdleBit, a
411 // race would prevent idle work from being run and trigger another call to
412 // this method with |do_idle_work| set to true.delayed_fd_
413
414 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("base"),
415 "MessagePumpForUI::ScheduleWorkInternal", "do_idle_work",
416 do_idle_work);
417
Gabriel Charette300f16792022-07-06 20:03:30418 uint64_t value = do_idle_work ? kTryNativeWorkBeforeIdleBit : 1;
Peter Kasting2f61c8b2022-07-19 23:43:46419 long ret = write(non_delayed_fd_, &value, sizeof(value));
Michael Thiessen7c36083d2018-08-10 20:24:54420 DPCHECK(ret >= 0);
[email protected]61c86c62011-08-02 16:11:16421}
422
Stephen Nusko408b9a92022-09-15 10:03:57423void MessagePumpForUI::ScheduleWorkWithDelay() {
424 TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("base"),
425 "MessagePumpForUI::ScheduleWorkWithDelay");
François Doray253a3062022-10-24 16:45:29426
427 // This initialization can't be done in the constructor because the
428 // FeatureList is not initialized at that time.
429 if (!yield_duration_.has_value())
430 yield_duration_ = kBrowserPeriodicYieldingToNativeDelay.Get();
431
Stephen Nusko408b9a92022-09-15 10:03:57432 auto timer = TimeTicks::Now();
433 struct itimerspec ts =
François Doray253a3062022-10-24 16:45:29434 GetTimerSpecDelay(timer.since_origin() + yield_duration_.value());
Stephen Nusko408b9a92022-09-15 10:03:57435 long ret = timerfd_settime(resume_after_yielding_non_delayed_fd_,
436 TFD_TIMER_ABSTIME, &ts, nullptr);
437 DPCHECK(ret >= 0);
438}
439
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33440void MessagePumpForUI::ScheduleDelayedWork(
441 const Delegate::NextWorkInfo& next_work_info) {
Michael Thiessend7ae7352018-07-10 00:57:13442 if (ShouldQuit())
Michael Thiessendbeca242017-08-28 21:10:08443 return;
Michael Thiessend7ae7352018-07-10 00:57:13444
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33445 if (delayed_scheduled_time_ &&
446 *delayed_scheduled_time_ == next_work_info.delayed_run_time) {
Michael Thiessen781ddeb2017-11-15 17:07:23447 return;
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33448 }
Michael Thiessend7ae7352018-07-10 00:57:13449
Etienne Pierre-dorayf2f8e13b2022-03-10 12:42:33450 DCHECK(!next_work_info.is_immediate());
451 delayed_scheduled_time_ = next_work_info.delayed_run_time;
Stephen Nusko408b9a92022-09-15 10:03:57452 TimeDelta yield_delay = next_work_info.delayed_run_time.since_origin();
453 struct itimerspec ts = GetTimerSpecDelay(yield_delay);
Peter Kasting2f61c8b2022-07-19 23:43:46454 long ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
Michael Thiessen7c36083d2018-08-10 20:24:54455 DPCHECK(ret >= 0);
Michael Thiessend7ae7352018-07-10 00:57:13456}
457
458void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
Stephen Nusko408b9a92022-09-15 10:03:57459 TRACE_EVENT(TRACE_DISABLED_BY_DEFAULT("base"),
460 "MessagePumpForUI::QuitWhenIdle");
Michael Thiessend7ae7352018-07-10 00:57:13461 DCHECK(!on_quit_callback_);
462 DCHECK(run_loop_);
463 on_quit_callback_ = std::move(callback);
464 run_loop_->QuitWhenIdle();
465 // Pump the loop in case we're already idle.
466 ScheduleWork();
467}
468
Aaron Colwell48c4d5072020-11-13 16:45:03469MessagePump::Delegate* MessagePumpForUI::SetDelegate(Delegate* delegate) {
470 return std::exchange(delegate_, delegate);
471}
472
473bool MessagePumpForUI::SetQuit(bool quit) {
474 return std::exchange(quit_, quit);
475}
476
[email protected]61c86c62011-08-02 16:11:16477} // namespace base