From d599d8be5238a3655a856d8fe00f0bd1a4cb62fb Mon Sep 17 00:00:00 2001 From: Niklas Schulze Date: Fri, 28 Jan 2022 14:53:21 +0100 Subject: [PATCH] Windows: Client Wrapper: Expose TaskRunner --- ci/licenses_golden/licenses_flutter | 2 + .../platform/windows/client_wrapper/BUILD.gn | 2 + .../flutter_task_runner_unittests.cc | 79 +++++++++++++++++++ .../client_wrapper/flutter_view_unittests.cc | 8 ++ .../include/flutter/flutter_task_runner.h | 55 +++++++++++++ .../include/flutter/flutter_view.h | 15 +++- .../testing/stub_flutter_windows_api.cc | 22 ++++++ .../testing/stub_flutter_windows_api.h | 11 +++ shell/platform/windows/flutter_windows.cc | 29 +++++++ .../platform/windows/public/flutter_windows.h | 24 +++++- 10 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 shell/platform/windows/client_wrapper/flutter_task_runner_unittests.cc create mode 100644 shell/platform/windows/client_wrapper/include/flutter/flutter_task_runner.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 27424939bcc1b..f57740f875cba 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1740,11 +1740,13 @@ FILE: ../../../flutter/shell/platform/windows/angle_surface_manager.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/dart_project_unittests.cc FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_engine.cc FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc +FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_task_runner_unittests.cc FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_view_controller.cc FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_view_controller_unittests.cc FILE: ../../../flutter/shell/platform/windows/client_wrapper/flutter_view_unittests.cc FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/dart_project.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_task_runner.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_view.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_view_controller.h FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plugin_registrar_windows.h diff --git a/shell/platform/windows/client_wrapper/BUILD.gn b/shell/platform/windows/client_wrapper/BUILD.gn index 18cf24d75abd3..c36e5ef75387c 100644 --- a/shell/platform/windows/client_wrapper/BUILD.gn +++ b/shell/platform/windows/client_wrapper/BUILD.gn @@ -8,6 +8,7 @@ import("//flutter/testing/testing.gni") _wrapper_includes = [ "include/flutter/dart_project.h", "include/flutter/flutter_engine.h", + "include/flutter/flutter_task_runner.h", "include/flutter/flutter_view_controller.h", "include/flutter/flutter_view.h", "include/flutter/plugin_registrar_windows.h", @@ -76,6 +77,7 @@ executable("client_wrapper_windows_unittests") { sources = [ "dart_project_unittests.cc", "flutter_engine_unittests.cc", + "flutter_task_runner_unittests.cc", "flutter_view_controller_unittests.cc", "flutter_view_unittests.cc", "plugin_registrar_windows_unittests.cc", diff --git a/shell/platform/windows/client_wrapper/flutter_task_runner_unittests.cc b/shell/platform/windows/client_wrapper/flutter_task_runner_unittests.cc new file mode 100644 index 0000000000000..9ef683bd38f40 --- /dev/null +++ b/shell/platform/windows/client_wrapper/flutter_task_runner_unittests.cc @@ -0,0 +1,79 @@ +// 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 +#include + +#include "flutter/shell/platform/windows/client_wrapper/include/flutter/flutter_task_runner.h" +#include "flutter/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h" +#include "gtest/gtest.h" + +namespace flutter { +namespace { + +// Stub implementation to validate calls to the API. +class TestWindowsApi : public testing::StubFlutterWindowsApi { + public: + TestWindowsApi(bool runs_tasks_on_current_thread) + : runs_tasks_on_current_thread_(runs_tasks_on_current_thread) {} + + void TaskRunnerPostTask(VoidCallback callback, void* user_data) override { + post_task_called_ = true; + callback(user_data); + } + + bool TaskRunnerRunsTasksOnCurrentThread() override { + return runs_tasks_on_current_thread_; + } + + bool post_task_called() { return post_task_called_; } + + private: + bool runs_tasks_on_current_thread_; + bool post_task_called_ = false; +}; + +} // namespace + +TEST(FlutterTaskRunnerTest, RunsTasksOnCurrentThreadPassesThrough) { + testing::ScopedStubFlutterWindowsApi scoped_api_stub( + std::make_unique(false)); + auto test_api = static_cast(scoped_api_stub.stub()); + FlutterTaskRunner task_runner( + reinterpret_cast(2)); + + EXPECT_FALSE(task_runner.RunsTasksOnCurrentThread()); +} + +TEST(FlutterTaskRunnerTest, PostTaskInline) { + testing::ScopedStubFlutterWindowsApi scoped_api_stub( + std::make_unique(true)); + auto test_api = static_cast(scoped_api_stub.stub()); + FlutterTaskRunner task_runner( + reinterpret_cast(2)); + + bool task_executed = false; + task_runner.PostTask([&task_executed]() { task_executed = true; }); + + EXPECT_TRUE(task_runner.RunsTasksOnCurrentThread()); + EXPECT_TRUE(task_executed); + EXPECT_FALSE(test_api->post_task_called()); +} + +TEST(FlutterTaskRunnerTest, PostTaskFromOtherThread) { + testing::ScopedStubFlutterWindowsApi scoped_api_stub( + std::make_unique(false)); + auto test_api = static_cast(scoped_api_stub.stub()); + FlutterTaskRunner task_runner( + reinterpret_cast(2)); + + bool task_executed = false; + task_runner.PostTask([&task_executed]() { task_executed = true; }); + + EXPECT_FALSE(task_runner.RunsTasksOnCurrentThread()); + EXPECT_TRUE(task_executed); + EXPECT_TRUE(test_api->post_task_called()); +} + +} // namespace flutter diff --git a/shell/platform/windows/client_wrapper/flutter_view_unittests.cc b/shell/platform/windows/client_wrapper/flutter_view_unittests.cc index 6fbe2981aa09b..d48945c81159e 100644 --- a/shell/platform/windows/client_wrapper/flutter_view_unittests.cc +++ b/shell/platform/windows/client_wrapper/flutter_view_unittests.cc @@ -28,4 +28,12 @@ TEST(FlutterViewTest, HwndAccessPassesThrough) { EXPECT_EQ(view.GetNativeWindow(), reinterpret_cast(7)); } +TEST(FlutterViewTest, GetPlatformTaskRunner) { + testing::ScopedStubFlutterWindowsApi scoped_api_stub( + std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + FlutterView view(reinterpret_cast(2)); + EXPECT_NE(view.GetPlatformTaskRunner(), nullptr); +} + } // namespace flutter diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_task_runner.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_task_runner.h new file mode 100644 index 0000000000000..2756b585985e4 --- /dev/null +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_task_runner.h @@ -0,0 +1,55 @@ +// 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_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_TASK_RUNNER_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_TASK_RUNNER_H_ + +#include + +#include + +namespace flutter { + +// An interface for scheduling tasks on the associated thread. +class FlutterTaskRunner { + public: + typedef std::function VoidCallback; + explicit FlutterTaskRunner(FlutterDesktopTaskRunnerRef task_runner) + : task_runner_(task_runner) {} + + // Schedules the task for execution by the associated thread. + void PostTask(VoidCallback callback) { + if (RunsTasksOnCurrentThread()) { + callback(); + return; + } + + struct Captures { + VoidCallback callback; + }; + auto captures = new Captures(); + captures->callback = callback; + FlutterDesktopTaskRunnerPostTask( + task_runner_, + [](void* opaque) { + auto captures = reinterpret_cast(opaque); + captures->callback(); + delete captures; + }, + captures); + } + + // Returns `true` if the associated thread is the current thread. + bool RunsTasksOnCurrentThread() { + return FlutterDesktopTaskRunnerRunsTasksOnCurrentThread(task_runner_); + } + + private: + // Handle for interacting with the C API's task runner. + FlutterDesktopTaskRunnerRef task_runner_; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_CLIENT_WRAPPER_INCLUDE_FLUTTER_TASK_RUNNER_H_ diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_view.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_view.h index 52945676d7ef6..8f49f5c6a2111 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_view.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_view.h @@ -7,12 +7,17 @@ #include +#include "flutter_task_runner.h" + namespace flutter { // A view displaying Flutter content. class FlutterView { public: - explicit FlutterView(FlutterDesktopViewRef view) : view_(view) {} + explicit FlutterView(FlutterDesktopViewRef view) + : view_(view), + platform_task_runner_(std::make_unique( + FlutterDesktopViewGetTaskRunner(view))) {} virtual ~FlutterView() = default; @@ -30,9 +35,17 @@ class FlutterView { HWND GetNativeWindow() { return FlutterDesktopViewGetHWND(view_); } #endif + // Gets the platform task runner. + FlutterTaskRunner* GetPlatformTaskRunner() { + return platform_task_runner_.get(); + } + private: // Handle for interacting with the C API's view. FlutterDesktopViewRef view_ = nullptr; + + // The task runner for this view. + std::unique_ptr platform_task_runner_; }; } // namespace flutter diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc index ae3b048de6357..13fe15ecf7425 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc @@ -150,6 +150,28 @@ HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef controller) { return reinterpret_cast(-1); } +FlutterDesktopTaskRunnerRef FlutterDesktopViewGetTaskRunner( + FlutterDesktopViewRef view) { + // The stub ignores this, so just return an arbitrary non-zero value. + return reinterpret_cast(1); +} + +void FlutterDesktopTaskRunnerPostTask(FlutterDesktopTaskRunnerRef task_runner, + VoidCallback callback, + void* user_data) { + if (s_stub_implementation) { + return s_stub_implementation->TaskRunnerPostTask(callback, user_data); + } +} + +bool FlutterDesktopTaskRunnerRunsTasksOnCurrentThread( + FlutterDesktopTaskRunnerRef task_runner) { + if (s_stub_implementation) { + return s_stub_implementation->TaskRunnerRunsTasksOnCurrentThread(); + } + return true; +} + FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( FlutterDesktopPluginRegistrarRef controller) { // The stub ignores this, so just return an arbitrary non-zero value. diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index 81d00ac820e69..dcf10aa54c56a 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -71,6 +71,17 @@ class StubFlutterWindowsApi { // Called for FlutterDesktopViewGetHWND. virtual HWND ViewGetHWND() { return reinterpret_cast(1); } + // Called for FlutterDesktopViewGetTaskRunner. + virtual FlutterDesktopTaskRunnerRef ViewGetTaskRunner() { + return reinterpret_cast(1); + } + + // Called for FlutterDesktopTaskRunnerPostTask. + virtual void TaskRunnerPostTask(VoidCallback callback, void* user_data) {} + + // Called for FlutterDesktopTaskRunnerRunsTasksOnCurrentThread. + virtual bool TaskRunnerRunsTasksOnCurrentThread() { return true; } + // Called for FlutterDesktopPluginRegistrarRegisterTopLevelWindowProcDelegate. virtual void PluginRegistrarRegisterTopLevelWindowProcDelegate( FlutterDesktopWindowProcCallback delegate, diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 81bf1eb6a7a95..e67ce2f164a65 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -61,6 +61,18 @@ static FlutterDesktopTextureRegistrarRef HandleForTextureRegistrar( return reinterpret_cast(registrar); } +// Returns the task runner corresponding to the given opaque API handle. +static flutter::TaskRunner* TaskRunnerFromHandle( + FlutterDesktopTaskRunnerRef ref) { + return reinterpret_cast(ref); +} + +// Returns the opaque API handle for the given task runner instance. +static FlutterDesktopTaskRunnerRef HandleForTaskRunner( + flutter::TaskRunner* task_runner) { + return reinterpret_cast(task_runner); +} + void FlutterDesktopViewControllerDestroy( FlutterDesktopViewControllerRef controller) { delete controller; @@ -148,11 +160,28 @@ HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view) { } #endif +FlutterDesktopTaskRunnerRef FlutterDesktopViewGetTaskRunner( + FlutterDesktopViewRef view) { + return HandleForTaskRunner(ViewFromHandle(view)->GetEngine()->task_runner()); +} + FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( FlutterDesktopPluginRegistrarRef registrar) { return HandleForView(registrar->engine->view()); } +void FlutterDesktopTaskRunnerPostTask(FlutterDesktopTaskRunnerRef task_runner, + VoidCallback callback, + void* user_data) { + return TaskRunnerFromHandle(task_runner) + ->RunNowOrPostTask([user_data, callback]() { callback(user_data); }); +} + +bool FlutterDesktopTaskRunnerRunsTasksOnCurrentThread( + FlutterDesktopTaskRunnerRef task_runner) { + return TaskRunnerFromHandle(task_runner)->RunsTasksOnCurrentThread(); +} + void FlutterDesktopResyncOutputStreams() { FILE* unused; if (freopen_s(&unused, "CONOUT$", "w", stdout)) { diff --git a/shell/platform/windows/public/flutter_windows.h b/shell/platform/windows/public/flutter_windows.h index 0aeb6fdc158c9..f3b93dfae17a2 100644 --- a/shell/platform/windows/public/flutter_windows.h +++ b/shell/platform/windows/public/flutter_windows.h @@ -34,6 +34,10 @@ typedef struct FlutterDesktopView* FlutterDesktopViewRef; struct FlutterDesktopEngine; typedef struct FlutterDesktopEngine* FlutterDesktopEngineRef; +// Opaque reference to a Flutter platform task runner. +struct FlutterDesktopTaskRunner; +typedef struct FlutterDesktopTaskRunner* FlutterDesktopTaskRunnerRef; + // Properties for configuring a Flutter engine instance. typedef struct { // The path to the flutter_assets folder for the application to be run. @@ -105,8 +109,8 @@ FLUTTER_EXPORT void FlutterDesktopViewControllerDestroy( // Its lifetime is the same as the |controller|'s. FLUTTER_EXPORT FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine( FlutterDesktopViewControllerRef controller); -// Returns the view managed by the given controller. +// Returns the view managed by the given controller. FLUTTER_EXPORT FlutterDesktopViewRef FlutterDesktopViewControllerGetView(FlutterDesktopViewControllerRef controller); @@ -205,6 +209,24 @@ FlutterDesktopViewGetCoreApplicationView(FlutterDesktopViewRef view); FLUTTER_EXPORT HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view); #endif +// Returns the task runner associated with the view. +FLUTTER_EXPORT FlutterDesktopTaskRunnerRef +FlutterDesktopViewGetTaskRunner(FlutterDesktopViewRef view); + +// ========== Task Runner ========== +typedef void (*VoidCallback)(void* user_data); + +// Posts a task to the runner. +FLUTTER_EXPORT void FlutterDesktopTaskRunnerPostTask( + FlutterDesktopTaskRunnerRef task_runner, + VoidCallback callback, + void* user_data); + +// Gets a flag indicating whether the task runner executes tasks on the current +// thread. +FLUTTER_EXPORT bool FlutterDesktopTaskRunnerRunsTasksOnCurrentThread( + FlutterDesktopTaskRunnerRef task_runner); + // ========== Plugin Registrar (extensions) ========== // These are Windows-specific extensions to flutter_plugin_registrar.h