summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authordarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-09-07 08:08:29 +0000
committerdarin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-09-07 08:08:29 +0000
commit752578567cc199568f0522fd0a95589d5cc822fc (patch)
treed716dac10bc6718da7ebd006d9eac39720cf3e2a /base
parent8c3f250cd0044b40992a1926cb257baa1318fe75 (diff)
downloadchromium_src-752578567cc199568f0522fd0a95589d5cc822fc.zip
chromium_src-752578567cc199568f0522fd0a95589d5cc822fc.tar.gz
chromium_src-752578567cc199568f0522fd0a95589d5cc822fc.tar.bz2
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
Diffstat (limited to 'base')
-rw-r--r--base/message_loop.cc340
-rw-r--r--base/message_loop.h172
-rw-r--r--base/message_loop_unittest.cc192
-rw-r--r--base/task.h78
-rw-r--r--base/timer.cc207
-rw-r--r--base/timer.h197
-rw-r--r--base/timer_unittest.cc356
-rw-r--r--base/tracked.h4
-rw-r--r--base/tracked_objects_unittest.cc10
-rw-r--r--base/worker_pool.cc1
10 files changed, 393 insertions, 1164 deletions
diff --git a/base/message_loop.cc b/base/message_loop.cc
index 410394f..181d76f 100644
--- a/base/message_loop.cc
+++ b/base/message_loop.cc
@@ -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.
+void MessageLoop::RunTask(Task* task) {
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;
-}
+ // Execute the task and assume the worst: It is probably not reentrant.
+ nestable_tasks_allowed_ = 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();
-}
+ delete task;
-void MessageLoop::BeforeTaskRunSetup() {
- DCHECK(nestable_tasks_allowed_);
- // Execute the task and assume the worst: It is probably not reentrant.
- nestable_tasks_allowed_ = false;
+ 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);
+bool MessageLoop::DoWork() {
+ if (!nestable_tasks_allowed_) {
+ // Task can't be executed right now.
+ return false;
+ }
- pump_->ScheduleDelayedWork(next_delayed_work_time);
-}
+ 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());
+ }
-bool MessageLoop::DoWork() {
- ReloadWorkQueue();
- return QueueOrRunTask(NULL);
+ // 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
-
-void MessageLoop::PrioritizedTaskQueue::push(Task * task) {
- queue_.push(PrioritizedTask(task, --next_sequence_number_));
-}
+// MessageLoop::PendingTask
-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;
-}
+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.
-//------------------------------------------------------------------------------
-// Implementation of a TaskQueue as a null terminated list, with end pointers.
-
-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());
- }
- }
-}
+ if (delayed_run_time < other.delayed_run_time)
+ return false;
-Task* MessageLoop::OptionallyPrioritizedTaskQueue::Pop() {
- if (!use_priority_queue_)
- return queue_.Pop();
- Task* task = prioritized_queue_.front();
- prioritized_queue_.pop();
- return task;
-}
+ if (delayed_run_time > other.delayed_run_time)
+ return true;
-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;
}
//------------------------------------------------------------------------------
diff --git a/base/message_loop.h b/base/message_loop.h
index babc17a..53c832a 100644
--- a/base/message_loop.h
+++ b/base/message_loop.h
@@ -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);
- };
-
- // 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_;
+ 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 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);
};
diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc
index 4fcde2f..b36090a6d 100644
--- a/base/message_loop_unittest.cc
+++ b/base/message_loop_unittest.cc
@@ -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);
diff --git a/base/task.h b/base/task.h
index 99bec4e..8db4560 100644
--- a/base/task.h
+++ b/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_)
diff --git a/base/timer.cc b/base/timer.cc
index 9f6e7b0..7fac059 100644
--- a/base/timer.cc
+++ b/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
diff --git a/base/timer.h b/base/timer.h
index e1a77e2..32adbf1 100644
--- a/base/timer.h
+++ b/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_
diff --git a/base/timer_unittest.cc b/base/timer_unittest.cc
index b48cfd6..a9d13e2 100644
--- a/base/timer_unittest.cc
+++ b/base/timer_unittest.cc
@@ -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);
diff --git a/base/tracked.h b/base/tracked.h
index dbcc106..f976175 100644
--- a/base/tracked.h
+++ b/base/tracked.h
@@ -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);
};
diff --git a/base/tracked_objects_unittest.cc b/base/tracked_objects_unittest.cc
index 4fececd..29827fc 100644
--- a/base/tracked_objects_unittest.cc
+++ b/base/tracked_objects_unittest.cc
@@ -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);
diff --git a/base/worker_pool.cc b/base/worker_pool.cc
index b2c8c2b..ae513e1 100644
--- a/base/worker_pool.cc
+++ b/base/worker_pool.cc
@@ -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;