| 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 #include <functional>
11 #include <mutex>
12 #include <thread>
13
14 #include "flutter/fml/logging.h"
15
16 namespace flutter {
17
18 TimerThread::TimerThread(std::function<void()> callback)
19 : callback_(std::move(callback)),
20 next_fire_time_(
21 std::chrono::time_point<std::chrono::high_resolution_clock>::max()) {}
22
23 void TimerThread::Start() {
24 FML_DCHECK(!thread_);
25 thread_ =
26 std::make_optional<std::thread>(&TimerThread::TimerThreadMain, this);
27 }
28
29 void TimerThread::Stop() {
30 if (!thread_) {
31 return;
32 }
33 {
34 std::lock_guard<std::mutex> lock(mutex_);
35 callback_ = nullptr;
36 }
37 cv_.notify_all();
38 thread_->join();
39 }
40
41 TimerThread::~TimerThread() {
42 // Ensure that Stop() has been called if Start() has been called.
43 FML_DCHECK(callback_ == nullptr || !thread_);
44 }
45
46 // Schedules the callback to be called at specified time point. If there is
47 // already a callback scheduled earlier than the specified time point, does
48 // nothing.
49 void TimerThread::ScheduleAt(
50 std::chrono::time_point<std::chrono::high_resolution_clock> time_point) {
51 std::lock_guard<std::mutex> lock(mutex_);
52 if (time_point < next_fire_time_) {
53 next_fire_time_ = time_point;
54 }
55 ++schedule_counter_;
56 cv_.notify_all();
57 }
58
59 void TimerThread::TimerThreadMain() {
60 std::unique_lock<std::mutex> lock(mutex_);
61 while (callback_ != nullptr) {
62 cv_.wait_until(lock, next_fire_time_, [this]() {
63 return std::chrono::high_resolution_clock::now() >= next_fire_time_ ||
64 callback_ == nullptr;
65 });
66 auto scheduled_count = schedule_counter_;
67 if (callback_) {
68 lock.unlock();
69 callback_();
70 lock.lock();
71 }
72 // If nothing was scheduled in the meanwhile park the timer.
73 if (scheduled_count == schedule_counter_ &&
74 next_fire_time_ <= std::chrono::high_resolution_clock::now()) {
75 next_fire_time_ =
76 std::chrono::time_point<std::chrono::high_resolution_clock>::max();
77 }
78 }
79 }
80
81 // Timer used for PollOnce timeout.
82 static const uintptr_t kPollTimeoutTimerId = 1;
83
84 TaskRunnerWindow::TaskRunnerWindow() : timer_thread_([this]() { OnTimer(); }) {
85 WNDCLASS window_class = RegisterWindowClass();
86 window_handle_ =
87 CreateWindowEx(0, window_class.lpszClassName, L"", 0, 0, 0, 0, 0,
88 HWND_MESSAGE, nullptr, window_class.hInstance, nullptr);
89
90 if (window_handle_) {
91 SetWindowLongPtr(window_handle_, GWLP_USERDATA,
92 reinterpret_cast<LONG_PTR>(this));
93 timer_thread_.Start();
94 } else {
95 auto error = GetLastError();
96 LPWSTR message = nullptr;
97 size_t size = FormatMessageW(
98 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
99 FORMAT_MESSAGE_IGNORE_INSERTS,
100 NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
101 reinterpret_cast<LPWSTR>(&message), 0, NULL);
102 OutputDebugString(message);
103 LocalFree(message);
104 }
105
106 thread_id_ = GetCurrentThreadId();
107 }
108
109 TaskRunnerWindow::~TaskRunnerWindow() {
110 timer_thread_.Stop();
111
112 if (window_handle_) {
113 DestroyWindow(window_handle_);
114 window_handle_ = nullptr;
115 }
116 UnregisterClass(window_class_name_.c_str(), nullptr);
117 }
118
119 void TaskRunnerWindow::OnTimer() {
120 WakeUp();
121 }
122
123 void TaskRunnerWindow::TimerProc(PTP_CALLBACK_INSTANCE instance,
124 PVOID context,
125 PTP_TIMER timer) {
126 reinterpret_cast<TaskRunnerWindow*>(context)->OnTimer();
127 }
128
129 std::shared_ptr<TaskRunnerWindow> TaskRunnerWindow::GetSharedInstance() {
130 static std::weak_ptr<TaskRunnerWindow> instance;
131 auto res = instance.lock();
132 if (!res) {
133 // can't use make_shared with private contructor
134 res.reset(new TaskRunnerWindow());
135 instance = res;
136 }
137 return res;
138 }
139
140 void TaskRunnerWindow::WakeUp() {
141 bool expected = false;
142 // Only post wake up message if needed otherwise the message queue will
143 // get flooded possibly resulting in application stopping to respond.
144 // https://github.com/flutter/flutter/issues/173843
145 if (wake_up_posted_.compare_exchange_strong(expected, true)) {
146 if (!PostMessage(window_handle_, WM_NULL, 0, 0)) {
147 FML_LOG(ERROR) << "Failed to post message to main thread.";
148 }
149 }
150 }
151
152 void TaskRunnerWindow::AddDelegate(Delegate* delegate) {
153 delegates_.push_back(delegate);
154 SetTimer(std::chrono::nanoseconds::zero());
155 }
156
157 void TaskRunnerWindow::RemoveDelegate(Delegate* delegate) {
158 auto i = std::find(delegates_.begin(), delegates_.end(), delegate);
159 if (i != delegates_.end()) {
160 delegates_.erase(i);
161 }
162 }
163
164 void TaskRunnerWindow::PollOnce(std::chrono::milliseconds timeout) {
165 MSG msg;
166 ::SetTimer(window_handle_, kPollTimeoutTimerId, timeout.count(), nullptr);
167 if (GetMessage(&msg, window_handle_, 0, 0)) {
168 TranslateMessage(&msg);
169 DispatchMessage(&msg);
170 }
171 ::KillTimer(window_handle_, kPollTimeoutTimerId);
172 }
173
174 void TaskRunnerWindow::ProcessTasks() {
175 auto next = std::chrono::nanoseconds::max();
176 auto delegates_copy(delegates_);
177 for (auto delegate : delegates_copy) {
178 // if not removed in the meanwhile
179 if (std::find(delegates_.begin(), delegates_.end(), delegate) !=
180 delegates_.end()) {
181 next = std::min(next, delegate->ProcessTasks());
182 }
183 }
184 SetTimer(next);
185 }
186
187 void TaskRunnerWindow::SetTimer(std::chrono::nanoseconds when) {
188 if (when == std::chrono::nanoseconds::max()) {
189 timer_thread_.ScheduleAt(
190 std::chrono::time_point<std::chrono::high_resolution_clock>::max());
191 } else {
192 timer_thread_.ScheduleAt(std::chrono::high_resolution_clock::now() + when);
193 }
194 }
195
196 WNDCLASS TaskRunnerWindow::RegisterWindowClass() {
197 window_class_name_ = L"FlutterTaskRunnerWindow";
198
199 WNDCLASS window_class{};
200 window_class.hCursor = nullptr;
201 window_class.lpszClassName = window_class_name_.c_str();
202 window_class.style = 0;
203 window_class.cbClsExtra = 0;
204 window_class.cbWndExtra = 0;
205 window_class.hInstance = GetModuleHandle(nullptr);
206 window_class.hIcon = nullptr;
207 window_class.hbrBackground = 0;
208 window_class.lpszMenuName = nullptr;
209 window_class.lpfnWndProc = WndProc;
210 RegisterClass(&window_class);
211 return window_class;
212 }
213
214 LRESULT
215 TaskRunnerWindow::HandleMessage(UINT const message,
216 WPARAM const wparam,
217 LPARAM const lparam) noexcept {
218 switch (message) {
219 case WM_NULL:
220 // After this point, WakeUp() needs to post new message to ensure
221 // that the wake-up request is not lost.
222 wake_up_posted_ = false;
223 ProcessTasks();
224 return 0;
225 }
226 return DefWindowProcW(window_handle_, message, wparam, lparam);
227 }
228
229 LRESULT TaskRunnerWindow::WndProc(HWND const window,
230 UINT const message,
231 WPARAM const wparam,
232 LPARAM const lparam) noexcept {
233 if (auto* that = reinterpret_cast<TaskRunnerWindow*>(
234 GetWindowLongPtr(window, GWLP_USERDATA))) {
235 return that->HandleMessage(message, wparam, lparam);
236 } else {
237 return DefWindowProc(window, message, wparam, lparam);
238 }
239 }
240
241 } // namespace flutter
flutter::TaskRunnerWindow::Delegate
Definition: task_runner_window.h:58
flutter::TaskRunnerWindow
Definition: task_runner_window.h:56
flutter::TimerThread::ScheduleAt
void ScheduleAt(std::chrono::time_point< std::chrono::high_resolution_clock > time_point)
Definition: task_runner_window.cc:49
flutter::TimerThread::TimerThread
TimerThread(std::function< void()> callback)
Definition: task_runner_window.cc:18
flutter::TimerThread::Stop
void Stop()
Definition: task_runner_window.cc:29
flutter::TimerThread::~TimerThread
~TimerThread()
Definition: task_runner_window.cc:41
flutter::TimerThread::Start
void Start()
Definition: task_runner_window.cc:23
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:52
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:82
task_runner_window.h