Flutter Engine
 
Loading...
Searching...
No Matches
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
6
7#include <timeapi.h>
8#include <algorithm>
9#include <chrono>
10
11#include "flutter/fml/logging.h"
12
13namespace flutter {
14
15// Timer used for PollOnce timeout.
16static const uintptr_t kPollTimeoutTimerId = 1;
17
18TaskRunnerWindow::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
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
69void 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
75void TaskRunnerWindow::TimerProc(PTP_CALLBACK_INSTANCE instance,
76 PVOID context,
77 PTP_TIMER timer) {
78 reinterpret_cast<TaskRunnerWindow*>(context)->OnTimer();
79}
80
81std::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
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
111 delegates_.push_back(delegate);
112 SetTimer(std::chrono::nanoseconds::zero());
113}
114
116 auto i = std::find(delegates_.begin(), delegates_.end(), delegate);
117 if (i != delegates_.end()) {
118 delegates_.erase(i);
119 }
120}
121
122void 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
132void 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
145void 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
160WNDCLASS 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
179TaskRunnerWindow::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
190LRESULT 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
void PollOnce(std::chrono::milliseconds timeout)
static std::shared_ptr< TaskRunnerWindow > GetSharedInstance()
void AddDelegate(Delegate *delegate)
void RemoveDelegate(Delegate *delegate)
GLFWwindow * window
Definition main.cc:60
VkInstance instance
Definition main.cc:64
return TRUE
G_BEGIN_DECLS GBytes * message
const uint8_t uint32_t uint32_t GError ** error
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
static const uintptr_t kPollTimeoutTimerId
struct tagMSG MSG
void * PVOID
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
__int64 LONGLONG
__w64 long LONG_PTR
#define GetMessage
struct _FILETIME FILETIME
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)
UINT_PTR WPARAM
#define PostMessage
#define DispatchMessage