Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Windows: Client Wrapper: Expose TaskRunner #31134

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/windows/client_wrapper/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <memory>
#include <string>

#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<TestWindowsApi>(false));
auto test_api = static_cast<TestWindowsApi*>(scoped_api_stub.stub());
FlutterTaskRunner task_runner(
reinterpret_cast<FlutterDesktopTaskRunnerRef>(2));

EXPECT_FALSE(task_runner.RunsTasksOnCurrentThread());
}

TEST(FlutterTaskRunnerTest, PostTaskInline) {
testing::ScopedStubFlutterWindowsApi scoped_api_stub(
std::make_unique<TestWindowsApi>(true));
auto test_api = static_cast<TestWindowsApi*>(scoped_api_stub.stub());
FlutterTaskRunner task_runner(
reinterpret_cast<FlutterDesktopTaskRunnerRef>(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<TestWindowsApi>(false));
auto test_api = static_cast<TestWindowsApi*>(scoped_api_stub.stub());
FlutterTaskRunner task_runner(
reinterpret_cast<FlutterDesktopTaskRunnerRef>(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
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,12 @@ TEST(FlutterViewTest, HwndAccessPassesThrough) {
EXPECT_EQ(view.GetNativeWindow(), reinterpret_cast<HWND>(7));
}

TEST(FlutterViewTest, GetPlatformTaskRunner) {
testing::ScopedStubFlutterWindowsApi scoped_api_stub(
std::make_unique<TestWindowsApi>());
auto test_api = static_cast<TestWindowsApi*>(scoped_api_stub.stub());
FlutterView view(reinterpret_cast<FlutterDesktopViewRef>(2));
EXPECT_NE(view.GetPlatformTaskRunner(), nullptr);
}

} // namespace flutter
Original file line number Diff line number Diff line change
@@ -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 <flutter_windows.h>

#include <functional>

namespace flutter {

// An interface for scheduling tasks on the associated thread.
class FlutterTaskRunner {
public:
typedef std::function<void()> 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<Captures*>(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_
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@

#include <flutter_windows.h>

#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<FlutterTaskRunner>(
FlutterDesktopViewGetTaskRunner(view))) {}

virtual ~FlutterView() = default;

Expand All @@ -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<FlutterTaskRunner> platform_task_runner_;
};

} // namespace flutter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,28 @@ HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef controller) {
return reinterpret_cast<HWND>(-1);
}

FlutterDesktopTaskRunnerRef FlutterDesktopViewGetTaskRunner(
FlutterDesktopViewRef view) {
// The stub ignores this, so just return an arbitrary non-zero value.
return reinterpret_cast<FlutterDesktopTaskRunnerRef>(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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ class StubFlutterWindowsApi {
// Called for FlutterDesktopViewGetHWND.
virtual HWND ViewGetHWND() { return reinterpret_cast<HWND>(1); }

// Called for FlutterDesktopViewGetTaskRunner.
virtual FlutterDesktopTaskRunnerRef ViewGetTaskRunner() {
return reinterpret_cast<FlutterDesktopTaskRunnerRef>(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,
Expand Down
29 changes: 29 additions & 0 deletions shell/platform/windows/flutter_windows.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ static FlutterDesktopTextureRegistrarRef HandleForTextureRegistrar(
return reinterpret_cast<FlutterDesktopTextureRegistrarRef>(registrar);
}

// Returns the task runner corresponding to the given opaque API handle.
static flutter::TaskRunner* TaskRunnerFromHandle(
FlutterDesktopTaskRunnerRef ref) {
return reinterpret_cast<flutter::TaskRunner*>(ref);
}

// Returns the opaque API handle for the given task runner instance.
static FlutterDesktopTaskRunnerRef HandleForTaskRunner(
flutter::TaskRunner* task_runner) {
return reinterpret_cast<FlutterDesktopTaskRunnerRef>(task_runner);
}

void FlutterDesktopViewControllerDestroy(
FlutterDesktopViewControllerRef controller) {
delete controller;
Expand Down Expand Up @@ -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)) {
Expand Down
24 changes: 23 additions & 1 deletion shell/platform/windows/public/flutter_windows.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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

Expand Down