Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(402)

Side by Side Diff: ui/accelerated_widget_mac/window_resize_helper_mac.cc

Issue 1513053002: WIP - Gutterless resize on Windows Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: feedback Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 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 "ui/accelerated_widget_mac/window_resize_helper_mac.h"
6
7 #include <list>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/synchronization/lock.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/thread_restrictions.h"
15
16 namespace ui {
17 namespace {
18
19 class WrappedTask;
20 class PumpableTaskRunner;
21 typedef std::list<WrappedTask*> WrappedTaskQueue;
22 typedef base::Callback<void(base::WaitableEvent*, base::TimeDelta)>
23 EventTimedWaitCallback;
24
25 // A wrapper for IPCs and tasks that we may potentially execute in
26 // WaitForSingleTaskToRun. Because these tasks are sent to two places to run,
27 // we to wrap them in this structure and track whether or not they have run
28 // yet, to avoid running them twice.
29 class WrappedTask {
30 public:
31 WrappedTask(const base::Closure& closure, base::TimeDelta delay);
32 ~WrappedTask();
33 bool ShouldRunBefore(const WrappedTask& other);
34 void Run();
35 void AddToTaskRunnerQueue(PumpableTaskRunner* pumpable_task_runner);
36 void RemoveFromTaskRunnerQueue();
37 const base::TimeTicks& can_run_time() const { return can_run_time_; }
38
39 private:
40 base::Closure closure_;
41 base::TimeTicks can_run_time_;
42 bool has_run_;
43 uint64 sequence_number_;
44 WrappedTaskQueue::iterator iterator_;
45
46 // Back pointer to the pumpable task runner that this task is enqueued in.
47 scoped_refptr<PumpableTaskRunner> pumpable_task_runner_;
48
49 DISALLOW_COPY_AND_ASSIGN(WrappedTask);
50 };
51
52 // The PumpableTaskRunner is a task runner that will wrap tasks in an
53 // WrappedTask, enqueues that wrapped task in the queue to be pumped via
54 // WaitForSingleWrappedTaskToRun during resizes, and posts the task to a
55 // target task runner. The posted task will run only once, either through a
56 // WaitForSingleWrappedTaskToRun call or through the target task runner.
57 class PumpableTaskRunner : public base::SingleThreadTaskRunner {
58 public:
59 PumpableTaskRunner(
60 const EventTimedWaitCallback& event_timed_wait_callback,
61 const scoped_refptr<base::SingleThreadTaskRunner>& target_task_runner);
62
63 // Enqueue WrappedTask and post it to |target_task_runner_|.
64 bool EnqueueAndPostWrappedTask(const tracked_objects::Location& from_here,
65 WrappedTask* task,
66 base::TimeDelta delay);
67
68 // Wait at most |max_delay| to run an enqueued task.
69 bool WaitForSingleWrappedTaskToRun(const base::TimeDelta& max_delay);
70
71 // base::SingleThreadTaskRunner implementation:
72 bool PostDelayedTask(const tracked_objects::Location& from_here,
73 const base::Closure& task,
74 base::TimeDelta delay) override;
75
76 bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
77 const base::Closure& task,
78 base::TimeDelta delay) override;
79
80 bool RunsTasksOnCurrentThread() const override;
81
82 private:
83 friend class WrappedTask;
84
85 ~PumpableTaskRunner() override;
86
87 // A queue of live messages. Must hold |task_queue_lock_| to access. Tasks
88 // are added only on the IO thread and removed only on the UI thread. The
89 // WrappedTask objects are removed from the queue when they are run (by
90 // |target_task_runner_| or by a call to WaitForSingleWrappedTaskToRun
91 // removing them out of the queue, or by TaskRunner when it is destroyed).
92 WrappedTaskQueue task_queue_;
93 base::Lock task_queue_lock_;
94
95 // Event used to wake up the UI thread if it is sleeping in
96 // WaitForSingleTaskToRun.
97 base::WaitableEvent event_;
98
99 // Callback to call TimedWait on |event_| from an appropriate class.
100 EventTimedWaitCallback event_timed_wait_callback_;
101
102 scoped_refptr<base::SingleThreadTaskRunner> target_task_runner_;
103
104 DISALLOW_COPY_AND_ASSIGN(PumpableTaskRunner);
105 };
106
107 base::LazyInstance<WindowResizeHelperMac>::Leaky g_window_resize_helper =
108 LAZY_INSTANCE_INITIALIZER;
109
110 ////////////////////////////////////////////////////////////////////////////////
111 // WrappedTask
112
113 WrappedTask::WrappedTask(const base::Closure& closure, base::TimeDelta delay)
114 : closure_(closure),
115 can_run_time_(base::TimeTicks::Now() + delay),
116 has_run_(false),
117 sequence_number_(0) {}
118
119 WrappedTask::~WrappedTask() {
120 RemoveFromTaskRunnerQueue();
121 }
122
123 bool WrappedTask::ShouldRunBefore(const WrappedTask& other) {
124 if (can_run_time_ < other.can_run_time_)
125 return true;
126 if (can_run_time_ > other.can_run_time_)
127 return false;
128 if (sequence_number_ < other.sequence_number_)
129 return true;
130 if (sequence_number_ > other.sequence_number_)
131 return false;
132 // Sequence numbers are unique, so this should never happen.
133 NOTREACHED();
134 return false;
135 }
136
137 void WrappedTask::Run() {
138 if (has_run_)
139 return;
140 RemoveFromTaskRunnerQueue();
141 has_run_ = true;
142 closure_.Run();
143 }
144
145 void WrappedTask::AddToTaskRunnerQueue(
146 PumpableTaskRunner* pumpable_task_runner) {
147 pumpable_task_runner_ = pumpable_task_runner;
148 base::AutoLock lock(pumpable_task_runner_->task_queue_lock_);
149 static uint64 last_sequence_number = 0;
150 last_sequence_number += 1;
151 sequence_number_ = last_sequence_number;
152 iterator_ = pumpable_task_runner_->task_queue_.insert(
153 pumpable_task_runner_->task_queue_.end(), this);
154 }
155
156 void WrappedTask::RemoveFromTaskRunnerQueue() {
157 if (!pumpable_task_runner_.get())
158 return;
159 // The scope of the task runner's lock must be limited because removing
160 // this reference to the task runner may destroy it.
161 {
162 base::AutoLock lock(pumpable_task_runner_->task_queue_lock_);
163 pumpable_task_runner_->task_queue_.erase(iterator_);
164 iterator_ = pumpable_task_runner_->task_queue_.end();
165 }
166 pumpable_task_runner_ = NULL;
167 }
168
169 ////////////////////////////////////////////////////////////////////////////////
170 // PumpableTaskRunner
171
172 PumpableTaskRunner::PumpableTaskRunner(
173 const EventTimedWaitCallback& event_timed_wait_callback,
174 const scoped_refptr<base::SingleThreadTaskRunner>& target_task_runner)
175 : event_(false /* auto-reset */, false /* initially signalled */),
176 event_timed_wait_callback_(event_timed_wait_callback),
177 target_task_runner_(target_task_runner) {}
178
179 PumpableTaskRunner::~PumpableTaskRunner() {
180 // Because tasks hold a reference to the task runner, the task queue must
181 // be empty when it is destroyed.
182 DCHECK(task_queue_.empty());
183 }
184
185 bool PumpableTaskRunner::WaitForSingleWrappedTaskToRun(
186 const base::TimeDelta& max_delay) {
187 base::TimeTicks stop_waiting_time = base::TimeTicks::Now() + max_delay;
188
189 for (;;) {
190 base::TimeTicks current_time = base::TimeTicks::Now();
191 base::TimeTicks next_task_time = stop_waiting_time;
192
193 // Find the first task to execute in the list. This lookup takes O(n) time,
194 // but n is rarely more than 2, and has never been observed to be more than
195 // 12.
196 WrappedTask* task_to_execute = NULL;
197 {
198 base::AutoLock lock(task_queue_lock_);
199
200 for (WrappedTaskQueue::iterator it = task_queue_.begin();
201 it != task_queue_.end(); ++it) {
202 WrappedTask* potential_task = *it;
203
204 // If this task is scheduled for the future, take it into account when
205 // deciding how long to sleep, and continue on to the next task.
206 if (potential_task->can_run_time() > current_time) {
207 if (potential_task->can_run_time() < next_task_time)
208 next_task_time = potential_task->can_run_time();
209 continue;
210 }
211 // If there is a better candidate than this task, continue to the next
212 // task.
213 if (task_to_execute &&
214 task_to_execute->ShouldRunBefore(*potential_task)) {
215 continue;
216 }
217 task_to_execute = potential_task;
218 }
219 }
220
221 if (task_to_execute) {
222 task_to_execute->Run();
223 return true;
224 }
225
226 // Calculate how much time we have left before we have to stop waiting or
227 // until a currently-enqueued task will be ready to run.
228 base::TimeDelta max_sleep_time = next_task_time - current_time;
229 if (max_sleep_time <= base::TimeDelta::FromMilliseconds(0))
230 break;
231
232 event_timed_wait_callback_.Run(&event_, max_sleep_time);
233 }
234
235 return false;
236 }
237
238 bool PumpableTaskRunner::EnqueueAndPostWrappedTask(
239 const tracked_objects::Location& from_here,
240 WrappedTask* task,
241 base::TimeDelta delay) {
242 task->AddToTaskRunnerQueue(this);
243
244 // Notify anyone waiting on the UI thread that there is a new entry in the
245 // task map. If they don't find the entry they are looking for, then they
246 // will just continue waiting.
247 event_.Signal();
248
249 return target_task_runner_->PostDelayedTask(
250 from_here, base::Bind(&WrappedTask::Run, base::Owned(task)), delay);
251 }
252
253 ////////////////////////////////////////////////////////////////////////////////
254 // PumpableTaskRunner, base::SingleThreadTaskRunner implementation:
255
256 bool PumpableTaskRunner::PostDelayedTask(
257 const tracked_objects::Location& from_here,
258 const base::Closure& task,
259 base::TimeDelta delay) {
260 return EnqueueAndPostWrappedTask(from_here, new WrappedTask(task, delay),
261 delay);
262 }
263
264 bool PumpableTaskRunner::PostNonNestableDelayedTask(
265 const tracked_objects::Location& from_here,
266 const base::Closure& task,
267 base::TimeDelta delay) {
268 // The correctness of non-nestable events hasn't been proven for this
269 // structure.
270 NOTREACHED();
271 return false;
272 }
273
274 bool PumpableTaskRunner::RunsTasksOnCurrentThread() const {
275 return target_task_runner_->RunsTasksOnCurrentThread();
276 }
277
278 } // namespace
279
280 ////////////////////////////////////////////////////////////////////////////////
281 // WindowResizeHelperMac
282
283 scoped_refptr<base::SingleThreadTaskRunner> WindowResizeHelperMac::task_runner()
284 const {
285 return task_runner_;
286 }
287
288 // static
289 WindowResizeHelperMac* WindowResizeHelperMac::Get() {
290 return g_window_resize_helper.Pointer();
291 }
292
293 void WindowResizeHelperMac::Init(
294 const scoped_refptr<base::SingleThreadTaskRunner>& target_task_runner) {
295 DCHECK(!task_runner_);
296 task_runner_ = new PumpableTaskRunner(
297 base::Bind(&WindowResizeHelperMac::EventTimedWait), target_task_runner);
298 }
299
300 bool WindowResizeHelperMac::WaitForSingleTaskToRun(
301 const base::TimeDelta& max_delay) {
302 PumpableTaskRunner* pumpable_task_runner =
303 static_cast<PumpableTaskRunner*>(task_runner_.get());
304 if (!pumpable_task_runner)
305 return false;
306 return pumpable_task_runner->WaitForSingleWrappedTaskToRun(max_delay);
307 }
308
309 WindowResizeHelperMac::WindowResizeHelperMac() {}
310 WindowResizeHelperMac::~WindowResizeHelperMac() {}
311
312 void WindowResizeHelperMac::EventTimedWait(base::WaitableEvent* event,
313 base::TimeDelta delay) {
314 base::ThreadRestrictions::ScopedAllowWait allow_wait;
315 event->TimedWait(delay);
316 }
317
318 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698