| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome_frame/task_marshaller.h" |
| |
| #include "base/callback.h" |
| #include "base/logging.h" |
| |
| TaskMarshallerThroughMessageQueue::TaskMarshallerThroughMessageQueue() |
| : wnd_(NULL), |
| msg_(0xFFFF) { |
| } |
| |
| TaskMarshallerThroughMessageQueue::~TaskMarshallerThroughMessageQueue() { |
| ClearTasks(); |
| } |
| |
| void TaskMarshallerThroughMessageQueue::PostTask( |
| const tracked_objects::Location& from_here, const base::Closure& task) { |
| DCHECK(wnd_ != NULL); |
| |
| lock_.Acquire(); |
| bool has_work = !pending_tasks_.empty(); |
| pending_tasks_.push(task); |
| lock_.Release(); |
| |
| // Don't post message if there is already one. |
| if (has_work) |
| return; |
| |
| if (!::PostMessage(wnd_, msg_, 0, 0)) { |
| DVLOG(1) << "Dropping MSG_EXECUTE_TASK message for destroyed window."; |
| ClearTasks(); |
| } |
| } |
| |
| void TaskMarshallerThroughMessageQueue::PostDelayedTask( |
| const tracked_objects::Location& source, |
| const base::Closure& task, |
| base::TimeDelta& delay) { |
| DCHECK(wnd_); |
| |
| base::AutoLock lock(lock_); |
| base::PendingTask delayed_task(source, task, base::TimeTicks::Now() + delay, |
| true); |
| base::TimeTicks top_run_time = delayed_tasks_.top().delayed_run_time; |
| delayed_tasks_.push(delayed_task); |
| |
| // Reschedule the timer if |delayed_task| will be the next delayed task to |
| // run. |
| if (delayed_task.delayed_run_time < top_run_time) { |
| ::SetTimer(wnd_, reinterpret_cast<UINT_PTR>(this), |
| static_cast<DWORD>(delay.InMilliseconds()), NULL); |
| } |
| } |
| |
| BOOL TaskMarshallerThroughMessageQueue::ProcessWindowMessage(HWND hWnd, |
| UINT uMsg, |
| WPARAM wParam, |
| LPARAM lParam, |
| LRESULT& lResult, |
| DWORD dwMsgMapID) { |
| if (hWnd == wnd_ && uMsg == msg_) { |
| ExecuteQueuedTasks(); |
| lResult = 0; |
| return TRUE; |
| } |
| |
| if (hWnd == wnd_ && uMsg == WM_TIMER) { |
| ExecuteDelayedTasks(); |
| lResult = 0; |
| return TRUE; |
| } |
| |
| return FALSE; |
| } |
| |
| base::Closure TaskMarshallerThroughMessageQueue::PopTask() { |
| base::AutoLock lock(lock_); |
| if (pending_tasks_.empty()) |
| return base::Closure(); |
| |
| base::Closure task = pending_tasks_.front(); |
| pending_tasks_.pop(); |
| return task; |
| } |
| |
| void TaskMarshallerThroughMessageQueue::ExecuteQueuedTasks() { |
| DCHECK(CalledOnValidThread()); |
| base::Closure task; |
| while (!(task = PopTask()).is_null()) |
| task.Run(); |
| } |
| |
| void TaskMarshallerThroughMessageQueue::ExecuteDelayedTasks() { |
| DCHECK(CalledOnValidThread()); |
| ::KillTimer(wnd_, reinterpret_cast<UINT_PTR>(this)); |
| while (true) { |
| lock_.Acquire(); |
| |
| if (delayed_tasks_.empty()) { |
| lock_.Release(); |
| return; |
| } |
| |
| base::PendingTask next_task = delayed_tasks_.top(); |
| base::TimeTicks now = base::TimeTicks::Now(); |
| base::TimeTicks next_run = next_task.delayed_run_time; |
| if (next_run > now) { |
| int64 delay = (next_run - now).InMillisecondsRoundedUp(); |
| ::SetTimer(wnd_, reinterpret_cast<UINT_PTR>(this), |
| static_cast<DWORD>(delay), NULL); |
| lock_.Release(); |
| return; |
| } |
| |
| delayed_tasks_.pop(); |
| lock_.Release(); |
| |
| // Run the task outside the lock. |
| next_task.task.Run(); |
| } |
| } |
| |
| void TaskMarshallerThroughMessageQueue::ClearTasks() { |
| base::AutoLock lock(lock_); |
| DVLOG_IF(1, !pending_tasks_.empty()) << "Destroying " |
| << pending_tasks_.size() |
| << " pending tasks."; |
| while (!pending_tasks_.empty()) |
| pending_tasks_.pop(); |
| |
| while (!delayed_tasks_.empty()) |
| delayed_tasks_.pop(); |
| } |