diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 873841def76fa8ac41127f8e91ea58d1cfb1a99c..a8242e8f56f660659c80e5066db4e4971f8d0ac2 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -224,6 +224,7 @@ FILE: ../../../flutter/fml/trace_event.h FILE: ../../../flutter/fml/unique_fd.cc FILE: ../../../flutter/fml/unique_fd.h FILE: ../../../flutter/fml/unique_object.h +FILE: ../../../flutter/fml/wakeable.h FILE: ../../../flutter/lib/io/dart_io.cc FILE: ../../../flutter/lib/io/dart_io.h FILE: ../../../flutter/lib/snapshot/libraries.json diff --git a/fml/BUILD.gn b/fml/BUILD.gn index 279afe4e206e7424600776d5522587c0913e99a8..de37396fe3614083e89726fde5735dc726612c5d 100644 --- a/fml/BUILD.gn +++ b/fml/BUILD.gn @@ -77,6 +77,7 @@ source_set("fml") { "unique_fd.cc", "unique_fd.h", "unique_object.h", + "wakeable.h", ] public_deps = [] diff --git a/fml/message_loop_impl.cc b/fml/message_loop_impl.cc index 5d8f4b9984c0052c07229df6a8394a1615482ceb..5ee993778f1429c8b23aa2172013d6e4adfb5142 100644 --- a/fml/message_loop_impl.cc +++ b/fml/message_loop_impl.cc @@ -41,6 +41,7 @@ fml::RefPtr MessageLoopImpl::Create() { MessageLoopImpl::MessageLoopImpl() : terminated_(false) { task_queue_ = std::make_unique(); + task_queue_->SetWakeable(this); } MessageLoopImpl::~MessageLoopImpl() = default; @@ -53,8 +54,7 @@ void MessageLoopImpl::PostTask(fml::closure task, fml::TimePoint target_time) { // |task| synchronously within this function. return; } - const auto wake_up = task_queue_->RegisterTask(task, target_time); - WakeUp(wake_up); + task_queue_->RegisterTask(task, target_time); } void MessageLoopImpl::AddTaskObserver(intptr_t key, fml::closure callback) { @@ -130,9 +130,7 @@ void MessageLoopImpl::FlushTasks(FlushType type) { // gather invocations -> Swap -> execute invocations // will lead us to run invocations on the wrong thread. std::lock_guard task_flush_lock(tasks_flushing_mutex_); - - const auto wake_up = task_queue_->GetTasksToRunNow(type, invocations); - WakeUp(wake_up); + task_queue_->GetTasksToRunNow(type, invocations); for (const auto& invocation : invocations) { invocation(); diff --git a/fml/message_loop_impl.h b/fml/message_loop_impl.h index 8fa85aed007ce166a234f45d6f131ce487046415..3ff5d9fcf533d8f35ecb09506a5cb36f55d13d6c 100644 --- a/fml/message_loop_impl.h +++ b/fml/message_loop_impl.h @@ -20,10 +20,12 @@ #include "flutter/fml/message_loop_task_queue.h" #include "flutter/fml/synchronization/thread_annotations.h" #include "flutter/fml/time/time_point.h" +#include "flutter/fml/wakeable.h" namespace fml { -class MessageLoopImpl : public fml::RefCountedThreadSafe { +class MessageLoopImpl : public Wakeable, + public fml::RefCountedThreadSafe { public: static fml::RefPtr Create(); @@ -33,8 +35,6 @@ class MessageLoopImpl : public fml::RefCountedThreadSafe { virtual void Terminate() = 0; - virtual void WakeUp(fml::TimePoint time_point) = 0; - void PostTask(fml::closure task, fml::TimePoint target_time); void AddTaskObserver(intptr_t key, fml::closure callback); diff --git a/fml/message_loop_task_queue.cc b/fml/message_loop_task_queue.cc index 11927da2703ef1479598e049eee2c0136ce94435..ef3ac3348e7ebdb7a3ce29f55c21978569af52cb 100644 --- a/fml/message_loop_task_queue.cc +++ b/fml/message_loop_task_queue.cc @@ -5,6 +5,7 @@ #define FML_USED_ON_EMBEDDER #include "flutter/fml/message_loop_task_queue.h" +#include "flutter/fml/message_loop_impl.h" namespace fml { @@ -17,11 +18,11 @@ void MessageLoopTaskQueue::Dispose() { delayed_tasks_ = {}; } -fml::TimePoint MessageLoopTaskQueue::RegisterTask(fml::closure task, - fml::TimePoint target_time) { +void MessageLoopTaskQueue::RegisterTask(fml::closure task, + fml::TimePoint target_time) { std::lock_guard lock(delayed_tasks_mutex_); delayed_tasks_.push({++order_, std::move(task), target_time}); - return delayed_tasks_.top().GetTargetTime(); + WakeUp(delayed_tasks_.top().GetTargetTime()); } bool MessageLoopTaskQueue::HasPendingTasks() { @@ -29,7 +30,7 @@ bool MessageLoopTaskQueue::HasPendingTasks() { return !delayed_tasks_.empty(); } -fml::TimePoint MessageLoopTaskQueue::GetTasksToRunNow( +void MessageLoopTaskQueue::GetTasksToRunNow( FlushType type, std::vector& invocations) { std::lock_guard lock(delayed_tasks_mutex_); @@ -48,9 +49,15 @@ fml::TimePoint MessageLoopTaskQueue::GetTasksToRunNow( } if (delayed_tasks_.empty()) { - return fml::TimePoint::Max(); + WakeUp(fml::TimePoint::Max()); } else { - return delayed_tasks_.top().GetTargetTime(); + WakeUp(delayed_tasks_.top().GetTargetTime()); + } +} + +void MessageLoopTaskQueue::WakeUp(fml::TimePoint time) { + if (wakeable_) { + wakeable_->WakeUp(time); } } @@ -94,4 +101,8 @@ void MessageLoopTaskQueue::Swap(MessageLoopTaskQueue& other) std::swap(delayed_tasks_, other.delayed_tasks_); } +void MessageLoopTaskQueue::SetWakeable(fml::Wakeable* wakeable) { + wakeable_ = wakeable; +} + } // namespace fml diff --git a/fml/message_loop_task_queue.h b/fml/message_loop_task_queue.h index 393565dedf1a1a9282c824dec9aa91c6e9df34c7..8be179311c32b8def810eee080a60ffc6a412d3f 100644 --- a/fml/message_loop_task_queue.h +++ b/fml/message_loop_task_queue.h @@ -14,6 +14,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/memory/ref_counted.h" #include "flutter/fml/synchronization/thread_annotations.h" +#include "flutter/fml/wakeable.h" namespace fml { @@ -23,7 +24,8 @@ enum class FlushType { }; // This class keeps track of all the tasks and observers that -// need to be run on it's MessageLoopImpl. +// need to be run on it's MessageLoopImpl. This also wakes up the +// loop at the required times. class MessageLoopTaskQueue { public: // Lifecycle. @@ -36,13 +38,11 @@ class MessageLoopTaskQueue { // Tasks methods. - fml::TimePoint RegisterTask(fml::closure task, fml::TimePoint target_time); + void RegisterTask(fml::closure task, fml::TimePoint target_time); bool HasPendingTasks(); - // Returns the wake up time. - fml::TimePoint GetTasksToRunNow(FlushType type, - std::vector& invocations); + void GetTasksToRunNow(FlushType type, std::vector& invocations); size_t GetNumPendingTasks(); @@ -58,7 +58,13 @@ class MessageLoopTaskQueue { void Swap(MessageLoopTaskQueue& other); + void SetWakeable(fml::Wakeable* wakeable); + private: + void WakeUp(fml::TimePoint time); + + Wakeable* wakeable_ = NULL; + std::mutex observers_mutex_; std::map task_observers_ FML_GUARDED_BY(observers_mutex_); diff --git a/fml/message_loop_task_queue_unittests.cc b/fml/message_loop_task_queue_unittests.cc index 06077bcb5a2654e8903ec8be22bdd22d7a5ca75e..a38a6e1047705f491767503fc2718937c816436b 100644 --- a/fml/message_loop_task_queue_unittests.cc +++ b/fml/message_loop_task_queue_unittests.cc @@ -5,20 +5,37 @@ #define FML_USED_ON_EMBEDDER #include "flutter/fml/message_loop_task_queue.h" +#include "flutter/fml/synchronization/count_down_latch.h" +#include "flutter/fml/synchronization/waitable_event.h" #include "gtest/gtest.h" +class TestWakeable : public fml::Wakeable { + public: + using WakeUpCall = std::function; + + TestWakeable(WakeUpCall call) : wake_up_call_(call) {} + + void WakeUp(fml::TimePoint time_point) override { wake_up_call_(time_point); } + + private: + WakeUpCall wake_up_call_; +}; + TEST(MessageLoopTaskQueue, StartsWithNoPendingTasks) { auto task_queue = std::make_unique(); ASSERT_FALSE(task_queue->HasPendingTasks()); } TEST(MessageLoopTaskQueue, RegisterOneTask) { - auto task_queue = std::make_unique(); const auto time = fml::TimePoint::Max(); - const auto wake_time = task_queue->RegisterTask([] {}, time); + + auto task_queue = std::make_unique(); + task_queue->SetWakeable(new TestWakeable( + [&time](fml::TimePoint wake_time) { ASSERT_TRUE(wake_time == time); })); + + task_queue->RegisterTask([] {}, time); ASSERT_TRUE(task_queue->HasPendingTasks()); ASSERT_TRUE(task_queue->GetNumPendingTasks() == 1); - ASSERT_TRUE(wake_time == time); } TEST(MessageLoopTaskQueue, RegisterTwoTasksAndCount) { @@ -68,3 +85,51 @@ TEST(MessageLoopTaskQueue, AddRemoveNotifyObservers) { task_queue->NotifyObservers(); ASSERT_TRUE(test_val == 0); } + +TEST(MessageLoopTaskQueue, WakeUpIndependentOfTime) { + auto task_queue = std::make_unique(); + + int num_wakes = 0; + task_queue->SetWakeable(new TestWakeable( + [&num_wakes](fml::TimePoint wake_time) { ++num_wakes; })); + + task_queue->RegisterTask([]() {}, fml::TimePoint::Now()); + task_queue->RegisterTask([]() {}, fml::TimePoint::Max()); + + ASSERT_TRUE(num_wakes == 2); +} + +TEST(MessageLoopTaskQueue, WakeUpWithMaxIfNoInvocations) { + auto task_queue = std::make_unique(); + fml::AutoResetWaitableEvent ev; + + task_queue->SetWakeable(new TestWakeable([&ev](fml::TimePoint wake_time) { + ASSERT_TRUE(wake_time == fml::TimePoint::Max()); + ev.Signal(); + })); + + std::vector invocations; + task_queue->GetTasksToRunNow(fml::FlushType::kAll, invocations); + ev.Wait(); +} + +TEST(MessageLoopTaskQueue, WokenUpWithNewerTime) { + auto task_queue = std::make_unique(); + fml::CountDownLatch latch(2); + + fml::TimePoint expected = fml::TimePoint::Max(); + + task_queue->SetWakeable( + new TestWakeable([&latch, &expected](fml::TimePoint wake_time) { + ASSERT_TRUE(wake_time == expected); + latch.CountDown(); + })); + + task_queue->RegisterTask([]() {}, fml::TimePoint::Max()); + + const auto now = fml::TimePoint::Now(); + expected = now; + task_queue->RegisterTask([]() {}, now); + + latch.Wait(); +} diff --git a/fml/wakeable.h b/fml/wakeable.h new file mode 100644 index 0000000000000000000000000000000000000000..2b3613981764468b04d5d6475de0639d74c90da9 --- /dev/null +++ b/fml/wakeable.h @@ -0,0 +1,21 @@ +// 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. + +#ifndef FLUTTER_FML_WAKEABLE_H_ +#define FLUTTER_FML_WAKEABLE_H_ + +#include "flutter/fml/time/time_point.h" + +namespace fml { + +class Wakeable { + public: + virtual ~Wakeable() {} + + virtual void WakeUp(fml::TimePoint time_point) = 0; +}; + +} // namespace fml + +#endif // FLUTTER_FML_WAKEABLE_H_