Skip to content

Commit

Permalink
fuchsia: Implement WakeUp using zx::timer (flutter#29019)
Browse files Browse the repository at this point in the history
  • Loading branch information
arbreng authored Oct 6, 2021
1 parent 97f0274 commit 0e87d51
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 8 deletions.
1 change: 1 addition & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ FILE: ../../../flutter/fml/message_loop.cc
FILE: ../../../flutter/fml/message_loop.h
FILE: ../../../flutter/fml/message_loop_impl.cc
FILE: ../../../flutter/fml/message_loop_impl.h
FILE: ../../../flutter/fml/message_loop_impl_unittests.cc
FILE: ../../../flutter/fml/message_loop_task_queues.cc
FILE: ../../../flutter/fml/message_loop_task_queues.h
FILE: ../../../flutter/fml/message_loop_task_queues_benchmark.cc
Expand Down
1 change: 1 addition & 0 deletions fml/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ if (enable_unittests) {
"memory/ref_counted_unittest.cc",
"memory/task_runner_checker_unittest.cc",
"memory/weak_ptr_unittest.cc",
"message_loop_impl_unittests.cc",
"message_loop_task_queues_merge_unmerge_unittests.cc",
"message_loop_task_queues_unittests.cc",
"message_loop_unittests.cc",
Expand Down
43 changes: 43 additions & 0 deletions fml/message_loop_impl_unittests.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#define FML_USED_ON_EMBEDDER

#include "flutter/fml/message_loop_impl.h"

#include "flutter/fml/time/time_delta.h"
#include "flutter/fml/time/time_point.h"
#include "gtest/gtest.h"

#define TIMESENSITIVE(x) TimeSensitiveTest_##x

TEST(MessageLoopImpl, TIMESENSITIVE(WakeUpTimersAreSingletons)) {
auto loop_impl = fml::MessageLoopImpl::Create();

const auto t1 = fml::TimeDelta::FromMilliseconds(10);
const auto t2 = fml::TimeDelta::FromMilliseconds(30);

const auto begin = fml::TimePoint::Now();

// Register a task scheduled in the future. This schedules a WakeUp call on
// the MessageLoopImpl with that fml::TimePoint.
loop_impl->PostTask(
[&]() {
auto delta = fml::TimePoint::Now() - begin;
auto ms = delta.ToMillisecondsF();
ASSERT_GE(ms, 20);
ASSERT_LE(ms, 40);

loop_impl->Terminate();
},
begin + t1);

// Call WakeUp manually to change the WakeUp time further in the future. If
// the timer is correctly set up to be rearmed instead of a task being
// scheduled for each WakeUp, the above task will be executed at t2 instead of
// t1 now.
loop_impl->WakeUp(begin + t2);

loop_impl->Run();
}
68 changes: 60 additions & 8 deletions fml/platform/fuchsia/message_loop_fuchsia.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <lib/zx/time.h>
#include <zircon/status.h>

#include "flutter/fml/platform/fuchsia/task_observers.h"

namespace fml {
Expand All @@ -28,6 +30,12 @@ constexpr async_loop_config_t kLoopConfig = {

MessageLoopFuchsia::MessageLoopFuchsia() : loop_(&kLoopConfig) {
async_set_default_dispatcher(loop_.dispatcher());

zx_status_t timer_status =
zx::timer::create(ZX_TIMER_SLACK_LATE, ZX_CLOCK_MONOTONIC, &timer_);
FML_CHECK(timer_status == ZX_OK)
<< "MessageLoopFuchsia failed to create timer; status="
<< zx_status_get_string(timer_status);
}

MessageLoopFuchsia::~MessageLoopFuchsia() {
Expand All @@ -39,23 +47,67 @@ MessageLoopFuchsia::~MessageLoopFuchsia() {
}

void MessageLoopFuchsia::Run() {
timer_wait_ = std::make_unique<async::Wait>(
timer_.get(), ZX_TIMER_SIGNALED, 0,
[this](async_dispatcher_t* dispatcher, async::Wait* wait,
zx_status_t status, const zx_packet_signal_t* signal) {
if (status == ZX_ERR_CANCELED) {
return;
}
FML_CHECK(signal->observed & ZX_TIMER_SIGNALED);

// Cancel the timer now, because `RunExpiredTasksNow` might not re-arm
// the timer. That would leave the timer in a signalled state and it
// would trigger the async::Wait again immediately, creating a busy
// loop.
//
// NOTE: It is not neccesary to synchronize this with the timer_.set()
// call below, even though WakeUp() can be called from any thread and
// thus timer_.set() can run in parallel with this timer_.cancel().
//
// Zircon will synchronize the 2 syscalls internally, and the Wait loop
// here is resilient to cancel() and set() being called in any order.
timer_.cancel();

// Run the tasks, which may or may not re-arm the timer for the future.
RunExpiredTasksNow();

// Kick off the next iteration of the timer wait loop.
zx_status_t wait_status = wait->Begin(loop_.dispatcher());
FML_CHECK(wait_status == ZX_OK)
<< "MessageLoopFuchsia::WakeUp failed to wait for timer; status="
<< zx_status_get_string(wait_status);
});

// Kick off the first iteration of the timer wait loop.
zx_status_t wait_status = timer_wait_->Begin(loop_.dispatcher());
FML_CHECK(wait_status == ZX_OK)
<< "MessageLoopFuchsia::WakeUp failed to wait for timer; status="
<< zx_status_get_string(wait_status);

// Kick off the underlying async loop that services the timer wait in addition
// to other tasks and waits queued on its `async_dispatcher_t`.
loop_.Run();

// Ensure any pending waits on the timer are properly canceled.
if (timer_wait_->is_pending()) {
timer_wait_->Cancel();
timer_.cancel();
}
}

void MessageLoopFuchsia::Terminate() {
loop_.Quit();
}

void MessageLoopFuchsia::WakeUp(fml::TimePoint time_point) {
fml::TimePoint now = fml::TimePoint::Now();
zx::duration due_time{0};
if (time_point > now) {
due_time = zx::nsec((time_point - now).ToNanoseconds());
}
constexpr zx::duration kZeroSlack(0);
zx::time due_time(time_point.ToEpochDelta().ToNanoseconds());

auto status = async::PostDelayedTask(
loop_.dispatcher(), [this]() { RunExpiredTasksNow(); }, due_time);
FML_DCHECK(status == ZX_OK);
zx_status_t timer_status = timer_.set(due_time, kZeroSlack);
FML_CHECK(timer_status == ZX_OK)
<< "MessageLoopFuchsia::WakeUp failed to set timer; status="
<< zx_status_get_string(timer_status);
}

} // namespace fml
4 changes: 4 additions & 0 deletions fml/platform/fuchsia/message_loop_fuchsia.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define FLUTTER_FML_PLATFORM_FUCHSIA_MESSAGE_LOOP_FUCHSIA_H_

#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/wait.h>
#include <lib/zx/timer.h>

#include "flutter/fml/macros.h"
#include "flutter/fml/message_loop_impl.h"
Expand All @@ -25,6 +27,8 @@ class MessageLoopFuchsia : public MessageLoopImpl {
void WakeUp(fml::TimePoint time_point) override;

async::Loop loop_;
std::unique_ptr<async::Wait> timer_wait_;
zx::timer timer_;

FML_FRIEND_MAKE_REF_COUNTED(MessageLoopFuchsia);
FML_FRIEND_REF_COUNTED_THREAD_SAFE(MessageLoopFuchsia);
Expand Down

0 comments on commit 0e87d51

Please sign in to comment.