1 // Copyright 2019 The Chromium 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 "android_webview/browser/gfx/task_queue_web_view.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "android_webview/common/aw_features.h"
11 #include "base/auto_reset.h"
12 #include "base/bind.h"
13 #include "base/containers/queue.h"
14 #include "base/logging.h"
15 #include "base/no_destructor.h"
16 #include "base/synchronization/condition_variable.h"
17 #include "base/synchronization/lock.h"
18 #include "base/thread_annotations.h"
19 #include "base/threading/thread_checker.h"
20 #include "base/threading/thread_local.h"
21 #include "base/trace_event/trace_event.h"
22 #include "components/viz/common/features.h"
23
24 namespace android_webview {
25
26 namespace {
27
GetAllowGL()28 base::ThreadLocalBoolean* GetAllowGL() {
29 static base::NoDestructor<base::ThreadLocalBoolean> allow_gl;
30 return allow_gl.get();
31 }
32
33 // This task queue is used when the client and gpu service runs on the same
34 // thread (render thread). It has some simple logic to avoid reentrancy; in most
35 // cases calling schedule will actually run the task immediately.
36 class TaskQueueSingleThread : public TaskQueueWebView {
37 public:
38 TaskQueueSingleThread();
39 ~TaskQueueSingleThread() override = default;
40
41 // TaskQueueWebView overrides.
42 void ScheduleTask(base::OnceClosure task, bool out_of_order) override;
43 void ScheduleOrRetainTask(base::OnceClosure task) override;
44 void ScheduleIdleTask(base::OnceClosure task) override;
45 void ScheduleClientTask(base::OnceClosure task) override;
46 void RunAllTasks() override;
47 void InitializeVizThread(const scoped_refptr<base::SingleThreadTaskRunner>&
48 viz_task_runner) override;
49 void ScheduleOnVizAndBlock(VizTask viz_task) override;
50
51 private:
52 // Flush the idle queue until it is empty.
53 void PerformAllIdleWork();
54 void RunTasks();
55
56 // All access to task queue should happen on a single thread.
57 THREAD_CHECKER(task_queue_thread_checker_);
58 base::circular_deque<base::OnceClosure> tasks_;
59 base::queue<base::OnceClosure> idle_tasks_;
60 base::queue<base::OnceClosure> client_tasks_;
61
62 bool inside_run_tasks_ = false;
63 bool inside_run_idle_tasks_ = false;
64
65 DISALLOW_COPY_AND_ASSIGN(TaskQueueSingleThread);
66 };
67
TaskQueueSingleThread()68 TaskQueueSingleThread::TaskQueueSingleThread() {
69 DETACH_FROM_THREAD(task_queue_thread_checker_);
70 }
71
ScheduleTask(base::OnceClosure task,bool out_of_order)72 void TaskQueueSingleThread::ScheduleTask(base::OnceClosure task,
73 bool out_of_order) {
74 DCHECK_CALLED_ON_VALID_THREAD(task_queue_thread_checker_);
75 LOG_IF(FATAL, !GetAllowGL()->Get())
76 << "ScheduleTask outside of ScopedAllowGL";
77 if (out_of_order)
78 tasks_.emplace_front(std::move(task));
79 else
80 tasks_.emplace_back(std::move(task));
81 RunTasks();
82 }
83
ScheduleOrRetainTask(base::OnceClosure task)84 void TaskQueueSingleThread::ScheduleOrRetainTask(base::OnceClosure task) {
85 ScheduleTask(std::move(task), false);
86 }
87
ScheduleIdleTask(base::OnceClosure task)88 void TaskQueueSingleThread::ScheduleIdleTask(base::OnceClosure task) {
89 LOG_IF(FATAL, !GetAllowGL()->Get())
90 << "ScheduleDelayedWork outside of ScopedAllowGL";
91 DCHECK_CALLED_ON_VALID_THREAD(task_queue_thread_checker_);
92 idle_tasks_.push(std::move(task));
93 }
94
ScheduleClientTask(base::OnceClosure task)95 void TaskQueueSingleThread::ScheduleClientTask(base::OnceClosure task) {
96 DCHECK_CALLED_ON_VALID_THREAD(task_queue_thread_checker_);
97 client_tasks_.emplace(std::move(task));
98 }
99
RunTasks()100 void TaskQueueSingleThread::RunTasks() {
101 TRACE_EVENT0("android_webview", "TaskQueueSingleThread::RunTasks");
102 DCHECK_CALLED_ON_VALID_THREAD(task_queue_thread_checker_);
103 if (inside_run_tasks_)
104 return;
105 base::AutoReset<bool> inside(&inside_run_tasks_, true);
106 while (tasks_.size()) {
107 std::move(tasks_.front()).Run();
108 tasks_.pop_front();
109 }
110 }
111
RunAllTasks()112 void TaskQueueSingleThread::RunAllTasks() {
113 DCHECK_CALLED_ON_VALID_THREAD(task_queue_thread_checker_);
114 RunTasks();
115 PerformAllIdleWork();
116 DCHECK(tasks_.empty());
117 DCHECK(idle_tasks_.empty());
118
119 // Client tasks may generate more service tasks, so run this
120 // in a loop.
121 while (!client_tasks_.empty()) {
122 base::queue<base::OnceClosure> local_client_tasks;
123 local_client_tasks.swap(client_tasks_);
124 while (!local_client_tasks.empty()) {
125 std::move(local_client_tasks.front()).Run();
126 local_client_tasks.pop();
127 }
128
129 RunTasks();
130 PerformAllIdleWork();
131 DCHECK(tasks_.empty());
132 DCHECK(idle_tasks_.empty());
133 }
134 }
135
PerformAllIdleWork()136 void TaskQueueSingleThread::PerformAllIdleWork() {
137 TRACE_EVENT0("android_webview", "TaskQueueWebview::PerformAllIdleWork");
138 DCHECK_CALLED_ON_VALID_THREAD(task_queue_thread_checker_);
139 if (inside_run_idle_tasks_)
140 return;
141 base::AutoReset<bool> inside(&inside_run_idle_tasks_, true);
142 while (idle_tasks_.size() > 0) {
143 base::OnceClosure task = std::move(idle_tasks_.front());
144 idle_tasks_.pop();
145 std::move(task).Run();
146 }
147 }
148
InitializeVizThread(const scoped_refptr<base::SingleThreadTaskRunner> & viz_task_runner)149 void TaskQueueSingleThread::InitializeVizThread(
150 const scoped_refptr<base::SingleThreadTaskRunner>& viz_task_runner) {
151 NOTREACHED();
152 }
153
ScheduleOnVizAndBlock(VizTask viz_task)154 void TaskQueueSingleThread::ScheduleOnVizAndBlock(VizTask viz_task) {
155 NOTREACHED();
156 }
157
158 // This class is used with kVizForWebView. The client is the single viz
159 // thread and the gpu service runs on the render thread. Render thread is
160 // allowed to block on the viz thread, but not the other way around. This
161 // achieves viz scheduling tasks to gpu by first blocking render thread
162 // on the viz thread so render thread is ready to receive and run tasks.
163 //
164 // This class does not implement methods only needed by command buffer.
165 // It does not reply on ScopedAllowGL either.
166 class TaskQueueViz : public TaskQueueWebView {
167 public:
168 TaskQueueViz();
169 ~TaskQueueViz() override;
170
171 // TaskQueueWebView overrides.
172 void ScheduleTask(base::OnceClosure task, bool out_of_order) override;
173 void ScheduleOrRetainTask(base::OnceClosure task) override;
174 void ScheduleIdleTask(base::OnceClosure task) override;
175 void ScheduleClientTask(base::OnceClosure task) override;
176 void RunAllTasks() override;
177 void InitializeVizThread(const scoped_refptr<base::SingleThreadTaskRunner>&
178 viz_task_runner) override;
179 void ScheduleOnVizAndBlock(VizTask viz_task) override;
180
181 private:
182 void RunOnViz(VizTask viz_task);
183 void SignalDone();
184 void EmplaceTask(base::OnceClosure task);
185
186 scoped_refptr<base::SingleThreadTaskRunner> viz_task_runner_;
187 THREAD_CHECKER(render_thread_checker_);
188
189 // Only accessed on viz thread.
190 bool allow_schedule_task_ = false;
191
192 // Only accessed on render thread.
193 bool inside_schedule_on_viz_and_block_ = false;
194
195 base::Lock lock_;
196 base::ConditionVariable condvar_{&lock_};
197 bool done_ GUARDED_BY(lock_) = true;
198 base::circular_deque<base::OnceClosure> tasks_ GUARDED_BY(lock_);
199
200 DISALLOW_COPY_AND_ASSIGN(TaskQueueViz);
201 };
202
TaskQueueViz()203 TaskQueueViz::TaskQueueViz() {
204 DETACH_FROM_THREAD(render_thread_checker_);
205 }
206
207 TaskQueueViz::~TaskQueueViz() = default;
208
ScheduleTask(base::OnceClosure task,bool out_of_order)209 void TaskQueueViz::ScheduleTask(base::OnceClosure task, bool out_of_order) {
210 TRACE_EVENT0("android_webview", "ScheduleTask");
211 DCHECK(viz_task_runner_->BelongsToCurrentThread());
212 DCHECK(allow_schedule_task_);
213 // |out_of_order| is not needed by TaskForwardingSequence. Not supporting
214 // it allows slightly more efficient swapping the task queue in
215 // ScheduleOnVizAndBlock .
216 DCHECK(!out_of_order);
217 EmplaceTask(std::move(task));
218 }
219
ScheduleOrRetainTask(base::OnceClosure task)220 void TaskQueueViz::ScheduleOrRetainTask(base::OnceClosure task) {
221 DCHECK(viz_task_runner_->BelongsToCurrentThread());
222 // The two branches end up doing the exact same thing only because retain can
223 // use the same task queue. The code says the intention which is
224 // |ScheduleOrRetainTask| behaves the same as |ScheduleTask| if
225 // |allow_schedule_task_| is true.
226 // Sharing the queue makes it clear |ScheduleTask| and |ScheduleOrRetainTask|
227 // but however has a non-practical risk of live-locking the render thread.
228 if (allow_schedule_task_) {
229 ScheduleTask(std::move(task), false);
230 return;
231 }
232 EmplaceTask(std::move(task));
233 }
234
EmplaceTask(base::OnceClosure task)235 void TaskQueueViz::EmplaceTask(base::OnceClosure task) {
236 base::AutoLock lock(lock_);
237 tasks_.emplace_back(std::move(task));
238 condvar_.Signal();
239 }
240
ScheduleIdleTask(base::OnceClosure task)241 void TaskQueueViz::ScheduleIdleTask(base::OnceClosure task) {
242 DCHECK_CALLED_ON_VALID_THREAD(render_thread_checker_);
243 DCHECK(inside_schedule_on_viz_and_block_);
244 EmplaceTask(std::move(task));
245 }
246
ScheduleClientTask(base::OnceClosure task)247 void TaskQueueViz::ScheduleClientTask(base::OnceClosure task) {
248 DCHECK(viz_task_runner_);
249 viz_task_runner_->PostTask(FROM_HERE, std::move(task));
250 }
251
RunAllTasks()252 void TaskQueueViz::RunAllTasks() {
253 // Intentional no-op.
254 }
255
InitializeVizThread(const scoped_refptr<base::SingleThreadTaskRunner> & viz_task_runner)256 void TaskQueueViz::InitializeVizThread(
257 const scoped_refptr<base::SingleThreadTaskRunner>& viz_task_runner) {
258 DCHECK(!viz_task_runner_);
259 viz_task_runner_ = viz_task_runner;
260 }
261
ScheduleOnVizAndBlock(VizTask viz_task)262 void TaskQueueViz::ScheduleOnVizAndBlock(VizTask viz_task) {
263 TRACE_EVENT0("android_webview", "ScheduleOnVizAndBlock");
264 DCHECK_CALLED_ON_VALID_THREAD(render_thread_checker_);
265
266 // Expected behavior is |viz_task| on the viz thread. From |viz_task| until
267 // the done closure is called (which may not be in the viz_task), viz thread
268 // is allowed to call ScheduleTask.
269 //
270 // Implementation is uses a normal run-loop like logic. The done closure
271 // marks |done_| true, and run loop exists when |done_| is true *and* the task
272 // queue is empty. A condition variable is signaled when |done_| is set or
273 // when something is appended to the task queue.
274 {
275 base::AutoLock lock(lock_);
276 DCHECK(done_);
277 done_ = false;
278 }
279
280 // Unretained safe because this object is never deleted.
281 viz_task_runner_->PostTask(
282 FROM_HERE, base::BindOnce(&TaskQueueViz::RunOnViz, base::Unretained(this),
283 std::move(viz_task)));
284
285 {
286 DCHECK(!inside_schedule_on_viz_and_block_);
287 base::AutoReset<bool> inside_bf(&inside_schedule_on_viz_and_block_, true);
288
289 base::AutoLock lock(lock_);
290 while (!done_ || !tasks_.empty()) {
291 while (!done_ && tasks_.empty())
292 condvar_.Wait();
293 if (!tasks_.empty()) {
294 base::circular_deque<base::OnceClosure> tasks;
295 tasks.swap(tasks_);
296 {
297 base::AutoUnlock unlock(lock_);
298 TRACE_EVENT0("android_webview", "RunTasks");
299 while (!tasks.empty()) {
300 std::move(tasks.front()).Run();
301 tasks.pop_front();
302 }
303 }
304 }
305 }
306 DCHECK(done_);
307 }
308 }
309
RunOnViz(VizTask viz_task)310 void TaskQueueViz::RunOnViz(VizTask viz_task) {
311 DCHECK(viz_task_runner_->BelongsToCurrentThread());
312 DCHECK(!allow_schedule_task_);
313 allow_schedule_task_ = true;
314 // Unretained safe because this object is never deleted.
315 std::move(viz_task).Run(
316 base::BindOnce(&TaskQueueViz::SignalDone, base::Unretained(this)));
317 }
318
SignalDone()319 void TaskQueueViz::SignalDone() {
320 DCHECK(viz_task_runner_->BelongsToCurrentThread());
321 DCHECK(allow_schedule_task_);
322 allow_schedule_task_ = false;
323
324 base::AutoLock lock(lock_);
325 DCHECK(!done_);
326 done_ = true;
327 condvar_.Signal();
328 }
329
330 } // namespace
331
ScopedAllowGL()332 ScopedAllowGL::ScopedAllowGL() {
333 DCHECK(!GetAllowGL()->Get());
334 GetAllowGL()->Set(true);
335 }
336
~ScopedAllowGL()337 ScopedAllowGL::~ScopedAllowGL() {
338 TaskQueueWebView* service = TaskQueueWebView::GetInstance();
339 DCHECK(service);
340 service->RunAllTasks();
341 GetAllowGL()->Set(false);
342 }
343
344 // static
GetInstance()345 TaskQueueWebView* TaskQueueWebView::GetInstance() {
346 static TaskQueueWebView* task_queue =
347 ::features::IsUsingVizForWebView()
348 ? static_cast<TaskQueueWebView*>(new TaskQueueViz)
349 : static_cast<TaskQueueWebView*>(new TaskQueueSingleThread);
350 return task_queue;
351 }
352
353 } // namespace android_webview
354