Home | History | Annotate | Download | only in renderer_host
      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.
      4 
      5 #include "content/browser/renderer_host/render_widget_resize_helper.h"
      6 
      7 #include <list>
      8 
      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"
     12 
     13 namespace content {
     14 namespace {
     15 
     16 class WrappedTask;
     17 class PumpableTaskRunner;
     18 typedef std::list<WrappedTask*> WrappedTaskQueue;
     19 typedef base::Callback<void(base::WaitableEvent*, base::TimeDelta)>
     20     EventTimedWaitCallback;
     21 
     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.
     26 class WrappedTask {
     27  public:
     28   WrappedTask(
     29       const base::Closure& closure,
     30       base::TimeDelta delay);
     31   ~WrappedTask();
     32   bool ShouldRunBefore(const WrappedTask& other);
     33   void Run();
     34   void AddToTaskRunnerQueue(PumpableTaskRunner* pumpable_task_runner);
     35   void RemoveFromTaskRunnerQueue();
     36   const base::TimeTicks& can_run_time() const { return can_run_time_; }
     37 
     38  private:
     39   base::Closure closure_;
     40   base::TimeTicks can_run_time_;
     41   bool has_run_;
     42   uint64 sequence_number_;
     43   WrappedTaskQueue::iterator iterator_;
     44 
     45   // Back pointer to the pumpable task runner that this task is enqueued in.
     46   scoped_refptr<PumpableTaskRunner> pumpable_task_runner_;
     47 
     48   DISALLOW_COPY_AND_ASSIGN(WrappedTask);
     49 };
     50 
     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 {
     58  public:
     59   explicit PumpableTaskRunner(
     60       const EventTimedWaitCallback& event_timed_wait_callback);
     61 
     62   // Enqueue WrappedTask and post it to |target_task_runner_|.
     63   bool EnqueueAndPostWrappedTask(
     64       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   // Remove a wrapped task from the queue.
     72   void RemoveWrappedTaskFromQueue(WrappedTask* task);
     73 
     74   // base::SingleThreadTaskRunner implementation:
     75   virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
     76                                const base::Closure& task,
     77                                base::TimeDelta delay) OVERRIDE;
     78 
     79   virtual bool PostNonNestableDelayedTask(
     80       const tracked_objects::Location& from_here,
     81       const base::Closure& task,
     82       base::TimeDelta delay) OVERRIDE;
     83 
     84   virtual bool RunsTasksOnCurrentThread() const OVERRIDE;
     85 
     86  private:
     87   friend class WrappedTask;
     88 
     89   virtual ~PumpableTaskRunner();
     90 
     91   // A queue of live messages.  Must hold |task_queue_lock_| to access. Tasks
     92   // are added only on the IO thread and removed only on the UI thread.  The
     93   // WrappedTask objects are removed from the queue when they are run (by
     94   // |target_task_runner_| or by a call to WaitForSingleWrappedTaskToRun
     95   // removing them out of the queue, or by TaskRunner when it is destroyed).
     96   WrappedTaskQueue task_queue_;
     97   base::Lock task_queue_lock_;
     98 
     99   // Event used to wake up the UI thread if it is sleeping in
    100   // WaitForSingleTaskToRun.
    101   base::WaitableEvent event_;
    102 
    103   // Callback to call TimedWait on |event_| from an appropriate class.
    104   EventTimedWaitCallback event_timed_wait_callback_;
    105 
    106   scoped_refptr<base::SingleThreadTaskRunner> target_task_runner_;
    107 
    108   DISALLOW_COPY_AND_ASSIGN(PumpableTaskRunner);
    109 };
    110 
    111 void HandleGpuIPC(int gpu_host_id, const IPC::Message& message) {
    112   GpuProcessHostUIShim* host = GpuProcessHostUIShim::FromID(gpu_host_id);
    113   if (host)
    114     host->OnMessageReceived(message);
    115 }
    116 
    117 void HandleRendererIPC(int render_process_id, const IPC::Message& message) {
    118   RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
    119   if (host)
    120     host->OnMessageReceived(message);
    121 }
    122 
    123 base::LazyInstance<RenderWidgetResizeHelper> g_render_widget_task_runner =
    124     LAZY_INSTANCE_INITIALIZER;
    125 
    126 ////////////////////////////////////////////////////////////////////////////////
    127 // WrappedTask
    128 
    129 WrappedTask::WrappedTask(
    130     const base::Closure& closure,
    131     base::TimeDelta delay)
    132     : closure_(closure),
    133       can_run_time_(base::TimeTicks::Now() + delay),
    134       has_run_(false),
    135       sequence_number_(0) {
    136 }
    137 
    138 WrappedTask::~WrappedTask() {
    139   RemoveFromTaskRunnerQueue();
    140 }
    141 
    142 bool WrappedTask::ShouldRunBefore(const WrappedTask& other) {
    143   if (can_run_time_ < other.can_run_time_)
    144     return true;
    145   if (can_run_time_ > other.can_run_time_)
    146     return false;
    147   if (sequence_number_ < other.sequence_number_)
    148     return true;
    149   if (sequence_number_ > other.sequence_number_)
    150     return false;
    151   // Sequence numbers are unique, so this should never happen.
    152   NOTREACHED();
    153   return false;
    154 }
    155 
    156 void WrappedTask::Run() {
    157   if (has_run_)
    158     return;
    159   RemoveFromTaskRunnerQueue();
    160   has_run_ = true;
    161   closure_.Run();
    162 }
    163 
    164 void WrappedTask::AddToTaskRunnerQueue(
    165     PumpableTaskRunner* pumpable_task_runner) {
    166   pumpable_task_runner_ = pumpable_task_runner;
    167   base::AutoLock lock(pumpable_task_runner_->task_queue_lock_);
    168   static uint64 last_sequence_number = 0;
    169   last_sequence_number += 1;
    170   sequence_number_ = last_sequence_number;
    171   iterator_ = pumpable_task_runner_->task_queue_.insert(
    172       pumpable_task_runner_->task_queue_.end(), this);
    173 }
    174 
    175 void WrappedTask::RemoveFromTaskRunnerQueue() {
    176   if (!pumpable_task_runner_.get())
    177     return;
    178   // The scope of the task runner's lock must be limited because removing
    179   // this reference to the task runner may destroy it.
    180   {
    181     base::AutoLock lock(pumpable_task_runner_->task_queue_lock_);
    182     pumpable_task_runner_->task_queue_.erase(iterator_);
    183     iterator_ = pumpable_task_runner_->task_queue_.end();
    184   }
    185   pumpable_task_runner_ = NULL;
    186 }
    187 
    188 ////////////////////////////////////////////////////////////////////////////////
    189 // PumpableTaskRunner
    190 
    191 PumpableTaskRunner::PumpableTaskRunner(
    192     const EventTimedWaitCallback& event_timed_wait_callback)
    193         : event_(false /* auto-reset */, false /* initially signalled */),
    194           event_timed_wait_callback_(event_timed_wait_callback),
    195           target_task_runner_(BrowserThread::GetMessageLoopProxyForThread(
    196               BrowserThread::UI)) {}
    197 
    198 PumpableTaskRunner::~PumpableTaskRunner() {
    199   // Because tasks hold a reference to the task runner, the task queue must
    200   // be empty when it is destroyed.
    201   DCHECK(task_queue_.empty());
    202 }
    203 
    204 bool PumpableTaskRunner::WaitForSingleWrappedTaskToRun(
    205     const base::TimeDelta& max_delay) {
    206   base::TimeTicks stop_waiting_time = base::TimeTicks::Now() + max_delay;
    207 
    208   for (;;) {
    209     base::TimeTicks current_time = base::TimeTicks::Now();
    210     base::TimeTicks next_task_time = stop_waiting_time;
    211 
    212     // Find the first task to execute in the list. This lookup takes O(n) time,
    213     // but n is rarely more than 2, and has never been observed to be more than
    214     // 12.
    215     WrappedTask* task_to_execute = NULL;
    216     {
    217       base::AutoLock lock(task_queue_lock_);
    218 
    219       for (WrappedTaskQueue::iterator it = task_queue_.begin(); it !=
    220                task_queue_.end(); ++it) {
    221         WrappedTask* potential_task = *it;
    222 
    223         // If this task is scheduled for the future, take it into account when
    224         // deciding how long to sleep, and continue on to the next task.
    225         if (potential_task->can_run_time() > current_time) {
    226           if (potential_task->can_run_time() < next_task_time)
    227             next_task_time = potential_task->can_run_time();
    228           continue;
    229         }
    230         // If there is a better candidate than this task, continue to the next
    231         // task.
    232         if (task_to_execute &&
    233             task_to_execute->ShouldRunBefore(*potential_task)) {
    234             continue;
    235         }
    236         task_to_execute = potential_task;
    237       }
    238     }
    239 
    240     if (task_to_execute) {
    241       task_to_execute->Run();
    242       return true;
    243     }
    244 
    245     // Calculate how much time we have left before we have to stop waiting or
    246     // until a currently-enqueued task will be ready to run.
    247     base::TimeDelta max_sleep_time = next_task_time - current_time;
    248     if (max_sleep_time <= base::TimeDelta::FromMilliseconds(0))
    249       break;
    250 
    251     event_timed_wait_callback_.Run(&event_, max_sleep_time);
    252   }
    253 
    254   return false;
    255 }
    256 
    257 bool PumpableTaskRunner::EnqueueAndPostWrappedTask(
    258     const tracked_objects::Location& from_here,
    259     WrappedTask* task,
    260     base::TimeDelta delay) {
    261   task->AddToTaskRunnerQueue(this);
    262 
    263   // Notify anyone waiting on the UI thread that there is a new entry in the
    264   // task map.  If they don't find the entry they are looking for, then they
    265   // will just continue waiting.
    266   event_.Signal();
    267 
    268   return target_task_runner_->PostDelayedTask(
    269         from_here, base::Bind(&WrappedTask::Run, base::Owned(task)), delay);
    270 }
    271 
    272 ////////////////////////////////////////////////////////////////////////////////
    273 // PumpableTaskRunner, base::SingleThreadTaskRunner implementation:
    274 
    275 bool PumpableTaskRunner::PostDelayedTask(
    276     const tracked_objects::Location& from_here,
    277     const base::Closure& task,
    278     base::TimeDelta delay) {
    279   return EnqueueAndPostWrappedTask(
    280       from_here,
    281       new WrappedTask(task, delay),
    282       delay);
    283 }
    284 
    285 bool PumpableTaskRunner::PostNonNestableDelayedTask(
    286     const tracked_objects::Location& from_here,
    287     const base::Closure& task,
    288     base::TimeDelta delay) {
    289   // The correctness of non-nestable events hasn't been proven for this
    290   // structure.
    291   NOTREACHED();
    292   return false;
    293 }
    294 
    295 bool PumpableTaskRunner::RunsTasksOnCurrentThread() const {
    296   return target_task_runner_->RunsTasksOnCurrentThread();
    297 }
    298 
    299 }  // namespace
    300 
    301 ////////////////////////////////////////////////////////////////////////////////
    302 // RenderWidgetResizeHelper
    303 
    304 scoped_refptr<base::SingleThreadTaskRunner>
    305     RenderWidgetResizeHelper::task_runner() const {
    306   return task_runner_;
    307 }
    308 
    309 // static
    310 RenderWidgetResizeHelper* RenderWidgetResizeHelper::Get() {
    311   return g_render_widget_task_runner.Pointer();
    312 }
    313 
    314 bool RenderWidgetResizeHelper::WaitForSingleTaskToRun(
    315     const base::TimeDelta& max_delay) {
    316   PumpableTaskRunner* pumpable_task_runner =
    317       reinterpret_cast<PumpableTaskRunner*>(task_runner_.get());
    318   return pumpable_task_runner->WaitForSingleWrappedTaskToRun(max_delay);
    319 }
    320 
    321 void RenderWidgetResizeHelper::PostRendererProcessMsg(
    322     int render_process_id, const IPC::Message& msg) {
    323   PumpableTaskRunner* pumpable_task_runner =
    324       reinterpret_cast<PumpableTaskRunner*>(task_runner_.get());
    325   pumpable_task_runner->EnqueueAndPostWrappedTask(
    326       FROM_HERE,
    327       new WrappedTask(base::Bind(HandleRendererIPC, render_process_id, msg),
    328                        base::TimeDelta()),
    329       base::TimeDelta());
    330 }
    331 
    332 void RenderWidgetResizeHelper::PostGpuProcessMsg(
    333     int gpu_host_id, const IPC::Message& msg) {
    334   PumpableTaskRunner* pumpable_task_runner =
    335       reinterpret_cast<PumpableTaskRunner*>(task_runner_.get());
    336   pumpable_task_runner->EnqueueAndPostWrappedTask(
    337       FROM_HERE,
    338       new WrappedTask(base::Bind(HandleGpuIPC, gpu_host_id, msg),
    339                       base::TimeDelta()),
    340       base::TimeDelta());
    341 }
    342 
    343 RenderWidgetResizeHelper::RenderWidgetResizeHelper() {
    344   task_runner_ = new PumpableTaskRunner(base::Bind(&EventTimedWait));
    345 }
    346 
    347 RenderWidgetResizeHelper::~RenderWidgetResizeHelper() {}
    348 
    349 // static
    350 void RenderWidgetResizeHelper::EventTimedWait(
    351     base::WaitableEvent* event, base::TimeDelta delay) {
    352   base::ThreadRestrictions::ScopedAllowWait allow_wait;
    353   event->TimedWait(delay);
    354 }
    355 
    356 }  // namespace content
    357 
    358