Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
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#include <functional>
11#include <mutex>
12#include <thread>
13
14#include "flutter/fml/logging.h"
15
16namespace flutter {
17
18TimerThread::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
24 FML_DCHECK(!thread_);
25 thread_ =
26 std::make_optional<std::thread>(&TimerThread::TimerThreadMain, this);
27}
28
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
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.
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
59void 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.
82static const uintptr_t kPollTimeoutTimerId = 1;
83
84TaskRunnerWindow::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
109TaskRunnerWindow::~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
119void TaskRunnerWindow::OnTimer() {
120 WakeUp();
121}
122
123void TaskRunnerWindow::TimerProc(PTP_CALLBACK_INSTANCE instance,
124 PVOID context,
125 PTP_TIMER timer) {
126 reinterpret_cast<TaskRunnerWindow*>(context)->OnTimer();
127}
128
129std::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
140void 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
152void TaskRunnerWindow::AddDelegate(Delegate* delegate) {
153 delegates_.push_back(delegate);
154 SetTimer(std::chrono::nanoseconds::zero());
155}
156
157void 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
164void 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
174void 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
187void TaskRunnerWindow::SetTimer(std::chrono::nanoseconds when) {
188 auto now = std::chrono::high_resolution_clock::now();
189 auto remaining_to_max =
190 std::chrono::nanoseconds::max() - now.time_since_epoch();
191 when = std::min(when, remaining_to_max);
192 timer_thread_.ScheduleAt(now + when);
193}
194
195WNDCLASS TaskRunnerWindow::RegisterWindowClass() {
196 window_class_name_ = L"FlutterTaskRunnerWindow";
197
198 WNDCLASS window_class{};
199 window_class.hCursor = nullptr;
200 window_class.lpszClassName = window_class_name_.c_str();
201 window_class.style = 0;
202 window_class.cbClsExtra = 0;
203 window_class.cbWndExtra = 0;
204 window_class.hInstance = GetModuleHandle(nullptr);
205 window_class.hIcon = nullptr;
206 window_class.hbrBackground = 0;
207 window_class.lpszMenuName = nullptr;
208 window_class.lpfnWndProc = WndProc;
209 RegisterClass(&window_class);
210 return window_class;
211}
212
214TaskRunnerWindow::HandleMessage(UINT const message,
215 WPARAM const wparam,
216 LPARAM const lparam) noexcept {
217 switch (message) {
218 case WM_NULL:
219 // After this point, WakeUp() needs to post new message to ensure
220 // that the wake-up request is not lost.
221 wake_up_posted_ = false;
222 ProcessTasks();
223 return 0;
224 }
225 return DefWindowProcW(window_handle_, message, wparam, lparam);
226}
227
228LRESULT TaskRunnerWindow::WndProc(HWND const window,
229 UINT const message,
230 WPARAM const wparam,
231 LPARAM const lparam) noexcept {
232 if (auto* that = reinterpret_cast<TaskRunnerWindow*>(
233 GetWindowLongPtr(window, GWLP_USERDATA))) {
234 return that->HandleMessage(message, wparam, lparam);
235 } else {
236 return DefWindowProc(window, message, wparam, lparam);
237 }
238}
239
240} // namespace flutter
void ScheduleAt(std::chrono::time_point< std::chrono::high_resolution_clock > time_point)
TimerThread(std::function< void()> callback)
GLFWwindow * window
Definition main.cc:60
VkInstance instance
Definition main.cc:64
const char * message
const uint8_t uint32_t uint32_t GError ** error
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
#define FML_DCHECK(condition)
Definition logging.h:122
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
Definition ref_ptr.h:261
struct tagMSG MSG
void * PVOID
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
__w64 long LONG_PTR
#define GetMessage
WINBASEAPI _Check_return_ _Post_equals_last_error_ DWORD WINAPI GetLastError(VOID)
UINT_PTR WPARAM
#define PostMessage
#define DispatchMessage