Index: base/message_loop.cc |
=================================================================== |
--- base/message_loop.cc (revision 1824) |
+++ base/message_loop.cc (working copy) |
@@ -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 @@ |
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 @@ |
DeletePendingTasks(); |
ReloadWorkQueue(); |
DeletePendingTasks(); |
+ |
+#if defined(OS_WIN) |
+ // Match timeBeginPeriod() from construction. |
+ timeEndPeriod(1); |
+#endif |
} |
void MessageLoop::AddDestructionObserver(DestructionObserver *obs) { |
@@ -162,10 +184,13 @@ |
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::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 @@ |
{ |
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::RunTimerTask(Timer* timer) { |
- HistogramEvent(kTimerEvent); |
+void MessageLoop::RunTask(Task* task) { |
+ DCHECK(nestable_tasks_allowed_); |
+ // Execute the task and assume the worst: It is probably not reentrant. |
+ nestable_tasks_allowed_ = false; |
- 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); |
- } |
+ HistogramEvent(kTaskRunEvent); |
+ task->Run(); |
+ delete task; |
- // This is an unknown timer task, and we *can't* delay running it, as a user |
- // might try to cancel it with TimerManager at any moment. |
- DCHECK(nestable_tasks_allowed_); |
- RunTask(task); |
- return true; |
+ nestable_tasks_allowed_ = 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::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; |
} |
-} |
-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. |
+ // 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::RunTask(Task* task) { |
- BeforeTaskRunSetup(); |
- HistogramEvent(kTaskRunEvent); |
- // task may self-delete during Run() if we don't happen to own it. |
- // ...so check *before* we Run, since we can't check after. |
- bool we_own_task = task->owned_by_message_loop_; |
- task->Run(); |
- if (we_own_task) |
- task->RecycleOrDelete(); // Relinquish control, and probably delete. |
- AfterTaskRunRestore(); |
-} |
- |
-void MessageLoop::BeforeTaskRunSetup() { |
- DCHECK(nestable_tasks_allowed_); |
- // Execute the task and assume the worst: It is probably not reentrant. |
- nestable_tasks_allowed_ = false; |
-} |
- |
-void MessageLoop::AfterTaskRunRestore() { |
- nestable_tasks_allowed_ = true; |
-} |
- |
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::DidChangeNextTimerExpiry() { |
- Time next_delayed_work_time = timer_manager_.GetNextFireTime(); |
- if (next_delayed_work_time.is_null()) |
- return; |
+bool MessageLoop::DoWork() { |
+ if (!nestable_tasks_allowed_) { |
+ // Task can't be executed right now. |
+ return false; |
+ } |
- // 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); |
+ for (;;) { |
+ ReloadWorkQueue(); |
+ if (work_queue_.empty()) |
+ break; |
- pump_->ScheduleDelayedWork(next_delayed_work_time); |
-} |
+ // 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(); |
-bool MessageLoop::DoWork() { |
- ReloadWorkQueue(); |
- return QueueOrRunTask(NULL); |
+ // Move to the delayed work queue. Initialize the sequence number |
+ // before inserting into the delayed_work_queue_. The sequence number |
+ // is used to faciliate FIFO sorting when two tasks have the same |
+ // delayed_run_time value. |
+ pending_task.sequence_num = next_sequence_num_++; |
+ delayed_work_queue_.push(pending_task); |
+ |
+ if (was_empty) // We only schedule the next delayed work item. |
+ pump_->ScheduleDelayedWork(pending_task.delayed_run_time); |
+ } else { |
+ if (DeferOrRunPendingTask(pending_task)) |
+ return true; |
+ } |
+ } while (!work_queue_.empty()); |
+ } |
+ |
+ // Nothing happened. |
+ return false; |
} |
bool MessageLoop::DoDelayedWork(Time* next_delayed_work_time) { |
- bool did_work = timer_manager_.RunSomePendingTimers(); |
+ if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) { |
+ *next_delayed_work_time = Time(); |
+ return false; |
+ } |
+ |
+ if (delayed_work_queue_.top().delayed_run_time > Time::Now()) { |
+ *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; |
+ return false; |
+ } |
- // We may not have run any timers, but we may still have future timers to |
- // run, so we need to inform the pump again of pending timers. |
- *next_delayed_work_time = timer_manager_.GetNextFireTime(); |
+ PendingTask pending_task = delayed_work_queue_.top(); |
+ delayed_work_queue_.pop(); |
+ |
+ if (!delayed_work_queue_.empty()) |
+ *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; |
- return did_work; |
+ return DeferOrRunPendingTask(pending_task); |
} |
bool MessageLoop::DoIdleWork() { |
@@ -434,83 +432,25 @@ |
} |
//------------------------------------------------------------------------------ |
-// Implementation of the work_queue_ as a ProiritizedTaskQueue |
+// MessageLoop::PendingTask |
-void MessageLoop::PrioritizedTaskQueue::push(Task * task) { |
- queue_.push(PrioritizedTask(task, --next_sequence_number_)); |
-} |
+bool MessageLoop::PendingTask::operator<(const PendingTask& other) const { |
+ // Since the top of a priority queue is defined as the "greatest" element, we |
+ // need to invert the comparison here. We want the smaller time to be at the |
+ // top of the heap. |
-bool MessageLoop::PrioritizedTaskQueue::PrioritizedTask::operator < ( |
- PrioritizedTask const & right) const { |
- int compare = task_->priority() - right.task_->priority(); |
- if (compare) |
- return compare < 0; |
- // Don't compare directly, but rather subtract. This handles overflow |
- // as sequence numbers wrap around. |
- compare = sequence_number_ - right.sequence_number_; |
- DCHECK(compare); // Sequence number are unique for a "long time." |
- // Make sure we don't starve anything with a low priority. |
- CHECK(INT_MAX/8 > compare); // We don't get close to wrapping. |
- CHECK(INT_MIN/8 < compare); // We don't get close to wrapping. |
- return compare < 0; |
-} |
+ if (delayed_run_time < other.delayed_run_time) |
+ return false; |
-//------------------------------------------------------------------------------ |
-// Implementation of a TaskQueue as a null terminated list, with end pointers. |
+ if (delayed_run_time > other.delayed_run_time) |
+ return true; |
-void MessageLoop::TaskQueue::Push(Task* task) { |
- if (!first_) |
- first_ = task; |
- else |
- last_->set_next_task(task); |
- last_ = task; |
+ // 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; |
} |
-Task* MessageLoop::TaskQueue::Pop() { |
- DCHECK((!first_) == !last_); |
- Task* task = first_; |
- if (first_) { |
- first_ = task->next_task(); |
- if (!first_) |
- last_ = NULL; |
- else |
- task->set_next_task(NULL); |
- } |
- return task; |
-} |
- |
//------------------------------------------------------------------------------ |
-// Implementation of a Task queue that automatically switches into a priority |
-// queue if it observes any non-zero priorities on tasks. |
- |
-void MessageLoop::OptionallyPrioritizedTaskQueue::Push(Task* task) { |
- if (use_priority_queue_) { |
- prioritized_queue_.push(task); |
- } else { |
- queue_.Push(task); |
- if (task->priority()) { |
- use_priority_queue_ = true; // From now on. |
- while (!queue_.Empty()) |
- prioritized_queue_.push(queue_.Pop()); |
- } |
- } |
-} |
- |
-Task* MessageLoop::OptionallyPrioritizedTaskQueue::Pop() { |
- if (!use_priority_queue_) |
- return queue_.Pop(); |
- Task* task = prioritized_queue_.front(); |
- prioritized_queue_.pop(); |
- return task; |
-} |
- |
-bool MessageLoop::OptionallyPrioritizedTaskQueue::Empty() { |
- if (use_priority_queue_) |
- return prioritized_queue_.empty(); |
- return queue_.Empty(); |
-} |
- |
-//------------------------------------------------------------------------------ |
// Method and data for histogramming events and actions taken by each instance |
// on each thread. |