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_mac.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.
28 WrappedTask(const base::Closure
& closure
, base::TimeDelta delay
);
30 bool ShouldRunBefore(const WrappedTask
& other
);
32 void AddToTaskRunnerQueue(PumpableTaskRunner
* pumpable_task_runner
);
33 void RemoveFromTaskRunnerQueue();
34 const base::TimeTicks
& can_run_time() const { return can_run_time_
; }
37 base::Closure closure_
;
38 base::TimeTicks can_run_time_
;
40 uint64 sequence_number_
;
41 WrappedTaskQueue::iterator iterator_
;
43 // Back pointer to the pumpable task runner that this task is enqueued in.
44 scoped_refptr
<PumpableTaskRunner
> pumpable_task_runner_
;
46 DISALLOW_COPY_AND_ASSIGN(WrappedTask
);
49 // The PumpableTaskRunner is a task runner that will wrap tasks in an
50 // WrappedTask, enqueues that wrapped task in the queue to be pumped via
51 // WaitForSingleWrappedTaskToRun during resizes, and posts the task to a
52 // target task runner. The posted task will run only once, either through a
53 // WaitForSingleWrappedTaskToRun call or through the target task runner.
54 class PumpableTaskRunner
: public base::SingleThreadTaskRunner
{
56 explicit PumpableTaskRunner(
57 const EventTimedWaitCallback
& event_timed_wait_callback
);
59 // Enqueue WrappedTask and post it to |target_task_runner_|.
60 bool EnqueueAndPostWrappedTask(const tracked_objects::Location
& from_here
,
62 base::TimeDelta delay
);
64 // Wait at most |max_delay| to run an enqueued task.
65 bool WaitForSingleWrappedTaskToRun(const base::TimeDelta
& max_delay
);
67 // Remove a wrapped task from the queue.
68 void RemoveWrappedTaskFromQueue(WrappedTask
* task
);
70 // base::SingleThreadTaskRunner implementation:
71 bool PostDelayedTask(const tracked_objects::Location
& from_here
,
72 const base::Closure
& task
,
73 base::TimeDelta delay
) override
;
75 bool PostNonNestableDelayedTask(const tracked_objects::Location
& from_here
,
76 const base::Closure
& task
,
77 base::TimeDelta delay
) override
;
79 bool RunsTasksOnCurrentThread() const override
;
82 friend class WrappedTask
;
84 ~PumpableTaskRunner() override
;
86 // A queue of live messages. Must hold |task_queue_lock_| to access. Tasks
87 // are added only on the IO thread and removed only on the UI thread. The
88 // WrappedTask objects are removed from the queue when they are run (by
89 // |target_task_runner_| or by a call to WaitForSingleWrappedTaskToRun
90 // removing them out of the queue, or by TaskRunner when it is destroyed).
91 WrappedTaskQueue task_queue_
;
92 base::Lock task_queue_lock_
;
94 // Event used to wake up the UI thread if it is sleeping in
95 // WaitForSingleTaskToRun.
96 base::WaitableEvent event_
;
98 // Callback to call TimedWait on |event_| from an appropriate class.
99 EventTimedWaitCallback event_timed_wait_callback_
;
101 scoped_refptr
<base::SingleThreadTaskRunner
> target_task_runner_
;
103 DISALLOW_COPY_AND_ASSIGN(PumpableTaskRunner
);
106 void HandleGpuIPC(int gpu_host_id
, const IPC::Message
& message
) {
107 GpuProcessHostUIShim
* host
= GpuProcessHostUIShim::FromID(gpu_host_id
);
109 host
->OnMessageReceived(message
);
112 void HandleRendererIPC(int render_process_id
, const IPC::Message
& message
) {
113 RenderProcessHost
* host
= RenderProcessHost::FromID(render_process_id
);
115 host
->OnMessageReceived(message
);
118 base::LazyInstance
<RenderWidgetResizeHelper
> g_render_widget_task_runner
=
119 LAZY_INSTANCE_INITIALIZER
;
121 ////////////////////////////////////////////////////////////////////////////////
124 WrappedTask::WrappedTask(const base::Closure
& closure
, base::TimeDelta delay
)
126 can_run_time_(base::TimeTicks::Now() + delay
),
128 sequence_number_(0) {}
130 WrappedTask::~WrappedTask() {
131 RemoveFromTaskRunnerQueue();
134 bool WrappedTask::ShouldRunBefore(const WrappedTask
& other
) {
135 if (can_run_time_
< other
.can_run_time_
)
137 if (can_run_time_
> other
.can_run_time_
)
139 if (sequence_number_
< other
.sequence_number_
)
141 if (sequence_number_
> other
.sequence_number_
)
143 // Sequence numbers are unique, so this should never happen.
148 void WrappedTask::Run() {
151 RemoveFromTaskRunnerQueue();
156 void WrappedTask::AddToTaskRunnerQueue(
157 PumpableTaskRunner
* pumpable_task_runner
) {
158 pumpable_task_runner_
= pumpable_task_runner
;
159 base::AutoLock
lock(pumpable_task_runner_
->task_queue_lock_
);
160 static uint64 last_sequence_number
= 0;
161 last_sequence_number
+= 1;
162 sequence_number_
= last_sequence_number
;
163 iterator_
= pumpable_task_runner_
->task_queue_
.insert(
164 pumpable_task_runner_
->task_queue_
.end(), this);
167 void WrappedTask::RemoveFromTaskRunnerQueue() {
168 if (!pumpable_task_runner_
.get())
170 // The scope of the task runner's lock must be limited because removing
171 // this reference to the task runner may destroy it.
173 base::AutoLock
lock(pumpable_task_runner_
->task_queue_lock_
);
174 pumpable_task_runner_
->task_queue_
.erase(iterator_
);
175 iterator_
= pumpable_task_runner_
->task_queue_
.end();
177 pumpable_task_runner_
= NULL
;
180 ////////////////////////////////////////////////////////////////////////////////
181 // PumpableTaskRunner
183 PumpableTaskRunner::PumpableTaskRunner(
184 const EventTimedWaitCallback
& event_timed_wait_callback
)
185 : event_(false /* auto-reset */, false /* initially signalled */),
186 event_timed_wait_callback_(event_timed_wait_callback
),
188 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI
)) {}
190 PumpableTaskRunner::~PumpableTaskRunner() {
191 // Because tasks hold a reference to the task runner, the task queue must
192 // be empty when it is destroyed.
193 DCHECK(task_queue_
.empty());
196 bool PumpableTaskRunner::WaitForSingleWrappedTaskToRun(
197 const base::TimeDelta
& max_delay
) {
198 base::TimeTicks stop_waiting_time
= base::TimeTicks::Now() + max_delay
;
201 base::TimeTicks current_time
= base::TimeTicks::Now();
202 base::TimeTicks next_task_time
= stop_waiting_time
;
204 // Find the first task to execute in the list. This lookup takes O(n) time,
205 // but n is rarely more than 2, and has never been observed to be more than
207 WrappedTask
* task_to_execute
= NULL
;
209 base::AutoLock
lock(task_queue_lock_
);
211 for (WrappedTaskQueue::iterator it
= task_queue_
.begin();
212 it
!= task_queue_
.end(); ++it
) {
213 WrappedTask
* potential_task
= *it
;
215 // If this task is scheduled for the future, take it into account when
216 // deciding how long to sleep, and continue on to the next task.
217 if (potential_task
->can_run_time() > current_time
) {
218 if (potential_task
->can_run_time() < next_task_time
)
219 next_task_time
= potential_task
->can_run_time();
222 // If there is a better candidate than this task, continue to the next
224 if (task_to_execute
&&
225 task_to_execute
->ShouldRunBefore(*potential_task
)) {
228 task_to_execute
= potential_task
;
232 if (task_to_execute
) {
233 task_to_execute
->Run();
237 // Calculate how much time we have left before we have to stop waiting or
238 // until a currently-enqueued task will be ready to run.
239 base::TimeDelta max_sleep_time
= next_task_time
- current_time
;
240 if (max_sleep_time
<= base::TimeDelta::FromMilliseconds(0))
243 event_timed_wait_callback_
.Run(&event_
, max_sleep_time
);
249 bool PumpableTaskRunner::EnqueueAndPostWrappedTask(
250 const tracked_objects::Location
& from_here
,
252 base::TimeDelta delay
) {
253 task
->AddToTaskRunnerQueue(this);
255 // Notify anyone waiting on the UI thread that there is a new entry in the
256 // task map. If they don't find the entry they are looking for, then they
257 // will just continue waiting.
260 return target_task_runner_
->PostDelayedTask(
261 from_here
, base::Bind(&WrappedTask::Run
, base::Owned(task
)), delay
);
264 ////////////////////////////////////////////////////////////////////////////////
265 // PumpableTaskRunner, base::SingleThreadTaskRunner implementation:
267 bool PumpableTaskRunner::PostDelayedTask(
268 const tracked_objects::Location
& from_here
,
269 const base::Closure
& task
,
270 base::TimeDelta delay
) {
271 return EnqueueAndPostWrappedTask(from_here
, new WrappedTask(task
, delay
),
275 bool PumpableTaskRunner::PostNonNestableDelayedTask(
276 const tracked_objects::Location
& from_here
,
277 const base::Closure
& task
,
278 base::TimeDelta delay
) {
279 // The correctness of non-nestable events hasn't been proven for this
285 bool PumpableTaskRunner::RunsTasksOnCurrentThread() const {
286 return target_task_runner_
->RunsTasksOnCurrentThread();
291 ////////////////////////////////////////////////////////////////////////////////
292 // RenderWidgetResizeHelper
294 scoped_refptr
<base::SingleThreadTaskRunner
>
295 RenderWidgetResizeHelper::task_runner() const {
300 RenderWidgetResizeHelper
* RenderWidgetResizeHelper::Get() {
301 return g_render_widget_task_runner
.Pointer();
304 bool RenderWidgetResizeHelper::WaitForSingleTaskToRun(
305 const base::TimeDelta
& max_delay
) {
306 PumpableTaskRunner
* pumpable_task_runner
=
307 reinterpret_cast<PumpableTaskRunner
*>(task_runner_
.get());
308 return pumpable_task_runner
->WaitForSingleWrappedTaskToRun(max_delay
);
311 void RenderWidgetResizeHelper::PostRendererProcessMsg(int render_process_id
,
312 const IPC::Message
& msg
) {
313 PumpableTaskRunner
* pumpable_task_runner
=
314 reinterpret_cast<PumpableTaskRunner
*>(task_runner_
.get());
315 pumpable_task_runner
->EnqueueAndPostWrappedTask(
317 new WrappedTask(base::Bind(HandleRendererIPC
, render_process_id
, msg
),
322 void RenderWidgetResizeHelper::PostGpuProcessMsg(int gpu_host_id
,
323 const IPC::Message
& msg
) {
324 PumpableTaskRunner
* pumpable_task_runner
=
325 reinterpret_cast<PumpableTaskRunner
*>(task_runner_
.get());
326 pumpable_task_runner
->EnqueueAndPostWrappedTask(
327 FROM_HERE
, new WrappedTask(base::Bind(HandleGpuIPC
, gpu_host_id
, msg
),
332 RenderWidgetResizeHelper::RenderWidgetResizeHelper() {
333 task_runner_
= new PumpableTaskRunner(base::Bind(&EventTimedWait
));
336 RenderWidgetResizeHelper::~RenderWidgetResizeHelper() {}
339 void RenderWidgetResizeHelper::EventTimedWait(base::WaitableEvent
* event
,
340 base::TimeDelta delay
) {
341 base::ThreadRestrictions::ScopedAllowWait allow_wait
;
342 event
->TimedWait(delay
);
345 } // namespace content