diff options
author | darin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-09-07 08:08:29 +0000 |
---|---|---|
committer | darin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-09-07 08:08:29 +0000 |
commit | 752578567cc199568f0522fd0a95589d5cc822fc (patch) | |
tree | d716dac10bc6718da7ebd006d9eac39720cf3e2a | |
parent | 8c3f250cd0044b40992a1926cb257baa1318fe75 (diff) | |
download | chromium_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
-rw-r--r-- | base/message_loop.cc | 340 | ||||
-rw-r--r-- | base/message_loop.h | 172 | ||||
-rw-r--r-- | base/message_loop_unittest.cc | 192 | ||||
-rw-r--r-- | base/task.h | 78 | ||||
-rw-r--r-- | base/timer.cc | 207 | ||||
-rw-r--r-- | base/timer.h | 197 | ||||
-rw-r--r-- | base/timer_unittest.cc | 356 | ||||
-rw-r--r-- | base/tracked.h | 4 | ||||
-rw-r--r-- | base/tracked_objects_unittest.cc | 10 | ||||
-rw-r--r-- | base/worker_pool.cc | 1 |
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; |