Eliminate the TimerManager by pulling its priority queue into MessageLoop. This CL also eliminates TaskBase by creating a simple PendingTask struct that is allocated inline within a std::queue used to implement the queues in the MessageLoop class.
R=jar Review URL: http://codereview.chromium.org/483 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1825 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
@ -6,6 +6,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/message_pump_default.h"
|
||||
@ -57,13 +61,26 @@ static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
|
||||
|
||||
MessageLoop::MessageLoop(Type type)
|
||||
: type_(type),
|
||||
ALLOW_THIS_IN_INITIALIZER_LIST(timer_manager_(this)),
|
||||
nestable_tasks_allowed_(true),
|
||||
exception_restoration_(false),
|
||||
state_(NULL) {
|
||||
state_(NULL),
|
||||
next_sequence_num_(0) {
|
||||
DCHECK(!tls_index_.Get()) << "should only have one message loop per thread";
|
||||
tls_index_.Set(this);
|
||||
|
||||
// TODO(darin): This does not seem like the best place for this code to live!
|
||||
#if defined(OS_WIN)
|
||||
// We've experimented with all sorts of timers, and initially tried
|
||||
// to avoid using timeBeginPeriod because it does affect the system
|
||||
// globally. However, after much investigation, it turns out that all
|
||||
// of the major plugins (flash, windows media 9-11, and quicktime)
|
||||
// already use timeBeginPeriod to increase the speed of the clock.
|
||||
// Since the browser must work with these plugins, the browser already
|
||||
// needs to support a fast clock. We may as well use this ourselves,
|
||||
// as it really is the best timer mechanism for our needs.
|
||||
timeBeginPeriod(1);
|
||||
#endif
|
||||
|
||||
// TODO(darin): Choose the pump based on the requested type.
|
||||
#if defined(OS_WIN)
|
||||
if (type_ == TYPE_DEFAULT) {
|
||||
@ -95,6 +112,11 @@ MessageLoop::~MessageLoop() {
|
||||
DeletePendingTasks();
|
||||
ReloadWorkQueue();
|
||||
DeletePendingTasks();
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Match timeBeginPeriod() from construction.
|
||||
timeEndPeriod(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MessageLoop::AddDestructionObserver(DestructionObserver *obs) {
|
||||
@ -162,10 +184,13 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() {
|
||||
if (state_->run_depth != 1)
|
||||
return false;
|
||||
|
||||
if (delayed_non_nestable_queue_.Empty())
|
||||
if (deferred_non_nestable_work_queue_.empty())
|
||||
return false;
|
||||
|
||||
RunTask(delayed_non_nestable_queue_.Pop());
|
||||
|
||||
Task* task = deferred_non_nestable_work_queue_.front().task;
|
||||
deferred_non_nestable_work_queue_.pop();
|
||||
|
||||
RunTask(task);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -180,25 +205,41 @@ void MessageLoop::Quit() {
|
||||
}
|
||||
}
|
||||
|
||||
void MessageLoop::PostTask(
|
||||
const tracked_objects::Location& from_here, Task* task) {
|
||||
PostTask_Helper(from_here, task, 0, true);
|
||||
}
|
||||
|
||||
void MessageLoop::PostDelayedTask(
|
||||
const tracked_objects::Location& from_here, Task* task, int delay_ms) {
|
||||
PostTask_Helper(from_here, task, delay_ms, true);
|
||||
}
|
||||
|
||||
void MessageLoop::PostNonNestableTask(
|
||||
const tracked_objects::Location& from_here, Task* task) {
|
||||
PostTask_Helper(from_here, task, 0, false);
|
||||
}
|
||||
|
||||
void MessageLoop::PostNonNestableDelayedTask(
|
||||
const tracked_objects::Location& from_here, Task* task, int delay_ms) {
|
||||
PostTask_Helper(from_here, task, delay_ms, false);
|
||||
}
|
||||
|
||||
// Possibly called on a background thread!
|
||||
void MessageLoop::PostDelayedTask(const tracked_objects::Location& from_here,
|
||||
Task* task, int delay_ms) {
|
||||
void MessageLoop::PostTask_Helper(
|
||||
const tracked_objects::Location& from_here, Task* task, int delay_ms,
|
||||
bool nestable) {
|
||||
task->SetBirthPlace(from_here);
|
||||
|
||||
DCHECK(!task->owned_by_message_loop_);
|
||||
task->owned_by_message_loop_ = true;
|
||||
PendingTask pending_task(task, nestable);
|
||||
|
||||
if (delay_ms > 0) {
|
||||
task->delayed_run_time_ =
|
||||
pending_task.delayed_run_time =
|
||||
Time::Now() + TimeDelta::FromMilliseconds(delay_ms);
|
||||
} else {
|
||||
DCHECK(delay_ms == 0) << "delay should not be negative";
|
||||
}
|
||||
|
||||
PostTaskInternal(task);
|
||||
}
|
||||
|
||||
void MessageLoop::PostTaskInternal(Task* 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.
|
||||
@ -207,8 +248,8 @@ void MessageLoop::PostTaskInternal(Task* task) {
|
||||
{
|
||||
AutoLock locked(incoming_queue_lock_);
|
||||
|
||||
bool was_empty = incoming_queue_.Empty();
|
||||
incoming_queue_.Push(task);
|
||||
bool was_empty = incoming_queue_.empty();
|
||||
incoming_queue_.push(pending_task);
|
||||
if (!was_empty)
|
||||
return; // Someone else should have started the sub-pump.
|
||||
|
||||
@ -238,120 +279,47 @@ bool MessageLoop::NestableTasksAllowed() const {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
bool MessageLoop::RunTimerTask(Timer* timer) {
|
||||
HistogramEvent(kTimerEvent);
|
||||
|
||||
Task* task = timer->task();
|
||||
if (task->owned_by_message_loop_) {
|
||||
// We constructed it through PostDelayedTask().
|
||||
DCHECK(!timer->repeating());
|
||||
timer->set_task(NULL);
|
||||
delete timer;
|
||||
task->ResetBirthTime();
|
||||
return QueueOrRunTask(task);
|
||||
}
|
||||
|
||||
// This is an unknown timer task, and we *can't* delay running it, as a user
|
||||
// might try to cancel it with TimerManager at any moment.
|
||||
DCHECK(nestable_tasks_allowed_);
|
||||
RunTask(task);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessageLoop::DiscardTimer(Timer* timer) {
|
||||
Task* task = timer->task();
|
||||
if (task->owned_by_message_loop_) {
|
||||
DCHECK(!timer->repeating());
|
||||
timer->set_task(NULL);
|
||||
delete timer; // We constructed it through PostDelayedTask().
|
||||
delete task; // We were given ouwnership in PostTask().
|
||||
}
|
||||
}
|
||||
|
||||
bool MessageLoop::QueueOrRunTask(Task* new_task) {
|
||||
if (!nestable_tasks_allowed_) {
|
||||
// Task can't be executed right now. Add it to the queue.
|
||||
if (new_task)
|
||||
work_queue_.Push(new_task);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Queue new_task first so we execute the task in FIFO order.
|
||||
if (new_task)
|
||||
work_queue_.Push(new_task);
|
||||
|
||||
// Execute oldest task.
|
||||
while (!work_queue_.Empty()) {
|
||||
Task* task = work_queue_.Pop();
|
||||
if (task->nestable() || state_->run_depth == 1) {
|
||||
RunTask(task);
|
||||
// Show that we ran a task (Note: a new one might arrive as a
|
||||
// consequence!).
|
||||
return true;
|
||||
}
|
||||
// We couldn't run the task now because we're in a nested message loop
|
||||
// and the task isn't nestable.
|
||||
delayed_non_nestable_queue_.Push(task);
|
||||
}
|
||||
|
||||
// Nothing happened.
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageLoop::RunTask(Task* task) {
|
||||
BeforeTaskRunSetup();
|
||||
HistogramEvent(kTaskRunEvent);
|
||||
// task may self-delete during Run() if we don't happen to own it.
|
||||
// ...so check *before* we Run, since we can't check after.
|
||||
bool we_own_task = task->owned_by_message_loop_;
|
||||
task->Run();
|
||||
if (we_own_task)
|
||||
task->RecycleOrDelete(); // Relinquish control, and probably delete.
|
||||
AfterTaskRunRestore();
|
||||
}
|
||||
|
||||
void MessageLoop::BeforeTaskRunSetup() {
|
||||
DCHECK(nestable_tasks_allowed_);
|
||||
// Execute the task and assume the worst: It is probably not reentrant.
|
||||
nestable_tasks_allowed_ = false;
|
||||
|
||||
HistogramEvent(kTaskRunEvent);
|
||||
task->Run();
|
||||
delete task;
|
||||
|
||||
nestable_tasks_allowed_ = true;
|
||||
}
|
||||
|
||||
void MessageLoop::AfterTaskRunRestore() {
|
||||
nestable_tasks_allowed_ = true;
|
||||
bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) {
|
||||
if (pending_task.nestable || state_->run_depth == 1) {
|
||||
RunTask(pending_task.task);
|
||||
// Show that we ran a task (Note: a new one might arrive as a
|
||||
// consequence!).
|
||||
return true;
|
||||
}
|
||||
|
||||
// We couldn't run the task now because we're in a nested message loop
|
||||
// and the task isn't nestable.
|
||||
deferred_non_nestable_work_queue_.push(pending_task);
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessageLoop::ReloadWorkQueue() {
|
||||
// We can improve performance of our loading tasks from incoming_queue_ to
|
||||
// work_queue_ by waiting until the last minute (work_queue_ is empty) to
|
||||
// load. That reduces the number of locks-per-task significantly when our
|
||||
// queues get large. The optimization is disabled on threads that make use
|
||||
// of the priority queue (prioritization requires all our tasks to be in the
|
||||
// work_queue_ ASAP).
|
||||
if (!work_queue_.Empty() && !work_queue_.use_priority_queue())
|
||||
// queues get large.
|
||||
if (!work_queue_.empty())
|
||||
return; // Wait till we *really* need to lock and load.
|
||||
|
||||
// Acquire all we can from the inter-thread queue with one lock acquisition.
|
||||
TaskQueue new_task_list; // Null terminated list.
|
||||
{
|
||||
AutoLock lock(incoming_queue_lock_);
|
||||
if (incoming_queue_.Empty())
|
||||
if (incoming_queue_.empty())
|
||||
return;
|
||||
std::swap(incoming_queue_, new_task_list);
|
||||
DCHECK(incoming_queue_.Empty());
|
||||
} // Release lock.
|
||||
|
||||
while (!new_task_list.Empty()) {
|
||||
Task* task = new_task_list.Pop();
|
||||
DCHECK(task->owned_by_message_loop_);
|
||||
|
||||
// TODO(darin): We should probably postpone starting the timer until we
|
||||
// process the work queue as starting the timer here breaks the FIFO
|
||||
// ordering of tasks.
|
||||
if (!task->delayed_run_time_.is_null()) {
|
||||
timer_manager_.StartTimer(new Timer(task->delayed_run_time_, task));
|
||||
} else {
|
||||
work_queue_.Push(task);
|
||||
}
|
||||
std::swap(incoming_queue_, work_queue_);
|
||||
DCHECK(incoming_queue_.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@ -371,32 +339,62 @@ void MessageLoop::DeletePendingTasks() {
|
||||
*/
|
||||
}
|
||||
|
||||
void MessageLoop::DidChangeNextTimerExpiry() {
|
||||
Time next_delayed_work_time = timer_manager_.GetNextFireTime();
|
||||
if (next_delayed_work_time.is_null())
|
||||
return;
|
||||
|
||||
// Simulates malfunctioning, early firing timers. Pending tasks should only
|
||||
// be invoked when the delay they specify has elapsed.
|
||||
if (timer_manager_.use_broken_delay())
|
||||
next_delayed_work_time = Time::Now() + TimeDelta::FromMilliseconds(10);
|
||||
|
||||
pump_->ScheduleDelayedWork(next_delayed_work_time);
|
||||
}
|
||||
|
||||
bool MessageLoop::DoWork() {
|
||||
ReloadWorkQueue();
|
||||
return QueueOrRunTask(NULL);
|
||||
if (!nestable_tasks_allowed_) {
|
||||
// Task can't be executed right now.
|
||||
return false;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ReloadWorkQueue();
|
||||
if (work_queue_.empty())
|
||||
break;
|
||||
|
||||
// Execute oldest task.
|
||||
do {
|
||||
PendingTask pending_task = work_queue_.front();
|
||||
work_queue_.pop();
|
||||
if (!pending_task.delayed_run_time.is_null()) {
|
||||
bool was_empty = delayed_work_queue_.empty();
|
||||
|
||||
// Move to the delayed work queue. Initialize the sequence number
|
||||
// before inserting into the delayed_work_queue_. The sequence number
|
||||
// is used to faciliate FIFO sorting when two tasks have the same
|
||||
// delayed_run_time value.
|
||||
pending_task.sequence_num = next_sequence_num_++;
|
||||
delayed_work_queue_.push(pending_task);
|
||||
|
||||
if (was_empty) // We only schedule the next delayed work item.
|
||||
pump_->ScheduleDelayedWork(pending_task.delayed_run_time);
|
||||
} else {
|
||||
if (DeferOrRunPendingTask(pending_task))
|
||||
return true;
|
||||
}
|
||||
} while (!work_queue_.empty());
|
||||
}
|
||||
|
||||
// Nothing happened.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MessageLoop::DoDelayedWork(Time* next_delayed_work_time) {
|
||||
bool did_work = timer_manager_.RunSomePendingTimers();
|
||||
if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) {
|
||||
*next_delayed_work_time = Time();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delayed_work_queue_.top().delayed_run_time > Time::Now()) {
|
||||
*next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
|
||||
return false;
|
||||
}
|
||||
|
||||
// We may not have run any timers, but we may still have future timers to
|
||||
// run, so we need to inform the pump again of pending timers.
|
||||
*next_delayed_work_time = timer_manager_.GetNextFireTime();
|
||||
PendingTask pending_task = delayed_work_queue_.top();
|
||||
delayed_work_queue_.pop();
|
||||
|
||||
if (!delayed_work_queue_.empty())
|
||||
*next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
|
||||
|
||||
return did_work;
|
||||
return DeferOrRunPendingTask(pending_task);
|
||||
}
|
||||
|
||||
bool MessageLoop::DoIdleWork() {
|
||||
@ -434,80 +432,22 @@ MessageLoop::AutoRunState::~AutoRunState() {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Implementation of the work_queue_ as a ProiritizedTaskQueue
|
||||
// MessageLoop::PendingTask
|
||||
|
||||
void MessageLoop::PrioritizedTaskQueue::push(Task * task) {
|
||||
queue_.push(PrioritizedTask(task, --next_sequence_number_));
|
||||
}
|
||||
bool MessageLoop::PendingTask::operator<(const PendingTask& other) const {
|
||||
// Since the top of a priority queue is defined as the "greatest" element, we
|
||||
// need to invert the comparison here. We want the smaller time to be at the
|
||||
// top of the heap.
|
||||
|
||||
bool MessageLoop::PrioritizedTaskQueue::PrioritizedTask::operator < (
|
||||
PrioritizedTask const & right) const {
|
||||
int compare = task_->priority() - right.task_->priority();
|
||||
if (compare)
|
||||
return compare < 0;
|
||||
// Don't compare directly, but rather subtract. This handles overflow
|
||||
// as sequence numbers wrap around.
|
||||
compare = sequence_number_ - right.sequence_number_;
|
||||
DCHECK(compare); // Sequence number are unique for a "long time."
|
||||
// Make sure we don't starve anything with a low priority.
|
||||
CHECK(INT_MAX/8 > compare); // We don't get close to wrapping.
|
||||
CHECK(INT_MIN/8 < compare); // We don't get close to wrapping.
|
||||
return compare < 0;
|
||||
}
|
||||
if (delayed_run_time < other.delayed_run_time)
|
||||
return false;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Implementation of a TaskQueue as a null terminated list, with end pointers.
|
||||
if (delayed_run_time > other.delayed_run_time)
|
||||
return true;
|
||||
|
||||
void MessageLoop::TaskQueue::Push(Task* task) {
|
||||
if (!first_)
|
||||
first_ = task;
|
||||
else
|
||||
last_->set_next_task(task);
|
||||
last_ = task;
|
||||
}
|
||||
|
||||
Task* MessageLoop::TaskQueue::Pop() {
|
||||
DCHECK((!first_) == !last_);
|
||||
Task* task = first_;
|
||||
if (first_) {
|
||||
first_ = task->next_task();
|
||||
if (!first_)
|
||||
last_ = NULL;
|
||||
else
|
||||
task->set_next_task(NULL);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Implementation of a Task queue that automatically switches into a priority
|
||||
// queue if it observes any non-zero priorities on tasks.
|
||||
|
||||
void MessageLoop::OptionallyPrioritizedTaskQueue::Push(Task* task) {
|
||||
if (use_priority_queue_) {
|
||||
prioritized_queue_.push(task);
|
||||
} else {
|
||||
queue_.Push(task);
|
||||
if (task->priority()) {
|
||||
use_priority_queue_ = true; // From now on.
|
||||
while (!queue_.Empty())
|
||||
prioritized_queue_.push(queue_.Pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Task* MessageLoop::OptionallyPrioritizedTaskQueue::Pop() {
|
||||
if (!use_priority_queue_)
|
||||
return queue_.Pop();
|
||||
Task* task = prioritized_queue_.front();
|
||||
prioritized_queue_.pop();
|
||||
return task;
|
||||
}
|
||||
|
||||
bool MessageLoop::OptionallyPrioritizedTaskQueue::Empty() {
|
||||
if (use_priority_queue_)
|
||||
return prioritized_queue_.empty();
|
||||
return queue_.Empty();
|
||||
// If the times happen to match, then we use the sequence number to decide.
|
||||
// Compare the difference to support integer roll-over.
|
||||
return (sequence_num - other.sequence_num) > 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
@ -80,24 +80,34 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
// DestructionObserver is receiving a notification callback.
|
||||
void RemoveDestructionObserver(DestructionObserver* destruction_observer);
|
||||
|
||||
// Call the task's Run method asynchronously from within a message loop at
|
||||
// some point in the future. With the PostTask variant, tasks are invoked in
|
||||
// FIFO order, inter-mixed with normal UI event processing. With the
|
||||
// PostDelayedTask variant, tasks are called after at least approximately
|
||||
// 'delay_ms' have elapsed.
|
||||
// The "PostTask" family of methods call the task's Run method asynchronously
|
||||
// from within a message loop at some point in the future.
|
||||
//
|
||||
// The MessageLoop takes ownership of the Task, and deletes it after it
|
||||
// has been Run().
|
||||
// With the PostTask variant, tasks are invoked in FIFO order, inter-mixed
|
||||
// with normal UI or IO event processing. With the PostDelayedTask variant,
|
||||
// tasks are called after at least approximately 'delay_ms' have elapsed.
|
||||
//
|
||||
// NOTE: This method may be called on any thread. The Task will be invoked
|
||||
// The NonNestable variants work similarly except that they promise never to
|
||||
// dispatch the task from a nested invocation of MessageLoop::Run. Instead,
|
||||
// such tasks get deferred until the top-most MessageLoop::Run is executing.
|
||||
//
|
||||
// The MessageLoop takes ownership of the Task, and deletes it after it has
|
||||
// been Run().
|
||||
//
|
||||
// NOTE: These methods may be called on any thread. The Task will be invoked
|
||||
// on the thread that executes MessageLoop::Run().
|
||||
|
||||
void PostTask(
|
||||
const tracked_objects::Location& from_here, Task* task);
|
||||
|
||||
void PostDelayedTask(
|
||||
const tracked_objects::Location& from_here, Task* task, int delay_ms);
|
||||
|
||||
void PostTask(const tracked_objects::Location& from_here, Task* task) {
|
||||
PostDelayedTask(from_here, task, 0);
|
||||
}
|
||||
void PostNonNestableTask(
|
||||
const tracked_objects::Location& from_here, Task* task);
|
||||
|
||||
void PostDelayedTask(const tracked_objects::Location& from_here, Task* task,
|
||||
int delay_ms);
|
||||
void PostNonNestableDelayedTask(
|
||||
const tracked_objects::Location& from_here, Task* task, int delay_ms);
|
||||
|
||||
// A variant on PostTask that deletes the given object. This is useful
|
||||
// if the object needs to live until the next run of the MessageLoop (for
|
||||
@ -110,7 +120,7 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
// from RefCountedThreadSafe<T>!
|
||||
template <class T>
|
||||
void DeleteSoon(const tracked_objects::Location& from_here, T* object) {
|
||||
PostTask(from_here, new DeleteTask<T>(object));
|
||||
PostNonNestableTask(from_here, new DeleteTask<T>(object));
|
||||
}
|
||||
|
||||
// A variant on PostTask that releases the given reference counted object
|
||||
@ -125,7 +135,7 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
// RefCountedThreadSafe<T>!
|
||||
template <class T>
|
||||
void ReleaseSoon(const tracked_objects::Location& from_here, T* object) {
|
||||
PostTask(from_here, new ReleaseTask<T>(object));
|
||||
PostNonNestableTask(from_here, new ReleaseTask<T>(object));
|
||||
}
|
||||
|
||||
// Run the message loop.
|
||||
@ -199,10 +209,6 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
return loop;
|
||||
}
|
||||
|
||||
// Returns the TimerManager object for the current thread. This getter is
|
||||
// deprecated. Please use OneShotTimer or RepeatingTimer instead.
|
||||
base::TimerManager* timer_manager_deprecated() { return &timer_manager_; }
|
||||
|
||||
// Enables or disables the recursive task processing. This happens in the case
|
||||
// of recursive message loops. Some unwanted message loop may occurs when
|
||||
// using common controls or printer functions. By default, recursive task
|
||||
@ -229,11 +235,8 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
exception_restoration_ = restore;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
protected:
|
||||
friend class base::TimerManager; // So it can call DidChangeNextTimerExpiry
|
||||
|
||||
struct RunState {
|
||||
// Used to count how many Run() invocations are on the stack.
|
||||
int run_depth;
|
||||
@ -256,70 +259,23 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
RunState* previous_state_;
|
||||
};
|
||||
|
||||
// A prioritized queue with interface that mostly matches std::queue<>.
|
||||
// For debugging/performance testing, you can swap in std::queue<Task*>.
|
||||
class PrioritizedTaskQueue {
|
||||
public:
|
||||
PrioritizedTaskQueue() : next_sequence_number_(0) {}
|
||||
~PrioritizedTaskQueue() {}
|
||||
void pop() { queue_.pop(); }
|
||||
bool empty() { return queue_.empty(); }
|
||||
size_t size() { return queue_.size(); }
|
||||
Task* front() { return queue_.top().task(); }
|
||||
void push(Task * task);
|
||||
// This structure is copied around by value.
|
||||
struct PendingTask {
|
||||
Task* task; // The task to run.
|
||||
Time delayed_run_time; // The time when the task should be run.
|
||||
int sequence_num; // Used to facilitate sorting by run time.
|
||||
bool nestable; // True if OK to dispatch from a nested loop.
|
||||
|
||||
private:
|
||||
class PrioritizedTask {
|
||||
public:
|
||||
PrioritizedTask(Task* task, int sequence_number)
|
||||
: task_(task),
|
||||
sequence_number_(sequence_number),
|
||||
priority_(task->priority()) {}
|
||||
Task* task() const { return task_; }
|
||||
bool operator < (PrioritizedTask const & right) const ;
|
||||
|
||||
private:
|
||||
Task* task_;
|
||||
// Number to ensure (default) FIFO ordering in a PriorityQueue.
|
||||
int sequence_number_;
|
||||
// Priority of task when pushed.
|
||||
int priority_;
|
||||
}; // class PrioritizedTask
|
||||
|
||||
std::priority_queue<PrioritizedTask> queue_;
|
||||
// Default sequence number used when push'ing (monotonically decreasing).
|
||||
int next_sequence_number_;
|
||||
DISALLOW_EVIL_CONSTRUCTORS(PrioritizedTaskQueue);
|
||||
PendingTask(Task* task, bool nestable)
|
||||
: task(task), sequence_num(0), nestable(nestable) {
|
||||
}
|
||||
|
||||
// Used to support sorting.
|
||||
bool operator<(const PendingTask& other) const;
|
||||
};
|
||||
|
||||
// Implementation of a TaskQueue as a null terminated list, with end pointers.
|
||||
class TaskQueue {
|
||||
public:
|
||||
TaskQueue() : first_(NULL), last_(NULL) {}
|
||||
void Push(Task* task);
|
||||
Task* Pop(); // Extract the next Task from the queue, and return it.
|
||||
bool Empty() const { return !first_; }
|
||||
private:
|
||||
Task* first_;
|
||||
Task* last_;
|
||||
};
|
||||
|
||||
// Implementation of a Task queue that automatically switches into a priority
|
||||
// queue if it observes any non-zero priorities in tasks.
|
||||
class OptionallyPrioritizedTaskQueue {
|
||||
public:
|
||||
OptionallyPrioritizedTaskQueue() : use_priority_queue_(false) {}
|
||||
void Push(Task* task);
|
||||
Task* Pop(); // Extract next Task from queue, and return it.
|
||||
bool Empty();
|
||||
bool use_priority_queue() const { return use_priority_queue_; }
|
||||
|
||||
private:
|
||||
bool use_priority_queue_;
|
||||
PrioritizedTaskQueue prioritized_queue_;
|
||||
TaskQueue queue_;
|
||||
DISALLOW_EVIL_CONSTRUCTORS(OptionallyPrioritizedTaskQueue);
|
||||
};
|
||||
typedef std::queue<PendingTask> TaskQueue;
|
||||
typedef std::priority_queue<PendingTask> DelayedTaskQueue;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
base::MessagePumpWin* pump_win() {
|
||||
@ -356,10 +312,9 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
// Runs the specified task and deletes it.
|
||||
void RunTask(Task* task);
|
||||
|
||||
// Make state adjustments just before and after running tasks so that we can
|
||||
// continue to work if a native message loop is employed during a task.
|
||||
void BeforeTaskRunSetup();
|
||||
void AfterTaskRunRestore();
|
||||
// Calls RunTask or queues the pending_task on the deferred task list if it
|
||||
// cannot be run right now. Returns true if the task was run.
|
||||
bool DeferOrRunPendingTask(const PendingTask& pending_task);
|
||||
|
||||
// Load tasks from the incoming_queue_ into work_queue_ if the latter is
|
||||
// empty. The former requires a lock to access, while the latter is directly
|
||||
@ -371,20 +326,8 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
void DeletePendingTasks();
|
||||
|
||||
// Post a task to our incomming queue.
|
||||
void PostTaskInternal(Task* task);
|
||||
|
||||
// Called by the TimerManager when its next timer changes.
|
||||
void DidChangeNextTimerExpiry();
|
||||
|
||||
// Entry point for TimerManager to request the Run() of a task. If we
|
||||
// created the task during an PostTask(FROM_HERE, ), then we will also
|
||||
// perform destructions, and we'll have the option of queueing the task. If
|
||||
// we didn't create the timer, then we will Run it immediately.
|
||||
bool RunTimerTask(Timer* timer);
|
||||
|
||||
// Since some Timer's are owned by MessageLoop, the TimerManager (when it is
|
||||
// being destructed) passses us the timers to discard (without doing a Run()).
|
||||
void DiscardTimer(Timer* timer);
|
||||
void PostTask_Helper(const tracked_objects::Location& from_here, Task* task,
|
||||
int delay_ms, bool nestable);
|
||||
|
||||
// base::MessagePump::Delegate methods:
|
||||
virtual bool DoWork();
|
||||
@ -406,16 +349,17 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
|
||||
Type type_;
|
||||
|
||||
base::TimerManager timer_manager_;
|
||||
// A list of tasks that need to be processed by this instance. Note that
|
||||
// this queue is only accessed (push/pop) by our current thread.
|
||||
TaskQueue work_queue_;
|
||||
|
||||
// Contains delayed tasks, sorted by their 'delayed_run_time' property.
|
||||
DelayedTaskQueue delayed_work_queue_;
|
||||
|
||||
// A list of tasks that need to be processed by this instance. Note that this
|
||||
// queue is only accessed (push/pop) by our current thread.
|
||||
// As an optimization, when we don't need to use the prioritization of
|
||||
// work_queue_, we use a null terminated list (TaskQueue) as our
|
||||
// implementation of the queue. This saves on memory (list uses pointers
|
||||
// internal to Task) and probably runs faster than the priority queue when
|
||||
// there was no real prioritization.
|
||||
OptionallyPrioritizedTaskQueue work_queue_;
|
||||
// A queue of non-nestable tasks that we had to defer because when it came
|
||||
// time to execute them we were in a nested message loop. They will execute
|
||||
// once we're out of nested message loops.
|
||||
TaskQueue deferred_non_nestable_work_queue_;
|
||||
|
||||
scoped_refptr<base::MessagePump> pump_;
|
||||
|
||||
@ -438,13 +382,11 @@ class MessageLoop : public base::MessagePump::Delegate {
|
||||
// Protect access to incoming_queue_.
|
||||
Lock incoming_queue_lock_;
|
||||
|
||||
// A null terminated list of non-nestable tasks that we had to delay because
|
||||
// when it came time to execute them we were in a nested message loop. They
|
||||
// will execute once we're out of nested message loops.
|
||||
TaskQueue delayed_non_nestable_queue_;
|
||||
|
||||
RunState* state_;
|
||||
|
||||
// The next sequence number to use for delayed tasks.
|
||||
int next_sequence_num_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessageLoop);
|
||||
};
|
||||
|
||||
|
@ -137,6 +137,159 @@ void RunTest_PostTask_SEH(MessageLoop::Type message_loop_type) {
|
||||
EXPECT_EQ(foo->result(), "abacad");
|
||||
}
|
||||
|
||||
// This class runs slowly to simulate a large amount of work being done.
|
||||
class SlowTask : public Task {
|
||||
public:
|
||||
SlowTask(int pause_ms, int* quit_counter)
|
||||
: pause_ms_(pause_ms), quit_counter_(quit_counter) {
|
||||
}
|
||||
virtual void Run() {
|
||||
PlatformThread::Sleep(pause_ms_);
|
||||
if (--(*quit_counter_) == 0)
|
||||
MessageLoop::current()->Quit();
|
||||
}
|
||||
private:
|
||||
int pause_ms_;
|
||||
int* quit_counter_;
|
||||
};
|
||||
|
||||
// This class records the time when Run was called in a Time object, which is
|
||||
// useful for building a variety of MessageLoop tests.
|
||||
class RecordRunTimeTask : public SlowTask {
|
||||
public:
|
||||
RecordRunTimeTask(Time* run_time, int* quit_counter)
|
||||
: SlowTask(10, quit_counter), run_time_(run_time) {
|
||||
}
|
||||
virtual void Run() {
|
||||
*run_time_ = Time::Now();
|
||||
// Cause our Run function to take some time to execute. As a result we can
|
||||
// count on subsequent RecordRunTimeTask objects running at a future time,
|
||||
// without worry about the resolution of our system clock being an issue.
|
||||
SlowTask::Run();
|
||||
}
|
||||
private:
|
||||
Time* run_time_;
|
||||
};
|
||||
|
||||
void RunTest_PostDelayedTask_Basic(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Test that PostDelayedTask results in a delayed task.
|
||||
|
||||
const int kDelayMS = 100;
|
||||
|
||||
int num_tasks = 1;
|
||||
Time run_time;
|
||||
|
||||
loop.PostDelayedTask(
|
||||
FROM_HERE, new RecordRunTimeTask(&run_time, &num_tasks), kDelayMS);
|
||||
|
||||
Time time_before_run = Time::Now();
|
||||
loop.Run();
|
||||
Time time_after_run = Time::Now();
|
||||
|
||||
EXPECT_EQ(0, num_tasks);
|
||||
EXPECT_LT(kDelayMS, (time_after_run - time_before_run).InMilliseconds());
|
||||
}
|
||||
|
||||
void RunTest_PostDelayedTask_InDelayOrder(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Test that two tasks with different delays run in the right order.
|
||||
|
||||
int num_tasks = 2;
|
||||
Time run_time1, run_time2;
|
||||
|
||||
loop.PostDelayedTask(
|
||||
FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks), 200);
|
||||
// If we get a large pause in execution (due to a context switch) here, this
|
||||
// test could fail.
|
||||
loop.PostDelayedTask(
|
||||
FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), 10);
|
||||
|
||||
loop.Run();
|
||||
EXPECT_EQ(0, num_tasks);
|
||||
|
||||
EXPECT_TRUE(run_time2 < run_time1);
|
||||
}
|
||||
|
||||
void RunTest_PostDelayedTask_InPostOrder(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Test that two tasks with the same delay run in the order in which they
|
||||
// were posted.
|
||||
//
|
||||
// NOTE: This is actually an approximate test since the API only takes a
|
||||
// "delay" parameter, so we are not exactly simulating two tasks that get
|
||||
// posted at the exact same time. It would be nice if the API allowed us to
|
||||
// specify the desired run time.
|
||||
|
||||
const int kDelayMS = 100;
|
||||
|
||||
int num_tasks = 2;
|
||||
Time run_time1, run_time2;
|
||||
|
||||
loop.PostDelayedTask(
|
||||
FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks), kDelayMS);
|
||||
loop.PostDelayedTask(
|
||||
FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), kDelayMS);
|
||||
|
||||
loop.Run();
|
||||
EXPECT_EQ(0, num_tasks);
|
||||
|
||||
EXPECT_TRUE(run_time1 < run_time2);
|
||||
}
|
||||
|
||||
void RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Test that a delayed task still runs after a normal tasks even if the
|
||||
// normal tasks take a long time to run.
|
||||
|
||||
const int kPauseMS = 50;
|
||||
|
||||
int num_tasks = 2;
|
||||
Time run_time;
|
||||
|
||||
loop.PostTask(
|
||||
FROM_HERE, new SlowTask(kPauseMS, &num_tasks));
|
||||
loop.PostDelayedTask(
|
||||
FROM_HERE, new RecordRunTimeTask(&run_time, &num_tasks), 10);
|
||||
|
||||
Time time_before_run = Time::Now();
|
||||
loop.Run();
|
||||
Time time_after_run = Time::Now();
|
||||
|
||||
EXPECT_EQ(0, num_tasks);
|
||||
|
||||
EXPECT_LT(kPauseMS, (time_after_run - time_before_run).InMilliseconds());
|
||||
}
|
||||
|
||||
void RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Test that a delayed task still runs after a pile of normal tasks. The key
|
||||
// difference between this test and the previous one is that here we return
|
||||
// the MessageLoop a lot so we give the MessageLoop plenty of opportunities
|
||||
// to maybe run the delayed task. It should know not to do so until the
|
||||
// delayed task's delay has passed.
|
||||
|
||||
int num_tasks = 11;
|
||||
Time run_time1, run_time2;
|
||||
|
||||
// Clutter the ML with tasks.
|
||||
for (int i = 1; i < num_tasks; ++i)
|
||||
loop.PostTask(FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks));
|
||||
|
||||
loop.PostDelayedTask(
|
||||
FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), 1);
|
||||
|
||||
loop.Run();
|
||||
EXPECT_EQ(0, num_tasks);
|
||||
|
||||
EXPECT_TRUE(run_time2 > run_time1);
|
||||
}
|
||||
|
||||
class NestingTest : public Task {
|
||||
public:
|
||||
explicit NestingTest(int* depth) : depth_(depth) {
|
||||
@ -705,8 +858,7 @@ void RunTest_NonNestableWithNoNesting(MessageLoop::Type message_loop_type) {
|
||||
TaskList order;
|
||||
|
||||
Task* task = new OrderedTasks(&order, 1);
|
||||
task->set_nestable(false);
|
||||
MessageLoop::current()->PostTask(FROM_HERE, task);
|
||||
MessageLoop::current()->PostNonNestableTask(FROM_HERE, task);
|
||||
MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 2));
|
||||
MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3));
|
||||
MessageLoop::current()->Run();
|
||||
@ -730,13 +882,11 @@ void RunTest_NonNestableInNestedLoop(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop::current()->PostTask(FROM_HERE,
|
||||
new TaskThatPumps(&order, 1));
|
||||
Task* task = new OrderedTasks(&order, 2);
|
||||
task->set_nestable(false);
|
||||
MessageLoop::current()->PostTask(FROM_HERE, task);
|
||||
MessageLoop::current()->PostNonNestableTask(FROM_HERE, task);
|
||||
MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 3));
|
||||
MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 4));
|
||||
Task* non_nestable_quit = new QuitTask(&order, 5);
|
||||
non_nestable_quit->set_nestable(false);
|
||||
MessageLoop::current()->PostTask(FROM_HERE, non_nestable_quit);
|
||||
MessageLoop::current()->PostNonNestableTask(FROM_HERE, non_nestable_quit);
|
||||
|
||||
MessageLoop::current()->Run();
|
||||
|
||||
@ -869,6 +1019,36 @@ TEST(MessageLoopTest, PostTask_SEH) {
|
||||
RunTest_PostTask_SEH(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(MessageLoopTest, PostDelayedTask_Basic) {
|
||||
RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_UI);
|
||||
RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(MessageLoopTest, PostDelayedTask_InDelayOrder) {
|
||||
RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_UI);
|
||||
RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(MessageLoopTest, PostDelayedTask_InPostOrder) {
|
||||
RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_UI);
|
||||
RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(MessageLoopTest, PostDelayedTask_InPostOrder_2) {
|
||||
RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_UI);
|
||||
RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(MessageLoopTest, PostDelayedTask_InPostOrder_3) {
|
||||
RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_UI);
|
||||
RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
TEST(MessageLoopTest, Crasher) {
|
||||
RunTest_Crasher(MessageLoop::TYPE_DEFAULT);
|
||||
|
78
base/task.h
78
base/task.h
@ -15,86 +15,12 @@
|
||||
#include "base/tracked.h"
|
||||
#include "base/tuple.h"
|
||||
|
||||
namespace base {
|
||||
class TimerManager;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Base class of Task, where we store info to help MessageLoop handle PostTask()
|
||||
// elements of Task processing.
|
||||
|
||||
class Task;
|
||||
|
||||
// TODO(darin): Eliminate TaskBase. This data is ML specific and is not
|
||||
// applicable to other uses of Task.
|
||||
class TaskBase : public tracked_objects::Tracked {
|
||||
public:
|
||||
TaskBase() { Reset(); }
|
||||
virtual ~TaskBase() {}
|
||||
|
||||
// Use this method to adjust the priority given to a task by MessageLoop.
|
||||
void set_priority(int priority) { priority_ = priority; }
|
||||
int priority() const { return priority_; }
|
||||
|
||||
// Change whether this task will run in nested message loops.
|
||||
void set_nestable(bool nestable) { nestable_ = nestable; }
|
||||
bool nestable() { return nestable_; }
|
||||
|
||||
// Used to manage a linked-list of tasks.
|
||||
Task* next_task() const { return next_task_; }
|
||||
void set_next_task(Task* next) { next_task_ = next; }
|
||||
|
||||
protected:
|
||||
// If a derived class wishes to re-use this instance, then it should override
|
||||
// this method. This method is called by MessageLoop after processing a task
|
||||
// that was submitted to PostTask() or PostDelayedTask(). As seen, by default
|
||||
// it deletes the task, but the derived class can change this behaviour and
|
||||
// recycle (re-use) it. Be sure to call Reset() if you recycle it!
|
||||
virtual void RecycleOrDelete() { delete this; }
|
||||
|
||||
// Call this method if you are trying to recycle a Task. Note that only
|
||||
// derived classes should attempt this feat, as a replacement for creating a
|
||||
// new instance.
|
||||
void Reset() {
|
||||
priority_ = 0;
|
||||
delayed_run_time_ = Time();
|
||||
next_task_ = NULL;
|
||||
nestable_ = true;
|
||||
owned_by_message_loop_ = false;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class base::TimerManager; // To check is_owned_by_message_loop().
|
||||
friend class MessageLoop; // To maintain posted_task_delay().
|
||||
|
||||
// Priority for execution by MessageLoop. 0 is default. Higher means run
|
||||
// sooner, and lower (including negative) means run less soon.
|
||||
int priority_;
|
||||
|
||||
// The time when a delayed task should be run.
|
||||
Time delayed_run_time_;
|
||||
|
||||
// When tasks are collected into a queue by MessageLoop, this member is used
|
||||
// to form a null terminated list.
|
||||
Task* next_task_;
|
||||
|
||||
// A nestable task will run in nested message loops, otherwise it will run
|
||||
// only in the top level message loop.
|
||||
bool nestable_;
|
||||
|
||||
// True if this Task is owned by the message loop.
|
||||
bool owned_by_message_loop_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TaskBase);
|
||||
};
|
||||
|
||||
|
||||
// Task ------------------------------------------------------------------------
|
||||
//
|
||||
// A task is a generic runnable thingy, usually used for running code on a
|
||||
// different thread or for scheduling future tasks off of the message loop.
|
||||
|
||||
class Task : public TaskBase {
|
||||
class Task : public tracked_objects::Tracked {
|
||||
public:
|
||||
Task() {}
|
||||
virtual ~Task() {}
|
||||
@ -296,7 +222,6 @@ template<class T>
|
||||
class DeleteTask : public CancelableTask {
|
||||
public:
|
||||
explicit DeleteTask(T* obj) : obj_(obj) {
|
||||
set_nestable(false);
|
||||
}
|
||||
virtual void Run() {
|
||||
delete obj_;
|
||||
@ -313,7 +238,6 @@ template<class T>
|
||||
class ReleaseTask : public CancelableTask {
|
||||
public:
|
||||
explicit ReleaseTask(T* obj) : obj_(obj) {
|
||||
set_nestable(false);
|
||||
}
|
||||
virtual void Run() {
|
||||
if (obj_)
|
||||
|
207
base/timer.cc
207
base/timer.cc
@ -4,217 +4,10 @@
|
||||
|
||||
#include "base/timer.h"
|
||||
|
||||
#include <math.h>
|
||||
#if defined(OS_WIN)
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
|
||||
#include "base/atomic_sequence_num.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/message_loop.h"
|
||||
#include "base/task.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// A sequence number for all allocated times (used to break ties when
|
||||
// comparing times in the TimerManager, and assure FIFO execution sequence).
|
||||
static AtomicSequenceNumber timer_id_counter_(base::LINKER_INITIALIZED);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Timer
|
||||
|
||||
Timer::Timer(int delay, Task* task, bool repeating)
|
||||
: task_(task),
|
||||
delay_(delay),
|
||||
repeating_(repeating) {
|
||||
timer_id_ = timer_id_counter_.GetNext();
|
||||
DCHECK(delay >= 0);
|
||||
Reset();
|
||||
}
|
||||
|
||||
Timer::Timer(Time fire_time, Task* task)
|
||||
: fire_time_(fire_time),
|
||||
task_(task),
|
||||
repeating_(false) {
|
||||
timer_id_ = timer_id_counter_.GetNext();
|
||||
|
||||
// TODO(darin): kill off this stuff. because we are forced to compute 'now'
|
||||
// in order to determine the delay, it is possible that our fire time could
|
||||
// be in the past. /sigh/
|
||||
creation_time_ = Time::Now();
|
||||
delay_ = static_cast<int>((fire_time_ - creation_time_).InMilliseconds());
|
||||
if (delay_ < 0)
|
||||
delay_ = 0;
|
||||
}
|
||||
|
||||
void Timer::Reset() {
|
||||
creation_time_ = Time::Now();
|
||||
fire_time_ = creation_time_ + TimeDelta::FromMilliseconds(delay_);
|
||||
DHISTOGRAM_COUNTS(L"Timer.Durations", delay_);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TimerPQueue
|
||||
|
||||
void TimerPQueue::RemoveTimer(Timer* timer) {
|
||||
const std::vector<Timer*>::iterator location =
|
||||
find(c.begin(), c.end(), timer);
|
||||
if (location != c.end()) {
|
||||
c.erase(location);
|
||||
make_heap(c.begin(), c.end(), TimerComparison());
|
||||
}
|
||||
}
|
||||
|
||||
bool TimerPQueue::ContainsTimer(const Timer* timer) const {
|
||||
return find(c.begin(), c.end(), timer) != c.end();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TimerManager
|
||||
|
||||
TimerManager::TimerManager(MessageLoop* message_loop)
|
||||
: use_broken_delay_(false),
|
||||
message_loop_(message_loop) {
|
||||
#if defined(OS_WIN)
|
||||
// We've experimented with all sorts of timers, and initially tried
|
||||
// to avoid using timeBeginPeriod because it does affect the system
|
||||
// globally. However, after much investigation, it turns out that all
|
||||
// of the major plugins (flash, windows media 9-11, and quicktime)
|
||||
// already use timeBeginPeriod to increase the speed of the clock.
|
||||
// Since the browser must work with these plugins, the browser already
|
||||
// needs to support a fast clock. We may as well use this ourselves,
|
||||
// as it really is the best timer mechanism for our needs.
|
||||
timeBeginPeriod(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
TimerManager::~TimerManager() {
|
||||
#if defined(OS_WIN)
|
||||
// Match timeBeginPeriod() from construction.
|
||||
timeEndPeriod(1);
|
||||
#endif
|
||||
|
||||
// Be nice to unit tests, and discard and delete all timers along with the
|
||||
// embedded task objects by handing off to MessageLoop (which would have Run()
|
||||
// and optionally deleted the objects).
|
||||
while (timers_.size()) {
|
||||
Timer* pending = timers_.top();
|
||||
timers_.pop();
|
||||
message_loop_->DiscardTimer(pending);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Timer* TimerManager::StartTimer(int delay, Task* task, bool repeating) {
|
||||
Timer* t = new Timer(delay, task, repeating);
|
||||
StartTimer(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
void TimerManager::StopTimer(Timer* timer) {
|
||||
// Make sure the timer is actually running.
|
||||
if (!IsTimerRunning(timer))
|
||||
return;
|
||||
// Kill the active timer, and remove the pending entry from the queue.
|
||||
if (timer != timers_.top()) {
|
||||
timers_.RemoveTimer(timer);
|
||||
} else {
|
||||
timers_.pop();
|
||||
DidChangeNextTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void TimerManager::ResetTimer(Timer* timer) {
|
||||
StopTimer(timer);
|
||||
timer->Reset();
|
||||
StartTimer(timer);
|
||||
}
|
||||
|
||||
bool TimerManager::IsTimerRunning(const Timer* timer) const {
|
||||
return timers_.ContainsTimer(timer);
|
||||
}
|
||||
|
||||
Timer* TimerManager::PeekTopTimer() {
|
||||
if (timers_.empty())
|
||||
return NULL;
|
||||
return timers_.top();
|
||||
}
|
||||
|
||||
bool TimerManager::RunSomePendingTimers() {
|
||||
bool did_work = false;
|
||||
// Process a small group of timers. Cap the maximum number of timers we can
|
||||
// process so we don't deny cycles to other parts of the process when lots of
|
||||
// timers have been set.
|
||||
const int kMaxTimersPerCall = 2;
|
||||
for (int i = 0; i < kMaxTimersPerCall; ++i) {
|
||||
if (timers_.empty() || timers_.top()->fire_time() > Time::Now())
|
||||
break;
|
||||
|
||||
// Get a pending timer. Deal with updating the timers_ queue and setting
|
||||
// the TopTimer. We'll execute the timer task only after the timer queue
|
||||
// is back in a consistent state.
|
||||
Timer* pending = timers_.top();
|
||||
|
||||
// If pending task isn't invoked_later, then it must be possible to run it
|
||||
// now (i.e., current task needs to be reentrant).
|
||||
// TODO(jar): We may block tasks that we can queue from being popped.
|
||||
if (!message_loop_->NestableTasksAllowed() &&
|
||||
!pending->task()->owned_by_message_loop_)
|
||||
break;
|
||||
|
||||
timers_.pop();
|
||||
did_work = true;
|
||||
|
||||
// If the timer is repeating, add it back to the list of timers to process.
|
||||
if (pending->repeating()) {
|
||||
pending->Reset();
|
||||
timers_.push(pending);
|
||||
}
|
||||
|
||||
message_loop_->RunTimerTask(pending);
|
||||
}
|
||||
|
||||
// Restart the WM_TIMER (if necessary).
|
||||
if (did_work)
|
||||
DidChangeNextTimer();
|
||||
|
||||
return did_work;
|
||||
}
|
||||
|
||||
// Note: Caller is required to call timer->Reset() before calling StartTimer().
|
||||
// TODO(jar): change API so that Reset() is called as part of StartTimer, making
|
||||
// the API a little less error prone.
|
||||
void TimerManager::StartTimer(Timer* timer) {
|
||||
// Make sure the timer is not running.
|
||||
if (IsTimerRunning(timer))
|
||||
return;
|
||||
|
||||
timers_.push(timer); // Priority queue will sort the timer into place.
|
||||
|
||||
if (timers_.top() == timer) // We are new head of queue.
|
||||
DidChangeNextTimer();
|
||||
}
|
||||
|
||||
Time TimerManager::GetNextFireTime() const {
|
||||
if (timers_.empty())
|
||||
return Time();
|
||||
|
||||
return timers_.top()->fire_time();
|
||||
}
|
||||
|
||||
void TimerManager::DidChangeNextTimer() {
|
||||
// Determine if the next timer expiry actually changed...
|
||||
if (!timers_.empty()) {
|
||||
const Time& expiry = timers_.top()->fire_time();
|
||||
if (expiry == next_timer_expiry_)
|
||||
return;
|
||||
next_timer_expiry_ = expiry;
|
||||
} else {
|
||||
next_timer_expiry_ = Time();
|
||||
}
|
||||
message_loop_->DidChangeNextTimerExpiry();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// BaseTimer_Helper
|
||||
|
||||
|
197
base/timer.h
197
base/timer.h
@ -37,210 +37,17 @@
|
||||
// calling Reset on timer_ would postpone DoStuff by another 1 second. In
|
||||
// other words, Reset is shorthand for calling Stop and then Start again with
|
||||
// the same arguments.
|
||||
//
|
||||
// NOTE: The older TimerManager / Timer API is deprecated. New code should
|
||||
// use OneShotTimer or RepeatingTimer.
|
||||
|
||||
#ifndef BASE_TIMER_H_
|
||||
#define BASE_TIMER_H_
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/task.h"
|
||||
#include "base/time.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Timer/TimerManager are objects designed to help setting timers.
|
||||
// Goals of TimerManager:
|
||||
// - have only one system timer for all app timer functionality
|
||||
// - work around bugs with timers firing arbitrarily earlier than specified
|
||||
// - provide the ability to run timers even if the application is in a
|
||||
// windows modal app loop.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class MessageLoop;
|
||||
|
||||
namespace base {
|
||||
|
||||
class TimerManager;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// The core timer object. Use TimerManager to create and control timers.
|
||||
//
|
||||
// NOTE: This class is DEPRECATED. Do not use!
|
||||
class Timer {
|
||||
public:
|
||||
Timer(int delay, Task* task, bool repeating);
|
||||
|
||||
// For one-shot timers, you can also specify the exact fire time.
|
||||
Timer(Time fire_time, Task* task);
|
||||
|
||||
// The task to be run when the timer fires.
|
||||
Task* task() const { return task_; }
|
||||
void set_task(Task* task) { task_ = task; }
|
||||
|
||||
// Returns the absolute time at which the timer should fire.
|
||||
const Time &fire_time() const { return fire_time_; }
|
||||
|
||||
// A repeating timer is a timer that is automatically scheduled to fire again
|
||||
// after it fires.
|
||||
bool repeating() const { return repeating_; }
|
||||
|
||||
// Update (or fill in) creation_time_, and calculate future fire_time_ based
|
||||
// on current time plus delay_.
|
||||
void Reset();
|
||||
|
||||
// A unique identifier for this timer.
|
||||
int id() const { return timer_id_; }
|
||||
|
||||
protected:
|
||||
// Protected (rather than private) so that we can access from unit tests.
|
||||
|
||||
// The time when the timer should fire.
|
||||
Time fire_time_;
|
||||
|
||||
private:
|
||||
// The task that is run when this timer fires.
|
||||
Task* task_;
|
||||
|
||||
// Timer delay in milliseconds.
|
||||
int delay_;
|
||||
|
||||
// A monotonically increasing timer id. Used for ordering two timers which
|
||||
// have the same timestamp in a FIFO manner.
|
||||
int timer_id_;
|
||||
|
||||
// Whether or not this timer repeats.
|
||||
const bool repeating_;
|
||||
|
||||
// The tick count when the timer was "created". (i.e. when its current
|
||||
// iteration started.)
|
||||
Time creation_time_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Timer);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Used to implement TimerPQueue
|
||||
//
|
||||
// NOTE: This class is DEPRECATED. Do not use!
|
||||
class TimerComparison {
|
||||
public:
|
||||
bool operator() (const Timer* t1, const Timer* t2) const {
|
||||
const Time& f1 = t1->fire_time();
|
||||
const Time& f2 = t2->fire_time();
|
||||
// If the two timers have the same delay, revert to using
|
||||
// the timer_id to maintain FIFO ordering.
|
||||
if (f1 == f2) {
|
||||
// Gracefully handle wrap as we try to return (t1->id() > t2->id());
|
||||
int delta = t1->id() - t2->id();
|
||||
// Assuming the delta is smaller than 2**31, we'll always get the right
|
||||
// answer (in terms of sign of delta).
|
||||
return delta > 0;
|
||||
}
|
||||
return f1 > f2;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Subclass priority_queue to provide convenient access to removal from this
|
||||
// list.
|
||||
//
|
||||
// Terminology: The "pending" timer is the timer at the top of the queue,
|
||||
// i.e. the timer whose task needs to be Run next.
|
||||
//
|
||||
// NOTE: This class is DEPRECATED. Do not use!
|
||||
class TimerPQueue :
|
||||
public std::priority_queue<Timer*, std::vector<Timer*>, TimerComparison> {
|
||||
public:
|
||||
// Removes |timer| from the queue.
|
||||
void RemoveTimer(Timer* timer);
|
||||
|
||||
// Returns true if the queue contains |timer|.
|
||||
bool ContainsTimer(const Timer* timer) const;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// There is one TimerManager per thread, owned by the MessageLoop. Timers can
|
||||
// either be fired by the MessageLoop from within its run loop or via a system
|
||||
// timer event that the MesssageLoop constructs. The advantage of the former
|
||||
// is that we can make timers fire significantly faster than the granularity
|
||||
// provided by the system. The advantage of a system timer is that modal
|
||||
// message loops which don't run our MessageLoop code will still be able to
|
||||
// process system timer events.
|
||||
//
|
||||
// NOTE: TimerManager is not thread safe. You cannot set timers onto a thread
|
||||
// other than your own.
|
||||
//
|
||||
// NOTE: This class is DEPRECATED. Do not use!
|
||||
class TimerManager {
|
||||
public:
|
||||
explicit TimerManager(MessageLoop* message_loop);
|
||||
~TimerManager();
|
||||
|
||||
// Create and start a new timer. |task| is owned by the caller, as is the
|
||||
// timer object that is returned.
|
||||
Timer* StartTimer(int delay, Task* task, bool repeating);
|
||||
|
||||
// Starts a timer. This is a no-op if the timer is already started.
|
||||
void StartTimer(Timer* timer);
|
||||
|
||||
// Stop a timer. This is a no-op if the timer is already stopped.
|
||||
void StopTimer(Timer* timer);
|
||||
|
||||
// Reset an existing timer, which may or may not be currently in the queue of
|
||||
// upcoming timers. The timer's parameters are unchanged; it simply begins
|
||||
// counting down again as if it was just created.
|
||||
void ResetTimer(Timer* timer);
|
||||
|
||||
// Returns true if |timer| is in the queue of upcoming timers.
|
||||
bool IsTimerRunning(const Timer* timer) const;
|
||||
|
||||
// Run some small number of timers.
|
||||
// Returns true if it runs a task, false otherwise.
|
||||
bool RunSomePendingTimers();
|
||||
|
||||
// The absolute time at which the next timer is to fire. If there is not a
|
||||
// next timer to run, then the is_null property of the returned Time object
|
||||
// will be true. NOTE: This could be a time in the past!
|
||||
Time GetNextFireTime() const;
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
// For testing only, used to simulate broken early-firing WM_TIMER
|
||||
// notifications by setting arbitrarily small delays in SetTimer.
|
||||
void set_use_broken_delay(bool use_broken_delay) {
|
||||
use_broken_delay_ = use_broken_delay;
|
||||
}
|
||||
#endif // UNIT_TEST
|
||||
|
||||
bool use_broken_delay() const {
|
||||
return use_broken_delay_;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Peek at the timer which will fire soonest.
|
||||
Timer* PeekTopTimer();
|
||||
|
||||
private:
|
||||
void DidChangeNextTimer();
|
||||
|
||||
// A cached value that indicates the time when we think the next timer is to
|
||||
// fire. We use this to determine if we should call DidChangeNextTimerExpiry
|
||||
// on the MessageLoop.
|
||||
Time next_timer_expiry_;
|
||||
|
||||
TimerPQueue timers_;
|
||||
|
||||
bool use_broken_delay_;
|
||||
|
||||
// A lazily cached copy of MessageLoop::current.
|
||||
MessageLoop* message_loop_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TimerManager);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This class is an implementation detail of OneShotTimer and RepeatingTimer.
|
||||
// Please do not use this class directly.
|
||||
@ -353,8 +160,4 @@ class RepeatingTimer : public BaseTimer<Receiver, true> {};
|
||||
|
||||
} // namespace base
|
||||
|
||||
// TODO(darin): b/1346553: Remove these once Timer and TimerManager are unused.
|
||||
using base::Timer;
|
||||
using base::TimerManager;
|
||||
|
||||
#endif // BASE_TIMER_H_
|
||||
|
@ -7,324 +7,6 @@
|
||||
#include "base/timer.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
using base::TimerComparison;
|
||||
|
||||
namespace {
|
||||
|
||||
class TimerTest : public testing::Test {};
|
||||
|
||||
// A base class timer task that sanity-checks timer functionality and counts
|
||||
// the number of times it has run. Handles all message loop and memory
|
||||
// management issues.
|
||||
class TimerTask : public Task {
|
||||
public:
|
||||
// Runs all timers to completion. This returns only after all timers have
|
||||
// finished firing.
|
||||
static void RunTimers();
|
||||
|
||||
// Creates a new timer. If |repeating| is true, the timer will repeat 10
|
||||
// times before terminating.
|
||||
//
|
||||
// All timers are managed on the message loop of the thread that calls this
|
||||
// function the first time.
|
||||
TimerTask(int delay, bool repeating);
|
||||
|
||||
virtual ~TimerTask();
|
||||
|
||||
int iterations() const { return iterations_; }
|
||||
const Timer* timer() const { return timer_; }
|
||||
|
||||
// Resets the timer, if it exists.
|
||||
void Reset();
|
||||
|
||||
// Task
|
||||
virtual void Run();
|
||||
|
||||
protected:
|
||||
// Shuts down the message loop if necessary.
|
||||
static void QuitMessageLoop();
|
||||
|
||||
private:
|
||||
static MessageLoop* message_loop() {
|
||||
return MessageLoop::current();
|
||||
}
|
||||
|
||||
static int timer_count_;
|
||||
static bool loop_running_;
|
||||
|
||||
bool timer_running_;
|
||||
int delay_;
|
||||
TimeTicks start_ticks_;
|
||||
int iterations_;
|
||||
Timer* timer_;
|
||||
};
|
||||
|
||||
// static
|
||||
void TimerTask::RunTimers() {
|
||||
if (timer_count_ && !loop_running_) {
|
||||
loop_running_ = true;
|
||||
message_loop()->Run();
|
||||
}
|
||||
}
|
||||
|
||||
TimerTask::TimerTask(int delay, bool repeating)
|
||||
: timer_running_(false),
|
||||
delay_(delay),
|
||||
start_ticks_(TimeTicks::Now()),
|
||||
iterations_(0),
|
||||
timer_(NULL) {
|
||||
Reset(); // This will just set up the variables to indicate we have a
|
||||
// running timer.
|
||||
timer_ = message_loop()->timer_manager_deprecated()->StartTimer(
|
||||
delay, this, repeating);
|
||||
}
|
||||
|
||||
TimerTask::~TimerTask() {
|
||||
if (timer_) {
|
||||
message_loop()->timer_manager_deprecated()->StopTimer(timer_);
|
||||
delete timer_;
|
||||
}
|
||||
if (timer_running_) {
|
||||
timer_running_ = false;
|
||||
if (--timer_count_ <= 0)
|
||||
QuitMessageLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void TimerTask::Reset() {
|
||||
if (!timer_running_) {
|
||||
timer_running_ = true;
|
||||
++timer_count_;
|
||||
}
|
||||
if (timer_) {
|
||||
start_ticks_ = TimeTicks::Now();
|
||||
message_loop()->timer_manager_deprecated()->ResetTimer(timer_);
|
||||
}
|
||||
}
|
||||
|
||||
void TimerTask::Run() {
|
||||
++iterations_;
|
||||
|
||||
// Test that we fired on or after the delay, not before.
|
||||
const TimeTicks ticks = TimeTicks::Now();
|
||||
EXPECT_LE(delay_, (ticks - start_ticks_).InMilliseconds());
|
||||
// Note: Add the delay rather than using the ticks recorded.
|
||||
// Repeating timers have already started ticking before
|
||||
// this callback; we pretend they started *now*, then
|
||||
// it might seem like they fire early, when they do not.
|
||||
start_ticks_ += TimeDelta::FromMilliseconds(delay_);
|
||||
|
||||
// If we're done running, shut down the message loop.
|
||||
if (timer_->repeating() && (iterations_ < 10))
|
||||
return; // Iterate 10 times before terminating.
|
||||
message_loop()->timer_manager_deprecated()->StopTimer(timer_);
|
||||
timer_running_ = false;
|
||||
if (--timer_count_ <= 0)
|
||||
QuitMessageLoop();
|
||||
}
|
||||
|
||||
// static
|
||||
void TimerTask::QuitMessageLoop() {
|
||||
if (loop_running_) {
|
||||
message_loop()->Quit();
|
||||
loop_running_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
int TimerTask::timer_count_ = 0;
|
||||
bool TimerTask::loop_running_ = false;
|
||||
|
||||
// A task that deletes itself when run.
|
||||
class DeletingTask : public TimerTask {
|
||||
public:
|
||||
DeletingTask(int delay, bool repeating) : TimerTask(delay, repeating) { }
|
||||
|
||||
// Task
|
||||
virtual void Run();
|
||||
};
|
||||
|
||||
void DeletingTask::Run() {
|
||||
delete this;
|
||||
|
||||
// Can't call TimerTask::Run() here, we've destroyed ourselves.
|
||||
}
|
||||
|
||||
// A class that resets another TimerTask when run.
|
||||
class ResettingTask : public TimerTask {
|
||||
public:
|
||||
ResettingTask(int delay, bool repeating, TimerTask* task)
|
||||
: TimerTask(delay, repeating),
|
||||
task_(task) {
|
||||
}
|
||||
|
||||
virtual void Run();
|
||||
|
||||
private:
|
||||
TimerTask* task_;
|
||||
};
|
||||
|
||||
void ResettingTask::Run() {
|
||||
task_->Reset();
|
||||
|
||||
TimerTask::Run();
|
||||
}
|
||||
|
||||
// A class that quits the message loop when run.
|
||||
class QuittingTask : public TimerTask {
|
||||
public:
|
||||
QuittingTask(int delay, bool repeating) : TimerTask(delay, repeating) { }
|
||||
|
||||
virtual void Run();
|
||||
};
|
||||
|
||||
void QuittingTask::Run() {
|
||||
QuitMessageLoop();
|
||||
|
||||
TimerTask::Run();
|
||||
}
|
||||
|
||||
void RunTimerTest() {
|
||||
// Make sure oneshot timers work correctly.
|
||||
TimerTask task1(100, false);
|
||||
TimerTask::RunTimers();
|
||||
EXPECT_EQ(1, task1.iterations());
|
||||
|
||||
// Make sure repeating timers work correctly.
|
||||
TimerTask task2(10, true);
|
||||
TimerTask task3(100, true);
|
||||
TimerTask::RunTimers();
|
||||
EXPECT_EQ(10, task2.iterations());
|
||||
EXPECT_EQ(10, task3.iterations());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// The timer test cases:
|
||||
|
||||
void RunTest_TimerComparison(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Make sure TimerComparison sorts correctly.
|
||||
const TimerTask task1(10, false);
|
||||
const Timer* timer1 = task1.timer();
|
||||
const TimerTask task2(200, false);
|
||||
const Timer* timer2 = task2.timer();
|
||||
TimerComparison comparison;
|
||||
EXPECT_FALSE(comparison(timer1, timer2));
|
||||
EXPECT_TRUE(comparison(timer2, timer1));
|
||||
}
|
||||
|
||||
void RunTest_BasicTimer(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
RunTimerTest();
|
||||
}
|
||||
|
||||
void RunTest_BrokenTimer(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Simulate faulty early-firing timers. The tasks in RunTimerTest should
|
||||
// nevertheless be invoked after their specified delays, regardless of when
|
||||
// WM_TIMER fires.
|
||||
TimerManager* manager = MessageLoop::current()->timer_manager_deprecated();
|
||||
manager->set_use_broken_delay(true);
|
||||
RunTimerTest();
|
||||
manager->set_use_broken_delay(false);
|
||||
}
|
||||
|
||||
void RunTest_DeleteFromRun(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Make sure TimerManager correctly handles a Task that deletes itself when
|
||||
// run.
|
||||
new DeletingTask(50, true);
|
||||
TimerTask timer_task(150, false);
|
||||
new DeletingTask(250, true);
|
||||
TimerTask::RunTimers();
|
||||
EXPECT_EQ(1, timer_task.iterations());
|
||||
}
|
||||
|
||||
void RunTest_Reset(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Make sure resetting a timer after it has fired works.
|
||||
TimerTask timer_task1(250, false);
|
||||
TimerTask timer_task2(100, true);
|
||||
ResettingTask resetting_task1(600, false, &timer_task1);
|
||||
TimerTask::RunTimers();
|
||||
EXPECT_EQ(2, timer_task1.iterations());
|
||||
EXPECT_EQ(10, timer_task2.iterations());
|
||||
|
||||
// Make sure resetting a timer before it has fired works. This will reset
|
||||
// two timers, then stop the message loop between when they should have
|
||||
// finally fired.
|
||||
TimerTask timer_task3(100, false);
|
||||
TimerTask timer_task4(600, false);
|
||||
ResettingTask resetting_task3(50, false, &timer_task3);
|
||||
ResettingTask resetting_task4(50, false, &timer_task4);
|
||||
QuittingTask quitting_task(300, false);
|
||||
TimerTask::RunTimers();
|
||||
EXPECT_EQ(1, timer_task3.iterations());
|
||||
EXPECT_EQ(0, timer_task4.iterations());
|
||||
}
|
||||
|
||||
void RunTest_FifoOrder(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
// Creating timers with the same timeout should
|
||||
// always compare to result in FIFO ordering.
|
||||
|
||||
// Derive from the timer so that we can set it's fire time.
|
||||
// We have to do this, because otherwise, it's possible for
|
||||
// two timers, created back to back, to have different times,
|
||||
// and in that case, we aren't really testing what we want
|
||||
// to test!
|
||||
class MockTimer : public Timer {
|
||||
public:
|
||||
MockTimer(int delay) : Timer(delay, NULL, false) {}
|
||||
void set_fire_time(const Time& t) { fire_time_ = t; }
|
||||
};
|
||||
|
||||
class MockTimerManager : public TimerManager {
|
||||
public:
|
||||
MockTimerManager() : TimerManager(MessageLoop::current()) {
|
||||
}
|
||||
|
||||
// Pops the most-recent to fire timer and returns its timer id.
|
||||
// Returns -1 if there are no timers in the list.
|
||||
int pop() {
|
||||
int rv = -1;
|
||||
Timer* top = PeekTopTimer();
|
||||
if (top) {
|
||||
rv = top->id();
|
||||
StopTimer(top);
|
||||
delete top;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
};
|
||||
|
||||
MockTimer t1(0);
|
||||
MockTimer t2(0);
|
||||
t2.set_fire_time(t1.fire_time());
|
||||
TimerComparison comparison;
|
||||
EXPECT_TRUE(comparison(&t2, &t1));
|
||||
|
||||
// Issue a tight loop of timers; most will have the
|
||||
// same timestamp; some will not. Either way, since
|
||||
// all are created with delay(0), the second timer
|
||||
// must always be greater than the first. Then, pop
|
||||
// all the timers and verify that it's a FIFO list.
|
||||
MockTimerManager manager;
|
||||
const int kNumTimers = 1024;
|
||||
for (int i=0; i < kNumTimers; i++)
|
||||
manager.StartTimer(0, NULL, false);
|
||||
|
||||
int last_id = -1;
|
||||
int new_id = 0;
|
||||
while((new_id = manager.pop()) > 0)
|
||||
EXPECT_GT(new_id, last_id);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class OneShotTimerTester {
|
||||
@ -364,8 +46,6 @@ class RepeatingTimerTester {
|
||||
base::RepeatingTimer<RepeatingTimerTester> timer_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void RunTest_OneShotTimer(MessageLoop::Type message_loop_type) {
|
||||
MessageLoop loop(message_loop_type);
|
||||
|
||||
@ -440,42 +120,6 @@ void RunTest_RepeatingTimer_Cancel(MessageLoop::Type message_loop_type) {
|
||||
// Each test is run against each type of MessageLoop. That way we are sure
|
||||
// that timers work properly in all configurations.
|
||||
|
||||
TEST(TimerTest, TimerComparison) {
|
||||
RunTest_TimerComparison(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_TimerComparison(MessageLoop::TYPE_UI);
|
||||
RunTest_TimerComparison(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(TimerTest, BasicTimer) {
|
||||
RunTest_BasicTimer(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_BasicTimer(MessageLoop::TYPE_UI);
|
||||
RunTest_BasicTimer(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(TimerTest, BrokenTimer) {
|
||||
RunTest_BrokenTimer(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_BrokenTimer(MessageLoop::TYPE_UI);
|
||||
RunTest_BrokenTimer(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(TimerTest, DeleteFromRun) {
|
||||
RunTest_DeleteFromRun(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_DeleteFromRun(MessageLoop::TYPE_UI);
|
||||
RunTest_DeleteFromRun(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(TimerTest, Reset) {
|
||||
RunTest_Reset(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_Reset(MessageLoop::TYPE_UI);
|
||||
RunTest_Reset(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(TimerTest, FifoOrder) {
|
||||
RunTest_FifoOrder(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_FifoOrder(MessageLoop::TYPE_UI);
|
||||
RunTest_FifoOrder(MessageLoop::TYPE_IO);
|
||||
}
|
||||
|
||||
TEST(TimerTest, OneShotTimer) {
|
||||
RunTest_OneShotTimer(MessageLoop::TYPE_DEFAULT);
|
||||
RunTest_OneShotTimer(MessageLoop::TYPE_UI);
|
||||
|
@ -108,6 +108,8 @@ class Tracked {
|
||||
bool MissingBirthplace() const;
|
||||
|
||||
private:
|
||||
#ifdef TRACK_ALL_TASK_OBJECTS
|
||||
|
||||
// Pointer to instance were counts of objects with the same birth location
|
||||
// (on the same thread) are stored.
|
||||
Births* tracked_births_;
|
||||
@ -116,6 +118,8 @@ class Tracked {
|
||||
// reset before the object begins it active life.
|
||||
Time tracked_birth_time_;
|
||||
|
||||
#endif // TRACK_ALL_TASK_OBJECTS
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Tracked);
|
||||
};
|
||||
|
||||
|
@ -51,17 +51,15 @@ TEST_F(TrackedObjectsTest, MinimalStartupShutdown) {
|
||||
ThreadData::ShutdownSingleThreadedCleanup();
|
||||
}
|
||||
|
||||
class NoopTask : public Task {
|
||||
public:
|
||||
void Run() {}
|
||||
class NoopTracked : public tracked_objects::Tracked {
|
||||
};
|
||||
|
||||
TEST_F(TrackedObjectsTest, TinyStartupShutdown) {
|
||||
if (!ThreadData::StartTracking(true))
|
||||
return;
|
||||
|
||||
// Instigate tracking on a single task, or our thread.
|
||||
NoopTask task;
|
||||
// Instigate tracking on a single tracked object, or our thread.
|
||||
NoopTracked tracked;
|
||||
|
||||
const ThreadData* data = ThreadData::first();
|
||||
EXPECT_TRUE(data);
|
||||
@ -77,7 +75,7 @@ TEST_F(TrackedObjectsTest, TinyStartupShutdown) {
|
||||
|
||||
|
||||
// Now instigate a birth, and a death.
|
||||
delete new NoopTask;
|
||||
delete new NoopTracked;
|
||||
|
||||
birth_map.clear();
|
||||
data->SnapshotBirthMap(&birth_map);
|
||||
|
@ -53,6 +53,7 @@ void* PThreadCallback(void* param) {
|
||||
bool WorkerPool::PostTask(const tracked_objects::Location& from_here,
|
||||
Task* task, bool task_is_slow) {
|
||||
task->SetBirthPlace(from_here);
|
||||
|
||||
pthread_t thread;
|
||||
pthread_attr_t attr;
|
||||
|
||||
|
Reference in New Issue
Block a user