0

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:
darin@google.com
2008-09-07 08:08:29 +00:00
parent 8c3f250cd0
commit 752578567c
10 changed files with 395 additions and 1166 deletions

@ -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);

@ -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_)

@ -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

@ -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;