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 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
196WNDCLASS 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
215TaskRunnerWindow::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
229LRESULT 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
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