Home | History | Annotate | Download | only in chrome_frame
      1 // Copyright (c) 2011 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 "chrome_frame/task_marshaller.h"
      6 
      7 #include "base/callback.h"
      8 #include "base/logging.h"
      9 
     10 TaskMarshallerThroughMessageQueue::TaskMarshallerThroughMessageQueue()
     11     : wnd_(NULL),
     12       msg_(0xFFFF) {
     13 }
     14 
     15 TaskMarshallerThroughMessageQueue::~TaskMarshallerThroughMessageQueue() {
     16   ClearTasks();
     17 }
     18 
     19 void TaskMarshallerThroughMessageQueue::PostTask(
     20     const tracked_objects::Location& from_here, const base::Closure& task) {
     21   DCHECK(wnd_ != NULL);
     22 
     23   lock_.Acquire();
     24   bool has_work = !pending_tasks_.empty();
     25   pending_tasks_.push(task);
     26   lock_.Release();
     27 
     28   // Don't post message if there is already one.
     29   if (has_work)
     30     return;
     31 
     32   if (!::PostMessage(wnd_, msg_, 0, 0)) {
     33     DVLOG(1) << "Dropping MSG_EXECUTE_TASK message for destroyed window.";
     34     ClearTasks();
     35   }
     36 }
     37 
     38 void TaskMarshallerThroughMessageQueue::PostDelayedTask(
     39     const tracked_objects::Location& source,
     40     const base::Closure& task,
     41     base::TimeDelta& delay) {
     42   DCHECK(wnd_);
     43 
     44   base::AutoLock lock(lock_);
     45   base::PendingTask delayed_task(source, task, base::TimeTicks::Now() + delay,
     46                                  true);
     47   base::TimeTicks top_run_time = delayed_tasks_.top().delayed_run_time;
     48   delayed_tasks_.push(delayed_task);
     49 
     50   // Reschedule the timer if |delayed_task| will be the next delayed task to
     51   // run.
     52   if (delayed_task.delayed_run_time < top_run_time) {
     53     ::SetTimer(wnd_, reinterpret_cast<UINT_PTR>(this),
     54                static_cast<DWORD>(delay.InMilliseconds()), NULL);
     55   }
     56 }
     57 
     58 BOOL TaskMarshallerThroughMessageQueue::ProcessWindowMessage(HWND hWnd,
     59                                                              UINT uMsg,
     60                                                              WPARAM wParam,
     61                                                              LPARAM lParam,
     62                                                              LRESULT& lResult,
     63                                                              DWORD dwMsgMapID) {
     64   if (hWnd == wnd_ && uMsg == msg_) {
     65     ExecuteQueuedTasks();
     66     lResult = 0;
     67     return TRUE;
     68   }
     69 
     70   if (hWnd == wnd_ && uMsg == WM_TIMER) {
     71     ExecuteDelayedTasks();
     72     lResult = 0;
     73     return TRUE;
     74   }
     75 
     76   return FALSE;
     77 }
     78 
     79 base::Closure TaskMarshallerThroughMessageQueue::PopTask() {
     80   base::AutoLock lock(lock_);
     81   if (pending_tasks_.empty())
     82     return base::Closure();
     83 
     84   base::Closure task = pending_tasks_.front();
     85   pending_tasks_.pop();
     86   return task;
     87 }
     88 
     89 void TaskMarshallerThroughMessageQueue::ExecuteQueuedTasks() {
     90   DCHECK(CalledOnValidThread());
     91   base::Closure task;
     92   while (!(task = PopTask()).is_null())
     93     task.Run();
     94 }
     95 
     96 void TaskMarshallerThroughMessageQueue::ExecuteDelayedTasks() {
     97   DCHECK(CalledOnValidThread());
     98   ::KillTimer(wnd_, reinterpret_cast<UINT_PTR>(this));
     99   while (true) {
    100     lock_.Acquire();
    101 
    102     if (delayed_tasks_.empty()) {
    103       lock_.Release();
    104       return;
    105     }
    106 
    107     base::PendingTask next_task = delayed_tasks_.top();
    108     base::TimeTicks now = base::TimeTicks::Now();
    109     base::TimeTicks next_run = next_task.delayed_run_time;
    110     if (next_run > now) {
    111       int64 delay = (next_run - now).InMillisecondsRoundedUp();
    112       ::SetTimer(wnd_, reinterpret_cast<UINT_PTR>(this),
    113                  static_cast<DWORD>(delay), NULL);
    114       lock_.Release();
    115       return;
    116     }
    117 
    118     delayed_tasks_.pop();
    119     lock_.Release();
    120 
    121     // Run the task outside the lock.
    122     next_task.task.Run();
    123   }
    124 }
    125 
    126 void TaskMarshallerThroughMessageQueue::ClearTasks() {
    127   base::AutoLock lock(lock_);
    128   DVLOG_IF(1, !pending_tasks_.empty()) << "Destroying "
    129                                        << pending_tasks_.size()
    130                                        << " pending tasks.";
    131   while (!pending_tasks_.empty())
    132     pending_tasks_.pop();
    133 
    134   while (!delayed_tasks_.empty())
    135     delayed_tasks_.pop();
    136 }
    137