| Flutter Windows Embedder |

task_runner_window.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/shell/platform/windows/task_runner_window.h"
6
7 #include <timeapi.h>
8 #include <algorithm>
9 #include <chrono>
10
11 #include "flutter/fml/logging.h"
12
13 namespace flutter {
14
15 // Timer used for PollOnce timeout.
16 static const uintptr_t kPollTimeoutTimerId = 1;
17
18 TaskRunnerWindow::TaskRunnerWindow() {
19 WNDCLASS window_class = RegisterWindowClass();
20 window_handle_ =
21 CreateWindowEx(0, window_class.lpszClassName, L"", 0, 0, 0, 0, 0,
22 HWND_MESSAGE, nullptr, window_class.hInstance, nullptr);
23
24 timer_ = CreateThreadpoolTimer(TimerProc, this, nullptr);
25 if (!timer_) {
26 FML_LOG(ERROR) << "Failed to create threadpool timer, error: "
27 << GetLastError();
28 FML_CHECK(timer_);
29 }
30
31 if (window_handle_) {
32 SetWindowLongPtr(window_handle_, GWLP_USERDATA,
33 reinterpret_cast<LONG_PTR>(this));
34 } else {
35 auto error = GetLastError();
36 LPWSTR message = nullptr;
37 size_t size = FormatMessageW(
38 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
39 FORMAT_MESSAGE_IGNORE_INSERTS,
40 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
41 reinterpret_cast<LPWSTR>(&message), 0, NULL);
42 OutputDebugString(message);
43 LocalFree(message);
44 }
45
46 thread_id_ = GetCurrentThreadId();
47
48 // Increase timer precision for this process (the call only affects
49 // current process since Windows 10, version 2004).
50 timeBeginPeriod(1);
51 }
52
53 TaskRunnerWindow::~TaskRunnerWindow() {
54 SetThreadpoolTimer(timer_, nullptr, 0, 0);
55 // Ensures that no callbacks will run after CloseThreadpoolTimer.
56 // https://learn.microsoft.com/en-us/windows/win32/api/threadpoolapiset/nf-threadpoolapiset-closethreadpooltimer#remarks
57 WaitForThreadpoolTimerCallbacks(timer_, TRUE);
58 CloseThreadpoolTimer(timer_);
59
60 if (window_handle_) {
61 DestroyWindow(window_handle_);
62 window_handle_ = nullptr;
63 }
64 UnregisterClass(window_class_name_.c_str(), nullptr);
65
66 timeEndPeriod(1);
67 }
68
69 void TaskRunnerWindow::OnTimer() {
70 if (!PostMessage(window_handle_, WM_NULL, 0, 0)) {
71 FML_LOG(ERROR) << "Failed to post message to main thread.";
72 }
73 }
74
75 void TaskRunnerWindow::TimerProc(PTP_CALLBACK_INSTANCE instance,
76 PVOID context,
77 PTP_TIMER timer) {
78 reinterpret_cast<TaskRunnerWindow*>(context)->OnTimer();
79 }
80
81 std::shared_ptr<TaskRunnerWindow> TaskRunnerWindow::GetSharedInstance() {
82 static std::weak_ptr<TaskRunnerWindow> instance;
83 auto res = instance.lock();
84 if (!res) {
85 // can't use make_shared with private contructor
86 res.reset(new TaskRunnerWindow());
87 instance = res;
88 }
89 return res;
90 }
91
92 void TaskRunnerWindow::WakeUp() {
93 // When waking up from main thread while there are messages in the message
94 // queue use timer to post the WM_NULL message from background thread. This
95 // gives message loop chance to process input events before WM_NULL is
96 // processed - which is necessary because messages scheduled through
97 // PostMessage take precedence over input event messages. Otherwise await
98 // Future.delayed(Duration.zero) deadlocks the main thread. (See
99 // https://github.com/flutter/flutter/issues/173843)
100 if (thread_id_ == GetCurrentThreadId() && GetQueueStatus(QS_ALLEVENTS) != 0) {
101 SetTimer(std::chrono::nanoseconds::zero());
102 return;
103 }
104
105 if (!PostMessage(window_handle_, WM_NULL, 0, 0)) {
106 FML_LOG(ERROR) << "Failed to post message to main thread.";
107 }
108 }
109
110 void TaskRunnerWindow::AddDelegate(Delegate* delegate) {
111 delegates_.push_back(delegate);
112 SetTimer(std::chrono::nanoseconds::zero());
113 }
114
115 void TaskRunnerWindow::RemoveDelegate(Delegate* delegate) {
116 auto i = std::find(delegates_.begin(), delegates_.end(), delegate);
117 if (i != delegates_.end()) {
118 delegates_.erase(i);
119 }
120 }
121
122 void TaskRunnerWindow::PollOnce(std::chrono::milliseconds timeout) {
123 MSG msg;
124 ::SetTimer(window_handle_, kPollTimeoutTimerId, timeout.count(), nullptr);
125 if (GetMessage(&msg, window_handle_, 0, 0)) {
126 TranslateMessage(&msg);
127 DispatchMessage(&msg);
128 }
129 ::KillTimer(window_handle_, kPollTimeoutTimerId);
130 }
131
132 void TaskRunnerWindow::ProcessTasks() {
133 auto next = std::chrono::nanoseconds::max();
134 auto delegates_copy(delegates_);
135 for (auto delegate : delegates_copy) {
136 // if not removed in the meanwhile
137 if (std::find(delegates_.begin(), delegates_.end(), delegate) !=
138 delegates_.end()) {
139 next = std::min(next, delegate->ProcessTasks());
140 }
141 }
142 SetTimer(next);
143 }
144
145 void TaskRunnerWindow::SetTimer(std::chrono::nanoseconds when) {
146 if (when == std::chrono::nanoseconds::max()) {
147 SetThreadpoolTimer(timer_, nullptr, 0, 0);
148 } else {
149 auto microseconds =
150 std::chrono::duration_cast<std::chrono::microseconds>(when).count();
151 ULARGE_INTEGER ticks;
152 ticks.QuadPart = -static_cast<LONGLONG>(microseconds * 10);
153 FILETIME ft;
154 ft.dwLowDateTime = ticks.LowPart;
155 ft.dwHighDateTime = ticks.HighPart;
156 SetThreadpoolTimer(timer_, &ft, 0, 0);
157 }
158 }
159
160 WNDCLASS TaskRunnerWindow::RegisterWindowClass() {
161 window_class_name_ = L"FlutterTaskRunnerWindow";
162
163 WNDCLASS window_class{};
164 window_class.hCursor = nullptr;
165 window_class.lpszClassName = window_class_name_.c_str();
166 window_class.style = 0;
167 window_class.cbClsExtra = 0;
168 window_class.cbWndExtra = 0;
169 window_class.hInstance = GetModuleHandle(nullptr);
170 window_class.hIcon = nullptr;
171 window_class.hbrBackground = 0;
172 window_class.lpszMenuName = nullptr;
173 window_class.lpfnWndProc = WndProc;
174 RegisterClass(&window_class);
175 return window_class;
176 }
177
178 LRESULT
179 TaskRunnerWindow::HandleMessage(UINT const message,
180 WPARAM const wparam,
181 LPARAM const lparam) noexcept {
182 switch (message) {
183 case WM_NULL:
184 ProcessTasks();
185 return 0;
186 }
187 return DefWindowProcW(window_handle_, message, wparam, lparam);
188 }
189
190 LRESULT TaskRunnerWindow::WndProc(HWND const window,
191 UINT const message,
192 WPARAM const wparam,
193 LPARAM const lparam) noexcept {
194 if (auto* that = reinterpret_cast<TaskRunnerWindow*>(
195 GetWindowLongPtr(window, GWLP_USERDATA))) {
196 return that->HandleMessage(message, wparam, lparam);
197 } else {
198 return DefWindowProc(window, message, wparam, lparam);
199 }
200 }
201
202 } // namespace flutter
flutter::TaskRunnerWindow::Delegate
Definition: task_runner_window.h:22
flutter::TaskRunnerWindow
Definition: task_runner_window.h:20
flutter::TaskRunnerWindow::WakeUp
void WakeUp()
Definition: task_runner_window.cc:92
flutter::TaskRunnerWindow::PollOnce
void PollOnce(std::chrono::milliseconds timeout)
Definition: task_runner_window.cc:122
flutter::TaskRunnerWindow::GetSharedInstance
static std::shared_ptr< TaskRunnerWindow > GetSharedInstance()
Definition: task_runner_window.cc:81
flutter::TaskRunnerWindow::AddDelegate
void AddDelegate(Delegate *delegate)
Definition: task_runner_window.cc:110
flutter::TaskRunnerWindow::RemoveDelegate
void RemoveDelegate(Delegate *delegate)
Definition: task_runner_window.cc:115
flutter::TaskRunnerWindow::~TaskRunnerWindow
~TaskRunnerWindow()
Definition: task_runner_window.cc:53
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::kPollTimeoutTimerId
static const uintptr_t kPollTimeoutTimerId
Definition: task_runner_window.cc:16
task_runner_window.h