// 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(this), static_cast(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(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(this), static_cast(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(); }