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
10TaskMarshallerThroughMessageQueue::TaskMarshallerThroughMessageQueue()
11    : wnd_(NULL),
12      msg_(0xFFFF) {
13}
14
15TaskMarshallerThroughMessageQueue::~TaskMarshallerThroughMessageQueue() {
16  ClearTasks();
17}
18
19void 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
38void 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
58BOOL 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
79base::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
89void TaskMarshallerThroughMessageQueue::ExecuteQueuedTasks() {
90  DCHECK(CalledOnValidThread());
91  base::Closure task;
92  while (!(task = PopTask()).is_null())
93    task.Run();
94}
95
96void 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
126void 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