1 // Copyright 2014 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.
5 #include "content/browser/renderer_host/render_widget_resize_helper.h"
9 #include "content/browser/gpu/gpu_process_host_ui_shim.h"
10 #include "content/browser/renderer_host/render_process_host_impl.h"
11 #include "content/public/browser/browser_thread.h"
17 class PumpableTaskRunner
;
18 typedef std::list
<WrappedTask
*> WrappedTaskQueue
;
19 typedef base::Callback
<void(base::WaitableEvent
*, base::TimeDelta
)>
20 EventTimedWaitCallback
;
22 // A wrapper for IPCs and tasks that we may potentially execute in
23 // WaitForSingleTaskToRun. Because these tasks are sent to two places to run,
24 // we to wrap them in this structure and track whether or not they have run
25 // yet, to avoid running them twice.
29 const base::Closure
& closure
,
30 base::TimeDelta delay
);
32 bool ShouldRunBefore(const WrappedTask
& other
);
34 void AddToTaskRunnerQueue(PumpableTaskRunner
* pumpable_task_runner
);
35 void RemoveFromTaskRunnerQueue();
36 const base::TimeTicks
& can_run_time() const { return can_run_time_
; }
39 base::Closure closure_
;
40 base::TimeTicks can_run_time_
;
42 uint64 sequence_number_
;
43 WrappedTaskQueue::iterator iterator_
;
45 // Back pointer to the pumpable task runner that this task is enqueued in.
46 scoped_refptr
<PumpableTaskRunner
> pumpable_task_runner_
;
48 DISALLOW_COPY_AND_ASSIGN(WrappedTask
);
51 // The PumpableTaskRunner is a task runner that will wrap tasks in an
52 // WrappedTask, enqueues that wrapped task in the queue to be pumped via
53 // WaitForSingleWrappedTaskToRun during resizes, and posts the task to a
54 // target task runner. The posted task will run only once, either through a
55 // WaitForSingleWrappedTaskToRun call or through the target task runner.
56 class PumpableTaskRunner
57 : public base::SingleThreadTaskRunner
{
59 explicit PumpableTaskRunner(
60 const EventTimedWaitCallback
& event_timed_wait_callback
);
62 // Enqueue WrappedTask and post it to |target_task_runner_|.
63 bool EnqueueAndPostWrappedTask(
64 const tracked_objects::Location
& from_here
,
66 base::TimeDelta delay
);
68 // Wait at most |max_delay| to run an enqueued task.
69 bool WaitForSingleWrappedTaskToRun(const base::TimeDelta
& max_delay
);
71 // Remove a wrapped task from the queue.
72 void RemoveWrappedTaskFromQueue(WrappedTask
* task
);
74 // base::SingleThreadTaskRunner implementation:
75 bool PostDelayedTask(const tracked_objects::Location
& from_here
,
76 const base::Closure
& task
,
77 base::TimeDelta delay
) override
;
79 bool PostNonNestableDelayedTask(const tracked_objects::Location
& from_here
,
80 const base::Closure
& task
,
81 base::TimeDelta delay
) override
;
83 bool RunsTasksOnCurrentThread() const override
;
86 friend class WrappedTask
;
88 ~PumpableTaskRunner() override
;
90 // A queue of live messages. Must hold |task_queue_lock_| to access. Tasks
91 // are added only on the IO thread and removed only on the UI thread. The
92 // WrappedTask objects are removed from the queue when they are run (by
93 // |target_task_runner_| or by a call to WaitForSingleWrappedTaskToRun
94 // removing them out of the queue, or by TaskRunner when it is destroyed).
95 WrappedTaskQueue task_queue_
;
96 base::Lock task_queue_lock_
;
98 // Event used to wake up the UI thread if it is sleeping in
99 // WaitForSingleTaskToRun.
100 base::WaitableEvent event_
;
102 // Callback to call TimedWait on |event_| from an appropriate class.
103 EventTimedWaitCallback event_timed_wait_callback_
;
105 scoped_refptr
<base::SingleThreadTaskRunner
> target_task_runner_
;
107 DISALLOW_COPY_AND_ASSIGN(PumpableTaskRunner
);
110 void HandleGpuIPC(int gpu_host_id
, const IPC::Message
& message
) {
111 GpuProcessHostUIShim
* host
= GpuProcessHostUIShim::FromID(gpu_host_id
);
113 host
->OnMessageReceived(message
);
116 void HandleRendererIPC(int render_process_id
, const IPC::Message
& message
) {
117 RenderProcessHost
* host
= RenderProcessHost::FromID(render_process_id
);
119 host
->OnMessageReceived(message
);
122 base::LazyInstance
<RenderWidgetResizeHelper
> g_render_widget_task_runner
=
123 LAZY_INSTANCE_INITIALIZER
;
125 ////////////////////////////////////////////////////////////////////////////////
128 WrappedTask::WrappedTask(
129 const base::Closure
& closure
,
130 base::TimeDelta delay
)
132 can_run_time_(base::TimeTicks::Now() + delay
),
134 sequence_number_(0) {
137 WrappedTask::~WrappedTask() {
138 RemoveFromTaskRunnerQueue();
141 bool WrappedTask::ShouldRunBefore(const WrappedTask
& other
) {
142 if (can_run_time_
< other
.can_run_time_
)
144 if (can_run_time_
> other
.can_run_time_
)
146 if (sequence_number_
< other
.sequence_number_
)
148 if (sequence_number_
> other
.sequence_number_
)
150 // Sequence numbers are unique, so this should never happen.
155 void WrappedTask::Run() {
158 RemoveFromTaskRunnerQueue();
163 void WrappedTask::AddToTaskRunnerQueue(
164 PumpableTaskRunner
* pumpable_task_runner
) {
165 pumpable_task_runner_
= pumpable_task_runner
;
166 base::AutoLock
lock(pumpable_task_runner_
->task_queue_lock_
);
167 static uint64 last_sequence_number
= 0;
168 last_sequence_number
+= 1;
169 sequence_number_
= last_sequence_number
;
170 iterator_
= pumpable_task_runner_
->task_queue_
.insert(
171 pumpable_task_runner_
->task_queue_
.end(), this);
174 void WrappedTask::RemoveFromTaskRunnerQueue() {
175 if (!pumpable_task_runner_
.get())
177 // The scope of the task runner's lock must be limited because removing
178 // this reference to the task runner may destroy it.
180 base::AutoLock
lock(pumpable_task_runner_
->task_queue_lock_
);
181 pumpable_task_runner_
->task_queue_
.erase(iterator_
);
182 iterator_
= pumpable_task_runner_
->task_queue_
.end();
184 pumpable_task_runner_
= NULL
;
187 ////////////////////////////////////////////////////////////////////////////////
188 // PumpableTaskRunner
190 PumpableTaskRunner::PumpableTaskRunner(
191 const EventTimedWaitCallback
& event_timed_wait_callback
)
192 : event_(false /* auto-reset */, false /* initially signalled */),
193 event_timed_wait_callback_(event_timed_wait_callback
),
194 target_task_runner_(BrowserThread::GetMessageLoopProxyForThread(
195 BrowserThread::UI
)) {}
197 PumpableTaskRunner::~PumpableTaskRunner() {
198 // Because tasks hold a reference to the task runner, the task queue must
199 // be empty when it is destroyed.
200 DCHECK(task_queue_
.empty());
203 bool PumpableTaskRunner::WaitForSingleWrappedTaskToRun(
204 const base::TimeDelta
& max_delay
) {
205 base::TimeTicks stop_waiting_time
= base::TimeTicks::Now() + max_delay
;
208 base::TimeTicks current_time
= base::TimeTicks::Now();
209 base::TimeTicks next_task_time
= stop_waiting_time
;
211 // Find the first task to execute in the list. This lookup takes O(n) time,
212 // but n is rarely more than 2, and has never been observed to be more than
214 WrappedTask
* task_to_execute
= NULL
;
216 base::AutoLock
lock(task_queue_lock_
);
218 for (WrappedTaskQueue::iterator it
= task_queue_
.begin(); it
!=
219 task_queue_
.end(); ++it
) {
220 WrappedTask
* potential_task
= *it
;
222 // If this task is scheduled for the future, take it into account when
223 // deciding how long to sleep, and continue on to the next task.
224 if (potential_task
->can_run_time() > current_time
) {
225 if (potential_task
->can_run_time() < next_task_time
)
226 next_task_time
= potential_task
->can_run_time();
229 // If there is a better candidate than this task, continue to the next
231 if (task_to_execute
&&
232 task_to_execute
->ShouldRunBefore(*potential_task
)) {
235 task_to_execute
= potential_task
;
239 if (task_to_execute
) {
240 task_to_execute
->Run();
244 // Calculate how much time we have left before we have to stop waiting or
245 // until a currently-enqueued task will be ready to run.
246 base::TimeDelta max_sleep_time
= next_task_time
- current_time
;
247 if (max_sleep_time
<= base::TimeDelta::FromMilliseconds(0))
250 event_timed_wait_callback_
.Run(&event_
, max_sleep_time
);
256 bool PumpableTaskRunner::EnqueueAndPostWrappedTask(
257 const tracked_objects::Location
& from_here
,
259 base::TimeDelta delay
) {
260 task
->AddToTaskRunnerQueue(this);
262 // Notify anyone waiting on the UI thread that there is a new entry in the
263 // task map. If they don't find the entry they are looking for, then they
264 // will just continue waiting.
267 return target_task_runner_
->PostDelayedTask(
268 from_here
, base::Bind(&WrappedTask::Run
, base::Owned(task
)), delay
);
271 ////////////////////////////////////////////////////////////////////////////////
272 // PumpableTaskRunner, base::SingleThreadTaskRunner implementation:
274 bool PumpableTaskRunner::PostDelayedTask(
275 const tracked_objects::Location
& from_here
,
276 const base::Closure
& task
,
277 base::TimeDelta delay
) {
278 return EnqueueAndPostWrappedTask(
280 new WrappedTask(task
, delay
),
284 bool PumpableTaskRunner::PostNonNestableDelayedTask(
285 const tracked_objects::Location
& from_here
,
286 const base::Closure
& task
,
287 base::TimeDelta delay
) {
288 // The correctness of non-nestable events hasn't been proven for this
294 bool PumpableTaskRunner::RunsTasksOnCurrentThread() const {
295 return target_task_runner_
->RunsTasksOnCurrentThread();
300 ////////////////////////////////////////////////////////////////////////////////
301 // RenderWidgetResizeHelper
303 scoped_refptr
<base::SingleThreadTaskRunner
>
304 RenderWidgetResizeHelper::task_runner() const {
309 RenderWidgetResizeHelper
* RenderWidgetResizeHelper::Get() {
310 return g_render_widget_task_runner
.Pointer();
313 bool RenderWidgetResizeHelper::WaitForSingleTaskToRun(
314 const base::TimeDelta
& max_delay
) {
315 PumpableTaskRunner
* pumpable_task_runner
=
316 reinterpret_cast<PumpableTaskRunner
*>(task_runner_
.get());
317 return pumpable_task_runner
->WaitForSingleWrappedTaskToRun(max_delay
);
320 void RenderWidgetResizeHelper::PostRendererProcessMsg(
321 int render_process_id
, const IPC::Message
& msg
) {
322 PumpableTaskRunner
* pumpable_task_runner
=
323 reinterpret_cast<PumpableTaskRunner
*>(task_runner_
.get());
324 pumpable_task_runner
->EnqueueAndPostWrappedTask(
326 new WrappedTask(base::Bind(HandleRendererIPC
, render_process_id
, msg
),
331 void RenderWidgetResizeHelper::PostGpuProcessMsg(
332 int gpu_host_id
, const IPC::Message
& msg
) {
333 PumpableTaskRunner
* pumpable_task_runner
=
334 reinterpret_cast<PumpableTaskRunner
*>(task_runner_
.get());
335 pumpable_task_runner
->EnqueueAndPostWrappedTask(
337 new WrappedTask(base::Bind(HandleGpuIPC
, gpu_host_id
, msg
),
342 RenderWidgetResizeHelper::RenderWidgetResizeHelper() {
343 task_runner_
= new PumpableTaskRunner(base::Bind(&EventTimedWait
));
346 RenderWidgetResizeHelper::~RenderWidgetResizeHelper() {}
349 void RenderWidgetResizeHelper::EventTimedWait(
350 base::WaitableEvent
* event
, base::TimeDelta delay
) {
351 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
352 event
->TimedWait(delay
);
355 } // namespace content