summaryrefslogtreecommitdiff
path: root/base/message_loop/incoming_task_queue.cc
blob: 316b5ec645d65e0df1b2c5a7b3dd03f76ac341fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
// Copyright 2013 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 "base/message_loop/incoming_task_queue.h"

#include <limits>
#include <utility>

#include "base/location.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/time/time.h"
#include "build/build_config.h"

namespace base {
namespace internal {

namespace {

#if DCHECK_IS_ON()
// Delays larger than this are often bogus, and a warning should be emitted in
// debug builds to warn developers.  http://crbug.com/450045
const int kTaskDelayWarningThresholdInSeconds =
    14 * 24 * 60 * 60;  // 14 days.
#endif

// Returns true if MessagePump::ScheduleWork() must be called one
// time for every task that is added to the MessageLoop incoming queue.
bool AlwaysNotifyPump(MessageLoop::Type type) {
#if defined(OS_ANDROID)
  // The Android UI message loop needs to get notified each time a task is
  // added
  // to the incoming queue.
  return type == MessageLoop::TYPE_UI || type == MessageLoop::TYPE_JAVA;
#else
  return false;
#endif
}

TimeTicks CalculateDelayedRuntime(TimeDelta delay) {
  TimeTicks delayed_run_time;
  if (delay > TimeDelta())
    delayed_run_time = TimeTicks::Now() + delay;
  else
    DCHECK_EQ(delay.InMilliseconds(), 0) << "delay should not be negative";
  return delayed_run_time;
}

}  // namespace

IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop)
    : high_res_task_count_(0),
      message_loop_(message_loop),
      next_sequence_num_(0),
      message_loop_scheduled_(false),
      always_schedule_work_(AlwaysNotifyPump(message_loop_->type())),
      is_ready_for_scheduling_(false) {
}

bool IncomingTaskQueue::AddToIncomingQueue(
    const tracked_objects::Location& from_here,
    OnceClosure task,
    TimeDelta delay,
    bool nestable) {
  DCHECK(task);
  DLOG_IF(WARNING,
          delay.InSeconds() > kTaskDelayWarningThresholdInSeconds)
      << "Requesting super-long task delay period of " << delay.InSeconds()
      << " seconds from here: " << from_here.ToString();

  PendingTask pending_task(from_here, std::move(task),
                           CalculateDelayedRuntime(delay), nestable);
#if defined(OS_WIN)
  // We consider the task needs a high resolution timer if the delay is
  // more than 0 and less than 32ms. This caps the relative error to
  // less than 50% : a 33ms wait can wake at 48ms since the default
  // resolution on Windows is between 10 and 15ms.
  if (delay > TimeDelta() &&
      delay.InMilliseconds() < (2 * Time::kMinLowResolutionThresholdMs)) {
    pending_task.is_high_res = true;
  }
#endif
  return PostPendingTask(&pending_task);
}

bool IncomingTaskQueue::HasHighResolutionTasks() {
  AutoLock lock(incoming_queue_lock_);
  return high_res_task_count_ > 0;
}

bool IncomingTaskQueue::IsIdleForTesting() {
  AutoLock lock(incoming_queue_lock_);
  return incoming_queue_.empty();
}

int IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) {
  // Make sure no tasks are lost.
  DCHECK(work_queue->empty());

  // Acquire all we can from the inter-thread queue with one lock acquisition.
  AutoLock lock(incoming_queue_lock_);
  if (incoming_queue_.empty()) {
    // If the loop attempts to reload but there are no tasks in the incoming
    // queue, that means it will go to sleep waiting for more work. If the
    // incoming queue becomes nonempty we need to schedule it again.
    message_loop_scheduled_ = false;
  } else {
    incoming_queue_.swap(*work_queue);
  }
  // Reset the count of high resolution tasks since our queue is now empty.
  int high_res_tasks = high_res_task_count_;
  high_res_task_count_ = 0;
  return high_res_tasks;
}

void IncomingTaskQueue::WillDestroyCurrentMessageLoop() {
  base::subtle::AutoWriteLock lock(message_loop_lock_);
  message_loop_ = NULL;
}

void IncomingTaskQueue::StartScheduling() {
  bool schedule_work;
  {
    AutoLock lock(incoming_queue_lock_);
    DCHECK(!is_ready_for_scheduling_);
    DCHECK(!message_loop_scheduled_);
    is_ready_for_scheduling_ = true;
    schedule_work = !incoming_queue_.empty();
  }
  if (schedule_work) {
    DCHECK(message_loop_);
    // Don't need to lock |message_loop_lock_| here because this function is
    // called by MessageLoop on its thread.
    message_loop_->ScheduleWork();
  }
}

IncomingTaskQueue::~IncomingTaskQueue() {
  // Verify that WillDestroyCurrentMessageLoop() has been called.
  DCHECK(!message_loop_);
}

bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) {
  // Warning: Don't try to short-circuit, and handle this thread's tasks more
  // directly, as it could starve handling of foreign threads.  Put every task
  // into this queue.

  // Ensures |message_loop_| isn't destroyed while running.
  base::subtle::AutoReadLock hold_message_loop(message_loop_lock_);

  if (!message_loop_) {
    pending_task->task.Reset();
    return false;
  }

  bool schedule_work = false;
  {
    AutoLock hold(incoming_queue_lock_);

#if defined(OS_WIN)
    if (pending_task->is_high_res)
      ++high_res_task_count_;
#endif

    // Initialize the sequence number. The sequence number is used for delayed
    // tasks (to facilitate FIFO sorting when two tasks have the same
    // delayed_run_time value) and for identifying the task in about:tracing.
    pending_task->sequence_num = next_sequence_num_++;

    message_loop_->task_annotator()->DidQueueTask("MessageLoop::PostTask",
                                                  *pending_task);

    bool was_empty = incoming_queue_.empty();
    incoming_queue_.push(std::move(*pending_task));

    if (is_ready_for_scheduling_ &&
        (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) {
      schedule_work = true;
      // After we've scheduled the message loop, we do not need to do so again
      // until we know it has processed all of the work in our queue and is
      // waiting for more work again. The message loop will always attempt to
      // reload from the incoming queue before waiting again so we clear this
      // flag in ReloadWorkQueue().
      message_loop_scheduled_ = true;
    }
  }

  // Wake up the message loop and schedule work. This is done outside
  // |incoming_queue_lock_| because signaling the message loop may cause this
  // thread to be switched. If |incoming_queue_lock_| is held, any other thread
  // that wants to post a task will be blocked until this thread switches back
  // in and releases |incoming_queue_lock_|.
  if (schedule_work)
    message_loop_->ScheduleWork();

  return true;
}

}  // namespace internal
}  // namespace base