diff options
Diffstat (limited to 'base/message_loop.cc')
-rw-r--r-- | base/message_loop.cc | 340 |
1 files changed, 140 insertions, 200 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; } //------------------------------------------------------------------------------ |