Fully inplement TaskRunner for UWP (flutter/flutter#70890) (#28013)

diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index b238e78..8e2f603 100755
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1754,7 +1754,9 @@
 FILE: ../../../flutter/shell/platform/windows/system_utils_unittests.cc
 FILE: ../../../flutter/shell/platform/windows/system_utils_win32.cc
 FILE: ../../../flutter/shell/platform/windows/system_utils_winuwp.cc
+FILE: ../../../flutter/shell/platform/windows/task_runner.cc
 FILE: ../../../flutter/shell/platform/windows/task_runner.h
+FILE: ../../../flutter/shell/platform/windows/task_runner_unittests.cc
 FILE: ../../../flutter/shell/platform/windows/task_runner_win32.cc
 FILE: ../../../flutter/shell/platform/windows/task_runner_win32.h
 FILE: ../../../flutter/shell/platform/windows/task_runner_win32_window.cc
diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn
index 525e852..029305e 100644
--- a/shell/platform/windows/BUILD.gn
+++ b/shell/platform/windows/BUILD.gn
@@ -77,6 +77,7 @@
     "sequential_id_generator.cc",
     "sequential_id_generator.h",
     "system_utils.h",
+    "task_runner.cc",
     "task_runner.h",
     "text_input_plugin.cc",
     "text_input_plugin.h",
@@ -226,6 +227,7 @@
     "sequential_id_generator_unittests.cc",
     "string_conversion_unittests.cc",
     "system_utils_unittests.cc",
+    "task_runner_unittests.cc",
     "testing/engine_modifier.h",
     "testing/mock_gl_functions.h",
   ]
diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc
index b6fb9f4..bb020d5 100644
--- a/shell/platform/windows/flutter_windows_engine.cc
+++ b/shell/platform/windows/flutter_windows_engine.cc
@@ -143,8 +143,7 @@
   FlutterEngineGetProcAddresses(&embedder_api_);
 
   task_runner_ = TaskRunner::Create(
-      GetCurrentThreadId(), embedder_api_.GetCurrentTime,
-      [this](const auto* task) {
+      embedder_api_.GetCurrentTime, [this](const auto* task) {
         if (!engine_) {
           std::cerr << "Cannot post an engine task when engine is not running."
                     << std::endl;
diff --git a/shell/platform/windows/task_runner.cc b/shell/platform/windows/task_runner.cc
new file mode 100644
index 0000000..fa8b134
--- /dev/null
+++ b/shell/platform/windows/task_runner.cc
@@ -0,0 +1,104 @@
+// 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.
+
+#include "flutter/shell/platform/windows/task_runner.h"
+
+#include <atomic>
+#include <utility>
+
+namespace flutter {
+
+TaskRunner::TaskRunner(CurrentTimeProc get_current_time,
+                       const TaskExpiredCallback& on_task_expired)
+    : get_current_time_(get_current_time),
+      on_task_expired_(std::move(on_task_expired)) {}
+
+std::chrono::nanoseconds TaskRunner::ProcessTasks() {
+  const TaskTimePoint now = TaskTimePoint::clock::now();
+
+  std::vector<Task> expired_tasks;
+
+  // Process expired tasks.
+  {
+    std::lock_guard<std::mutex> lock(task_queue_mutex_);
+    while (!task_queue_.empty()) {
+      const auto& top = task_queue_.top();
+      // If this task (and all tasks after this) has not yet expired, there is
+      // nothing more to do. Quit iterating.
+      if (top.fire_time > now) {
+        break;
+      }
+
+      // Make a record of the expired task. Do NOT service the task here
+      // because we are still holding onto the task queue mutex. We don't want
+      // other threads to block on posting tasks onto this thread till we are
+      // done processing expired tasks.
+      expired_tasks.push_back(task_queue_.top());
+
+      // Remove the tasks from the delayed tasks queue.
+      task_queue_.pop();
+    }
+  }
+
+  // Fire expired tasks.
+  {
+    // Flushing tasks here without holing onto the task queue mutex.
+    for (const auto& task : expired_tasks) {
+      if (auto flutter_task = std::get_if<FlutterTask>(&task.variant)) {
+        on_task_expired_(flutter_task);
+      } else if (auto closure = std::get_if<TaskClosure>(&task.variant))
+        (*closure)();
+    }
+  }
+
+  // Calculate duration to sleep for on next iteration.
+  {
+    std::lock_guard<std::mutex> lock(task_queue_mutex_);
+    const auto next_wake = task_queue_.empty() ? TaskTimePoint::max()
+                                               : task_queue_.top().fire_time;
+
+    return std::min(next_wake - now, std::chrono::nanoseconds::max());
+  }
+}
+
+TaskRunner::TaskTimePoint TaskRunner::TimePointFromFlutterTime(
+    uint64_t flutter_target_time_nanos) const {
+  const auto now = TaskTimePoint::clock::now();
+  const auto flutter_duration = flutter_target_time_nanos - get_current_time_();
+  return now + std::chrono::nanoseconds(flutter_duration);
+}
+
+void TaskRunner::PostFlutterTask(FlutterTask flutter_task,
+                                 uint64_t flutter_target_time_nanos) {
+  Task task;
+  task.fire_time = TimePointFromFlutterTime(flutter_target_time_nanos);
+  task.variant = flutter_task;
+  EnqueueTask(std::move(task));
+}
+
+void TaskRunner::PostTask(TaskClosure closure) {
+  Task task;
+  task.fire_time = TaskTimePoint::clock::now();
+  task.variant = std::move(closure);
+  EnqueueTask(std::move(task));
+}
+
+void TaskRunner::EnqueueTask(Task task) {
+  static std::atomic_uint64_t sGlobalTaskOrder(0);
+
+  task.order = ++sGlobalTaskOrder;
+  {
+    std::lock_guard<std::mutex> lock(task_queue_mutex_);
+    task_queue_.push(task);
+
+    // Make sure the queue mutex is unlocked before waking up the loop. In case
+    // the wake causes this thread to be descheduled for the primary thread to
+    // process tasks, the acquisition of the lock on that thread while holding
+    // the lock here momentarily till the end of the scope is a pessimization.
+  }
+
+  WakeUp();
+}
+
+}  // namespace flutter
diff --git a/shell/platform/windows/task_runner.h b/shell/platform/windows/task_runner.h
index 0e3d444..7f181d0 100644
--- a/shell/platform/windows/task_runner.h
+++ b/shell/platform/windows/task_runner.h
@@ -5,10 +5,13 @@
 #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TASK_RUNNER_H_
 #define FLUTTER_SHELL_PLATFORM_WINDOWS_TASK_RUNNER_H_
 
-#include <windows.h>
-
 #include <chrono>
+#include <deque>
+#include <functional>
 #include <memory>
+#include <mutex>
+#include <queue>
+#include <variant>
 
 #include "flutter/shell/platform/embedder/embedder.h"
 
@@ -16,7 +19,6 @@
 
 typedef uint64_t (*CurrentTimeProc)();
 
-// Abstract custom task runner for scheduling custom tasks.
 class TaskRunner {
  public:
   using TaskTimePoint = std::chrono::steady_clock::time_point;
@@ -25,18 +27,18 @@
 
   virtual ~TaskRunner() = default;
 
-  // Returns if the current thread is the UI thread.
+  // Returns `true` if the current thread is this runner's thread.
   virtual bool RunsTasksOnCurrentThread() const = 0;
 
   // Post a Flutter engine task to the event loop for delayed execution.
-  virtual void PostFlutterTask(FlutterTask flutter_task,
-                               uint64_t flutter_target_time_nanos) = 0;
+  void PostFlutterTask(FlutterTask flutter_task,
+                       uint64_t flutter_target_time_nanos);
 
   // Post a task to the event loop.
-  virtual void PostTask(TaskClosure task) = 0;
+  void PostTask(TaskClosure task);
 
   // Post a task to the event loop or run it immediately if this is being called
-  // from the main thread.
+  // from the runner's thread.
   void RunNowOrPostTask(TaskClosure task) {
     if (RunsTasksOnCurrentThread()) {
       task();
@@ -45,12 +47,57 @@
     }
   }
 
-  // Creates a new task runner with the given main thread ID, current time
+  // Creates a new task runner with the current thread, current time
   // provider, and callback for tasks that are ready to be run.
   static std::unique_ptr<TaskRunner> Create(
-      DWORD main_thread_id,
       CurrentTimeProc get_current_time,
       const TaskExpiredCallback& on_task_expired);
+
+ protected:
+  TaskRunner(CurrentTimeProc get_current_time,
+             const TaskExpiredCallback& on_task_expired);
+
+  // Schedules timers to call `ProcessTasks()` at the runner's thread.
+  virtual void WakeUp() = 0;
+
+  // Executes expired task, and returns the duration until the next task
+  // deadline if exists, otherwise returns `std::chrono::nanoseconds::max()`.
+  //
+  // Each platform implementations must call this to schedule the tasks.
+  std::chrono::nanoseconds ProcessTasks();
+
+ private:
+  typedef std::variant<FlutterTask, TaskClosure> TaskVariant;
+
+  struct Task {
+    uint64_t order;
+    TaskTimePoint fire_time;
+    TaskVariant variant;
+
+    struct Comparer {
+      bool operator()(const Task& a, const Task& b) {
+        if (a.fire_time == b.fire_time) {
+          return a.order > b.order;
+        }
+        return a.fire_time > b.fire_time;
+      }
+    };
+  };
+
+  // Enqueues the given task.
+  void EnqueueTask(Task task);
+
+  // Returns a TaskTimePoint computed from the given target time from Flutter.
+  TaskTimePoint TimePointFromFlutterTime(
+      uint64_t flutter_target_time_nanos) const;
+
+  CurrentTimeProc get_current_time_;
+  TaskExpiredCallback on_task_expired_;
+  std::mutex task_queue_mutex_;
+  std::priority_queue<Task, std::deque<Task>, Task::Comparer> task_queue_;
+
+  TaskRunner(const TaskRunner&) = delete;
+  TaskRunner& operator=(const TaskRunner&) = delete;
 };
 
 }  // namespace flutter
diff --git a/shell/platform/windows/task_runner_unittests.cc b/shell/platform/windows/task_runner_unittests.cc
new file mode 100644
index 0000000..713474d
--- /dev/null
+++ b/shell/platform/windows/task_runner_unittests.cc
@@ -0,0 +1,89 @@
+// 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.
+
+#include <chrono>
+
+#include "flutter/fml/time/time_point.h"
+
+#include "flutter/shell/platform/windows/task_runner.h"
+
+#include "gtest/gtest.h"
+
+namespace flutter {
+namespace testing {
+
+namespace {
+class MockTaskRunner : public TaskRunner {
+ public:
+  MockTaskRunner(CurrentTimeProc get_current_time,
+                 const TaskExpiredCallback& on_task_expired)
+      : TaskRunner(get_current_time, on_task_expired) {}
+
+  virtual bool RunsTasksOnCurrentThread() const override { return true; }
+
+  void SimulateTimerAwake() { ProcessTasks(); }
+
+ protected:
+  virtual void WakeUp() override {
+    // Do nothing to avoid processing tasks immediately after the tasks is
+    // posted.
+  }
+};
+
+uint64_t MockGetCurrentTime() {
+  return static_cast<uint64_t>(
+      fml::TimePoint::Now().ToEpochDelta().ToNanoseconds());
+}
+}  // namespace
+
+TEST(TaskRunnerTest, MaybeExecuteTaskWithExactOrder) {
+  std::vector<uint64_t> executed_task_order;
+  auto runner =
+      MockTaskRunner(MockGetCurrentTime,
+                     [&executed_task_order](const FlutterTask* expired_task) {
+                       executed_task_order.push_back(expired_task->task);
+                     });
+
+  uint64_t time_now = MockGetCurrentTime();
+
+  runner.PostFlutterTask(FlutterTask{nullptr, 1}, time_now);
+  runner.PostFlutterTask(FlutterTask{nullptr, 2}, time_now);
+  runner.PostTask(
+      [&executed_task_order]() { executed_task_order.push_back(3); });
+  runner.PostTask(
+      [&executed_task_order]() { executed_task_order.push_back(4); });
+
+  runner.SimulateTimerAwake();
+
+  std::vector<uint64_t> posted_task_order{1, 2, 3, 4};
+  EXPECT_EQ(executed_task_order, posted_task_order);
+}
+
+TEST(TaskRunnerTest, MaybeExecuteTaskOnlyExpired) {
+  std::set<uint64_t> executed_task;
+  auto runner = MockTaskRunner(
+      MockGetCurrentTime, [&executed_task](const FlutterTask* expired_task) {
+        executed_task.insert(expired_task->task);
+      });
+
+  uint64_t time_now = MockGetCurrentTime();
+
+  uint64_t task_expired_before_now = 1;
+  uint64_t time_before_now = time_now - 10000;
+  runner.PostFlutterTask(FlutterTask{nullptr, task_expired_before_now},
+                         time_before_now);
+
+  uint64_t task_expired_after_now = 2;
+  uint64_t time_after_now = time_now + 10000;
+  runner.PostFlutterTask(FlutterTask{nullptr, task_expired_after_now},
+                         time_after_now);
+
+  runner.SimulateTimerAwake();
+
+  std::set<uint64_t> only_task_expired_before_now{task_expired_before_now};
+  EXPECT_EQ(executed_task, only_task_expired_before_now);
+}
+
+}  // namespace testing
+}  // namespace flutter
diff --git a/shell/platform/windows/task_runner_win32.cc b/shell/platform/windows/task_runner_win32.cc
index 62de4a1..01c7179 100644
--- a/shell/platform/windows/task_runner_win32.cc
+++ b/shell/platform/windows/task_runner_win32.cc
@@ -4,27 +4,19 @@
 
 #include "flutter/shell/platform/windows/task_runner_win32.h"
 
-#include <atomic>
-#include <iostream>
-#include <utility>
-
 namespace flutter {
 
 // static
 std::unique_ptr<TaskRunner> TaskRunner::Create(
-    DWORD main_thread_id,
     CurrentTimeProc get_current_time,
     const TaskExpiredCallback& on_task_expired) {
-  return std::make_unique<TaskRunnerWin32>(main_thread_id, get_current_time,
-                                           on_task_expired);
+  return std::make_unique<TaskRunnerWin32>(get_current_time, on_task_expired);
 }
 
-TaskRunnerWin32::TaskRunnerWin32(DWORD main_thread_id,
-                                 CurrentTimeProc get_current_time,
+TaskRunnerWin32::TaskRunnerWin32(CurrentTimeProc get_current_time,
                                  const TaskExpiredCallback& on_task_expired)
-    : main_thread_id_(main_thread_id),
-      get_current_time_(get_current_time),
-      on_task_expired_(std::move(on_task_expired)) {
+    : TaskRunner(get_current_time, on_task_expired) {
+  main_thread_id_ = GetCurrentThreadId();
   task_runner_window_ = TaskRunnerWin32Window::GetSharedInstance();
   task_runner_window_->AddDelegate(this);
 }
@@ -38,89 +30,10 @@
 }
 
 std::chrono::nanoseconds TaskRunnerWin32::ProcessTasks() {
-  const TaskTimePoint now = TaskTimePoint::clock::now();
-
-  std::vector<Task> expired_tasks;
-
-  // Process expired tasks.
-  {
-    std::lock_guard<std::mutex> lock(task_queue_mutex_);
-    while (!task_queue_.empty()) {
-      const auto& top = task_queue_.top();
-      // If this task (and all tasks after this) has not yet expired, there is
-      // nothing more to do. Quit iterating.
-      if (top.fire_time > now) {
-        break;
-      }
-
-      // Make a record of the expired task. Do NOT service the task here
-      // because we are still holding onto the task queue mutex. We don't want
-      // other threads to block on posting tasks onto this thread till we are
-      // done processing expired tasks.
-      expired_tasks.push_back(task_queue_.top());
-
-      // Remove the tasks from the delayed tasks queue.
-      task_queue_.pop();
-    }
-  }
-
-  // Fire expired tasks.
-  {
-    // Flushing tasks here without holing onto the task queue mutex.
-    for (const auto& task : expired_tasks) {
-      if (auto flutter_task = std::get_if<FlutterTask>(&task.variant)) {
-        on_task_expired_(flutter_task);
-      } else if (auto closure = std::get_if<TaskClosure>(&task.variant))
-        (*closure)();
-    }
-  }
-
-  // Calculate duration to sleep for on next iteration.
-  {
-    std::lock_guard<std::mutex> lock(task_queue_mutex_);
-    const auto next_wake = task_queue_.empty() ? TaskTimePoint::max()
-                                               : task_queue_.top().fire_time;
-
-    return std::min(next_wake - now, std::chrono::nanoseconds::max());
-  }
+  return TaskRunner::ProcessTasks();
 }
 
-TaskRunnerWin32::TaskTimePoint TaskRunnerWin32::TimePointFromFlutterTime(
-    uint64_t flutter_target_time_nanos) const {
-  const auto now = TaskTimePoint::clock::now();
-  const auto flutter_duration = flutter_target_time_nanos - get_current_time_();
-  return now + std::chrono::nanoseconds(flutter_duration);
-}
-
-void TaskRunnerWin32::PostFlutterTask(FlutterTask flutter_task,
-                                      uint64_t flutter_target_time_nanos) {
-  Task task;
-  task.fire_time = TimePointFromFlutterTime(flutter_target_time_nanos);
-  task.variant = flutter_task;
-  EnqueueTask(std::move(task));
-}
-
-void TaskRunnerWin32::PostTask(TaskClosure closure) {
-  Task task;
-  task.fire_time = TaskTimePoint::clock::now();
-  task.variant = std::move(closure);
-  EnqueueTask(std::move(task));
-}
-
-void TaskRunnerWin32::EnqueueTask(Task task) {
-  static std::atomic_uint64_t sGlobalTaskOrder(0);
-
-  task.order = ++sGlobalTaskOrder;
-  {
-    std::lock_guard<std::mutex> lock(task_queue_mutex_);
-    task_queue_.push(task);
-
-    // Make sure the queue mutex is unlocked before waking up the loop. In case
-    // the wake causes this thread to be descheduled for the primary thread to
-    // process tasks, the acquisition of the lock on that thread while holding
-    // the lock here momentarily till the end of the scope is a pessimization.
-  }
-
+void TaskRunnerWin32::WakeUp() {
   task_runner_window_->WakeUp();
 }
 
diff --git a/shell/platform/windows/task_runner_win32.h b/shell/platform/windows/task_runner_win32.h
index 149e9ac..6e3ceb8 100644
--- a/shell/platform/windows/task_runner_win32.h
+++ b/shell/platform/windows/task_runner_win32.h
@@ -7,14 +7,6 @@
 
 #include <windows.h>
 
-#include <chrono>
-#include <deque>
-#include <functional>
-#include <mutex>
-#include <queue>
-#include <thread>
-#include <variant>
-
 #include "flutter/shell/platform/embedder/embedder.h"
 #include "flutter/shell/platform/windows/task_runner.h"
 #include "flutter/shell/platform/windows/task_runner_win32_window.h"
@@ -27,61 +19,25 @@
 class TaskRunnerWin32 : public TaskRunner,
                         public TaskRunnerWin32Window::Delegate {
  public:
-  // Creates a new task runner with the given main thread ID, current time
-  // provider, and callback for tasks that are ready to be run.
-  TaskRunnerWin32(DWORD main_thread_id,
-                  CurrentTimeProc get_current_time,
+  TaskRunnerWin32(CurrentTimeProc get_current_time,
                   const TaskExpiredCallback& on_task_expired);
-
   virtual ~TaskRunnerWin32();
 
   // |TaskRunner|
   bool RunsTasksOnCurrentThread() const override;
 
-  // |TaskRunner|
-  void PostFlutterTask(FlutterTask flutter_task,
-                       uint64_t flutter_target_time_nanos) override;
-
-  // |TaskRunner|
-  void PostTask(TaskClosure task) override;
-
   // |TaskRunnerWin32Window::Delegate|
   std::chrono::nanoseconds ProcessTasks() override;
 
+ protected:
+  // |TaskRunner|
+  void WakeUp() override;
+
  private:
-  typedef std::variant<FlutterTask, TaskClosure> TaskVariant;
-
-  struct Task {
-    uint64_t order;
-    TaskTimePoint fire_time;
-    TaskVariant variant;
-
-    struct Comparer {
-      bool operator()(const Task& a, const Task& b) {
-        if (a.fire_time == b.fire_time) {
-          return a.order > b.order;
-        }
-        return a.fire_time > b.fire_time;
-      }
-    };
-  };
-
-  // Enqueues the given task.
-  void EnqueueTask(Task task);
-
-  // Returns a TaskTimePoint computed from the given target time from Flutter.
-  TaskTimePoint TimePointFromFlutterTime(
-      uint64_t flutter_target_time_nanos) const;
-
   DWORD main_thread_id_;
-  CurrentTimeProc get_current_time_;
-  TaskExpiredCallback on_task_expired_;
-  std::mutex task_queue_mutex_;
-  std::priority_queue<Task, std::deque<Task>, Task::Comparer> task_queue_;
   std::shared_ptr<TaskRunnerWin32Window> task_runner_window_;
 
   TaskRunnerWin32(const TaskRunnerWin32&) = delete;
-
   TaskRunnerWin32& operator=(const TaskRunnerWin32&) = delete;
 };
 
diff --git a/shell/platform/windows/task_runner_winuwp.cc b/shell/platform/windows/task_runner_winuwp.cc
index 9f8ebaf..8bba42e 100644
--- a/shell/platform/windows/task_runner_winuwp.cc
+++ b/shell/platform/windows/task_runner_winuwp.cc
@@ -4,48 +4,50 @@
 
 #include "flutter/shell/platform/windows/task_runner_winuwp.h"
 
-#include <atomic>
-#include <utility>
-
 namespace flutter {
 
 // static
 std::unique_ptr<TaskRunner> TaskRunner::Create(
-    DWORD main_thread_id,
     CurrentTimeProc get_current_time,
     const TaskExpiredCallback& on_task_expired) {
-  return std::make_unique<TaskRunnerWinUwp>(main_thread_id, on_task_expired);
+  return std::make_unique<TaskRunnerWinUwp>(get_current_time, on_task_expired);
 }
 
-TaskRunnerWinUwp::TaskRunnerWinUwp(DWORD main_thread_id,
+TaskRunnerWinUwp::TaskRunnerWinUwp(CurrentTimeProc get_current_time,
                                    const TaskExpiredCallback& on_task_expired)
-    : main_thread_id_(main_thread_id),
-      on_task_expired_(std::move(on_task_expired)) {
-  dispatcher_ =
-      winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher();
+    : TaskRunner(get_current_time, on_task_expired) {
+  dispatcher_queue_ =
+      winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
+  dispatcher_queue_timer_ = dispatcher_queue_.CreateTimer();
+  dispatcher_queue_timer_.Tick({this, &TaskRunnerWinUwp::OnTick});
 }
 
 TaskRunnerWinUwp::~TaskRunnerWinUwp() = default;
 
 bool TaskRunnerWinUwp::RunsTasksOnCurrentThread() const {
-  return GetCurrentThreadId() == main_thread_id_;
+  return dispatcher_queue_.HasThreadAccess();
 }
 
-void TaskRunnerWinUwp::PostFlutterTask(FlutterTask flutter_task,
-                                       uint64_t flutter_target_time_nanos) {
-  // TODO: Handle the target time. See
-  // https://github.com/flutter/flutter/issues/70890.
-
-  dispatcher_.RunAsync(
-      winrt::Windows::UI::Core::CoreDispatcherPriority::Normal,
-      [this, flutter_task]() { on_task_expired_(&flutter_task); });
+void TaskRunnerWinUwp::WakeUp() {
+  dispatcher_queue_.TryEnqueue([this]() { ProcessTasksAndScheduleNext(); });
 }
 
-void TaskRunnerWinUwp::PostTask(TaskClosure task) {
-  // TODO: Handle the target time. See PostFlutterTask()
+void TaskRunnerWinUwp::OnTick(
+    winrt::Windows::System::DispatcherQueueTimer const&,
+    winrt::Windows::Foundation::IInspectable const&) {
+  ProcessTasks();
+}
 
-  dispatcher_.RunAsync(winrt::Windows::UI::Core::CoreDispatcherPriority::Normal,
-                       [task]() { task(); });
+void TaskRunnerWinUwp::ProcessTasksAndScheduleNext() {
+  auto next = ProcessTasks();
+
+  if (next == std::chrono::nanoseconds::max()) {
+    dispatcher_queue_timer_.Stop();
+  } else {
+    dispatcher_queue_timer_.Interval(
+        std::chrono::duration_cast<winrt::Windows::Foundation::TimeSpan>(next));
+    dispatcher_queue_timer_.Start();
+  }
 }
 
 }  // namespace flutter
diff --git a/shell/platform/windows/task_runner_winuwp.h b/shell/platform/windows/task_runner_winuwp.h
index 7ee6d19..d92ce80a 100644
--- a/shell/platform/windows/task_runner_winuwp.h
+++ b/shell/platform/windows/task_runner_winuwp.h
@@ -2,51 +2,44 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_WINRT_TASK_RUNNER_H_
-#define FLUTTER_SHELL_PLATFORM_WINDOWS_WINRT_TASK_RUNNER_H_
+#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TASK_RUNNER_WINUWP_H_
+#define FLUTTER_SHELL_PLATFORM_WINDOWS_TASK_RUNNER_WINUWP_H_
 
-#include <windows.h>
-
-#include <third_party/cppwinrt/generated/winrt/Windows.UI.Core.h>
-
-#include <chrono>
-#include <functional>
-#include <thread>
+#include <third_party/cppwinrt/generated/winrt/Windows.Foundation.h>
+#include <third_party/cppwinrt/generated/winrt/Windows.System.h>
 
 #include "flutter/shell/platform/embedder/embedder.h"
 #include "flutter/shell/platform/windows/task_runner.h"
 
 namespace flutter {
 
-// A custom task runner that uses a CoreDispatcher to schedule
-// flutter tasks.
+// A custom task runner that uses a DispatcherQueue.
 class TaskRunnerWinUwp : public TaskRunner {
  public:
-  TaskRunnerWinUwp(DWORD main_thread_id,
+  TaskRunnerWinUwp(CurrentTimeProc get_current_time,
                    const TaskExpiredCallback& on_task_expired);
-
-  ~TaskRunnerWinUwp();
-
-  TaskRunnerWinUwp(const TaskRunnerWinUwp&) = delete;
-  TaskRunnerWinUwp& operator=(const TaskRunnerWinUwp&) = delete;
+  virtual ~TaskRunnerWinUwp();
 
   // |TaskRunner|
   bool RunsTasksOnCurrentThread() const override;
 
+ protected:
   // |TaskRunner|
-  void PostFlutterTask(FlutterTask flutter_task,
-                       uint64_t flutter_target_time_nanos) override;
-
-  // |TaskRunner|
-  void PostTask(TaskClosure task) override;
+  void WakeUp() override;
 
  private:
-  DWORD main_thread_id_;
-  TaskExpiredCallback on_task_expired_;
+  void OnTick(winrt::Windows::System::DispatcherQueueTimer const&,
+              winrt::Windows::Foundation::IInspectable const&);
 
-  winrt::Windows::UI::Core::CoreDispatcher dispatcher_{nullptr};
+  void ProcessTasksAndScheduleNext();
+
+  winrt::Windows::System::DispatcherQueue dispatcher_queue_{nullptr};
+  winrt::Windows::System::DispatcherQueueTimer dispatcher_queue_timer_{nullptr};
+
+  TaskRunnerWinUwp(const TaskRunnerWinUwp&) = delete;
+  TaskRunnerWinUwp& operator=(const TaskRunnerWinUwp&) = delete;
 };
 
 }  // namespace flutter
 
-#endif  // FLUTTER_SHELL_PLATFORM_WINDOWS_WINRT_TASK_RUNNER_H_
+#endif  // FLUTTER_SHELL_PLATFORM_WINDOWS_TASK_RUNNER_WINUWP_H_