michael@0: // Copyright (c) 2009 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "base/message_loop.h" michael@0: michael@0: #include michael@0: michael@0: #include "mozilla/Atomics.h" michael@0: #include "base/compiler_specific.h" michael@0: #include "base/lazy_instance.h" michael@0: #include "base/logging.h" michael@0: #include "base/message_pump_default.h" michael@0: #include "base/string_util.h" michael@0: #include "base/thread_local.h" michael@0: michael@0: #if defined(OS_MACOSX) michael@0: #include "base/message_pump_mac.h" michael@0: #endif michael@0: #if defined(OS_POSIX) michael@0: #include "base/message_pump_libevent.h" michael@0: #endif michael@0: #if defined(OS_LINUX) || defined(OS_BSD) michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: #include "base/message_pump_glib.h" michael@0: #endif michael@0: #ifdef MOZ_WIDGET_QT michael@0: #include "base/message_pump_qt.h" michael@0: #endif michael@0: #endif michael@0: #ifdef ANDROID michael@0: #include "base/message_pump_android.h" michael@0: #endif michael@0: #ifdef MOZ_TASK_TRACER michael@0: #include "GeckoTaskTracer.h" michael@0: #endif michael@0: michael@0: #include "MessagePump.h" michael@0: michael@0: using base::Time; michael@0: using base::TimeDelta; michael@0: using base::TimeTicks; michael@0: michael@0: // A lazily created thread local storage for quick access to a thread's message michael@0: // loop, if one exists. This should be safe and free of static constructors. michael@0: static base::LazyInstance > lazy_tls_ptr( michael@0: base::LINKER_INITIALIZED); michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: // Logical events for Histogram profiling. Run with -message-loop-histogrammer michael@0: // to get an accounting of messages and actions taken on each thread. michael@0: static const int kTaskRunEvent = 0x1; michael@0: static const int kTimerEvent = 0x2; michael@0: michael@0: // Provide range of message IDs for use in histogramming and debug display. michael@0: static const int kLeastNonZeroMessageId = 1; michael@0: static const int kMaxMessageId = 1099; michael@0: static const int kNumberOfDistinctMessagesDisplayed = 1100; michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: #if defined(OS_WIN) michael@0: michael@0: // Upon a SEH exception in this thread, it restores the original unhandled michael@0: // exception filter. michael@0: static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) { michael@0: ::SetUnhandledExceptionFilter(old_filter); michael@0: return EXCEPTION_CONTINUE_SEARCH; michael@0: } michael@0: michael@0: // Retrieves a pointer to the current unhandled exception filter. There michael@0: // is no standalone getter method. michael@0: static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() { michael@0: LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL; michael@0: top_filter = ::SetUnhandledExceptionFilter(0); michael@0: ::SetUnhandledExceptionFilter(top_filter); michael@0: return top_filter; michael@0: } michael@0: michael@0: #endif // defined(OS_WIN) michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: // static michael@0: MessageLoop* MessageLoop::current() { michael@0: // TODO(darin): sadly, we cannot enable this yet since people call us even michael@0: // when they have no intention of using us. michael@0: //DCHECK(loop) << "Ouch, did you forget to initialize me?"; michael@0: return lazy_tls_ptr.Pointer()->Get(); michael@0: } michael@0: michael@0: static mozilla::Atomic message_loop_id_seq(0); michael@0: michael@0: MessageLoop::MessageLoop(Type type) michael@0: : type_(type), michael@0: id_(++message_loop_id_seq), michael@0: nestable_tasks_allowed_(true), michael@0: exception_restoration_(false), michael@0: state_(NULL), michael@0: run_depth_base_(1), michael@0: #ifdef OS_WIN michael@0: os_modal_loop_(false), michael@0: #endif // OS_WIN michael@0: transient_hang_timeout_(0), michael@0: permanent_hang_timeout_(0), michael@0: next_sequence_num_(0) { michael@0: DCHECK(!current()) << "should only have one message loop per thread"; michael@0: lazy_tls_ptr.Pointer()->Set(this); michael@0: if (type_ == TYPE_MOZILLA_UI) { michael@0: pump_ = new mozilla::ipc::MessagePump(); michael@0: return; michael@0: } michael@0: if (type_ == TYPE_MOZILLA_CHILD) { michael@0: pump_ = new mozilla::ipc::MessagePumpForChildProcess(); michael@0: // There is a MessageLoop Run call from XRE_InitChildProcess michael@0: // and another one from MessagePumpForChildProcess. The one michael@0: // from MessagePumpForChildProcess becomes the base, so we need michael@0: // to set run_depth_base_ to 2 or we'll never be able to process michael@0: // Idle tasks. michael@0: run_depth_base_ = 2; michael@0: return; michael@0: } michael@0: if (type_ == TYPE_MOZILLA_NONMAINTHREAD) { michael@0: pump_ = new mozilla::ipc::MessagePumpForNonMainThreads(); michael@0: return; michael@0: } michael@0: michael@0: #if defined(OS_WIN) michael@0: // TODO(rvargas): Get rid of the OS guards. michael@0: if (type_ == TYPE_DEFAULT) { michael@0: pump_ = new base::MessagePumpDefault(); michael@0: } else if (type_ == TYPE_IO) { michael@0: pump_ = new base::MessagePumpForIO(); michael@0: } else { michael@0: DCHECK(type_ == TYPE_UI); michael@0: pump_ = new base::MessagePumpForUI(); michael@0: } michael@0: #elif defined(OS_POSIX) michael@0: if (type_ == TYPE_UI) { michael@0: #if defined(OS_MACOSX) michael@0: pump_ = base::MessagePumpMac::Create(); michael@0: #elif defined(OS_LINUX) || defined(OS_BSD) michael@0: pump_ = new base::MessagePumpForUI(); michael@0: #endif // OS_LINUX michael@0: } else if (type_ == TYPE_IO) { michael@0: pump_ = new base::MessagePumpLibevent(); michael@0: } else { michael@0: pump_ = new base::MessagePumpDefault(); michael@0: } michael@0: #endif // OS_POSIX michael@0: } michael@0: michael@0: MessageLoop::~MessageLoop() { michael@0: DCHECK(this == current()); michael@0: michael@0: // Let interested parties have one last shot at accessing this. michael@0: FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_, michael@0: WillDestroyCurrentMessageLoop()); michael@0: michael@0: DCHECK(!state_); michael@0: michael@0: // Clean up any unprocessed tasks, but take care: deleting a task could michael@0: // result in the addition of more tasks (e.g., via DeleteSoon). We set a michael@0: // limit on the number of times we will allow a deleted task to generate more michael@0: // tasks. Normally, we should only pass through this loop once or twice. If michael@0: // we end up hitting the loop limit, then it is probably due to one task that michael@0: // is being stubborn. Inspect the queues to see who is left. michael@0: bool did_work; michael@0: for (int i = 0; i < 100; ++i) { michael@0: DeletePendingTasks(); michael@0: ReloadWorkQueue(); michael@0: // If we end up with empty queues, then break out of the loop. michael@0: did_work = DeletePendingTasks(); michael@0: if (!did_work) michael@0: break; michael@0: } michael@0: DCHECK(!did_work); michael@0: michael@0: // OK, now make it so that no one can find us. michael@0: lazy_tls_ptr.Pointer()->Set(NULL); michael@0: } michael@0: michael@0: void MessageLoop::AddDestructionObserver(DestructionObserver *obs) { michael@0: DCHECK(this == current()); michael@0: destruction_observers_.AddObserver(obs); michael@0: } michael@0: michael@0: void MessageLoop::RemoveDestructionObserver(DestructionObserver *obs) { michael@0: DCHECK(this == current()); michael@0: destruction_observers_.RemoveObserver(obs); michael@0: } michael@0: michael@0: void MessageLoop::Run() { michael@0: AutoRunState save_state(this); michael@0: RunHandler(); michael@0: } michael@0: michael@0: void MessageLoop::RunAllPending() { michael@0: AutoRunState save_state(this); michael@0: state_->quit_received = true; // Means run until we would otherwise block. michael@0: RunHandler(); michael@0: } michael@0: michael@0: // Runs the loop in two different SEH modes: michael@0: // enable_SEH_restoration_ = false : any unhandled exception goes to the last michael@0: // one that calls SetUnhandledExceptionFilter(). michael@0: // enable_SEH_restoration_ = true : any unhandled exception goes to the filter michael@0: // that was existed before the loop was run. michael@0: void MessageLoop::RunHandler() { michael@0: #if defined(OS_WIN) michael@0: if (exception_restoration_) { michael@0: LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter(); michael@0: MOZ_SEH_TRY { michael@0: RunInternal(); michael@0: } MOZ_SEH_EXCEPT(SEHFilter(current_filter)) { michael@0: } michael@0: return; michael@0: } michael@0: #endif michael@0: michael@0: RunInternal(); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: void MessageLoop::RunInternal() { michael@0: DCHECK(this == current()); michael@0: pump_->Run(this); michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // Wrapper functions for use in above message loop framework. michael@0: michael@0: bool MessageLoop::ProcessNextDelayedNonNestableTask() { michael@0: if (state_->run_depth > run_depth_base_) michael@0: return false; michael@0: michael@0: if (deferred_non_nestable_work_queue_.empty()) michael@0: return false; michael@0: michael@0: Task* task = deferred_non_nestable_work_queue_.front().task; michael@0: deferred_non_nestable_work_queue_.pop(); michael@0: michael@0: RunTask(task); michael@0: return true; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: void MessageLoop::Quit() { michael@0: DCHECK(current() == this); michael@0: if (state_) { michael@0: state_->quit_received = true; michael@0: } else { michael@0: NOTREACHED() << "Must be inside Run to call Quit"; michael@0: } michael@0: } michael@0: michael@0: void MessageLoop::PostTask( michael@0: const tracked_objects::Location& from_here, Task* task) { michael@0: PostTask_Helper(from_here, task, 0, true); michael@0: } michael@0: michael@0: void MessageLoop::PostDelayedTask( michael@0: const tracked_objects::Location& from_here, Task* task, int delay_ms) { michael@0: PostTask_Helper(from_here, task, delay_ms, true); michael@0: } michael@0: michael@0: void MessageLoop::PostNonNestableTask( michael@0: const tracked_objects::Location& from_here, Task* task) { michael@0: PostTask_Helper(from_here, task, 0, false); michael@0: } michael@0: michael@0: void MessageLoop::PostNonNestableDelayedTask( michael@0: const tracked_objects::Location& from_here, Task* task, int delay_ms) { michael@0: PostTask_Helper(from_here, task, delay_ms, false); michael@0: } michael@0: michael@0: void MessageLoop::PostIdleTask( michael@0: const tracked_objects::Location& from_here, Task* task) { michael@0: DCHECK(current() == this); michael@0: michael@0: #ifdef MOZ_TASK_TRACER michael@0: task = mozilla::tasktracer::CreateTracedTask(task); michael@0: #endif michael@0: michael@0: task->SetBirthPlace(from_here); michael@0: PendingTask pending_task(task, false); michael@0: deferred_non_nestable_work_queue_.push(pending_task); michael@0: } michael@0: michael@0: // Possibly called on a background thread! michael@0: void MessageLoop::PostTask_Helper( michael@0: const tracked_objects::Location& from_here, Task* task, int delay_ms, michael@0: bool nestable) { michael@0: michael@0: #ifdef MOZ_TASK_TRACER michael@0: task = mozilla::tasktracer::CreateTracedTask(task); michael@0: #endif michael@0: michael@0: task->SetBirthPlace(from_here); michael@0: michael@0: PendingTask pending_task(task, nestable); michael@0: michael@0: if (delay_ms > 0) { michael@0: pending_task.delayed_run_time = michael@0: TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms); michael@0: } else { michael@0: DCHECK(delay_ms == 0) << "delay should not be negative"; michael@0: } michael@0: michael@0: // Warning: Don't try to short-circuit, and handle this thread's tasks more michael@0: // directly, as it could starve handling of foreign threads. Put every task michael@0: // into this queue. michael@0: michael@0: scoped_refptr pump; michael@0: { michael@0: AutoLock locked(incoming_queue_lock_); michael@0: incoming_queue_.push(pending_task); michael@0: pump = pump_; michael@0: } michael@0: // Since the incoming_queue_ may contain a task that destroys this message michael@0: // loop, we cannot exit incoming_queue_lock_ until we are done with |this|. michael@0: // We use a stack-based reference to the message pump so that we can call michael@0: // ScheduleWork outside of incoming_queue_lock_. michael@0: michael@0: pump->ScheduleWork(); michael@0: } michael@0: michael@0: void MessageLoop::SetNestableTasksAllowed(bool allowed) { michael@0: if (nestable_tasks_allowed_ != allowed) { michael@0: nestable_tasks_allowed_ = allowed; michael@0: if (!nestable_tasks_allowed_) michael@0: return; michael@0: // Start the native pump if we are not already pumping. michael@0: pump_->ScheduleWorkForNestedLoop(); michael@0: } michael@0: } michael@0: michael@0: void MessageLoop::ScheduleWork() { michael@0: // Start the native pump if we are not already pumping. michael@0: pump_->ScheduleWork(); michael@0: } michael@0: michael@0: bool MessageLoop::NestableTasksAllowed() const { michael@0: return nestable_tasks_allowed_; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: michael@0: void MessageLoop::RunTask(Task* task) { michael@0: DCHECK(nestable_tasks_allowed_); michael@0: // Execute the task and assume the worst: It is probably not reentrant. michael@0: nestable_tasks_allowed_ = false; michael@0: michael@0: task->Run(); michael@0: delete task; michael@0: michael@0: nestable_tasks_allowed_ = true; michael@0: } michael@0: michael@0: bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) { michael@0: if (pending_task.nestable || state_->run_depth <= run_depth_base_) { michael@0: RunTask(pending_task.task); michael@0: // Show that we ran a task (Note: a new one might arrive as a michael@0: // consequence!). michael@0: return true; michael@0: } michael@0: michael@0: // We couldn't run the task now because we're in a nested message loop michael@0: // and the task isn't nestable. michael@0: deferred_non_nestable_work_queue_.push(pending_task); michael@0: return false; michael@0: } michael@0: michael@0: void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) { michael@0: // Move to the delayed work queue. Initialize the sequence number michael@0: // before inserting into the delayed_work_queue_. The sequence number michael@0: // is used to faciliate FIFO sorting when two tasks have the same michael@0: // delayed_run_time value. michael@0: PendingTask new_pending_task(pending_task); michael@0: new_pending_task.sequence_num = next_sequence_num_++; michael@0: delayed_work_queue_.push(new_pending_task); michael@0: } michael@0: michael@0: void MessageLoop::ReloadWorkQueue() { michael@0: // We can improve performance of our loading tasks from incoming_queue_ to michael@0: // work_queue_ by waiting until the last minute (work_queue_ is empty) to michael@0: // load. That reduces the number of locks-per-task significantly when our michael@0: // queues get large. michael@0: if (!work_queue_.empty()) michael@0: return; // Wait till we *really* need to lock and load. michael@0: michael@0: // Acquire all we can from the inter-thread queue with one lock acquisition. michael@0: { michael@0: AutoLock lock(incoming_queue_lock_); michael@0: if (incoming_queue_.empty()) michael@0: return; michael@0: std::swap(incoming_queue_, work_queue_); michael@0: DCHECK(incoming_queue_.empty()); michael@0: } michael@0: } michael@0: michael@0: bool MessageLoop::DeletePendingTasks() { michael@0: MOZ_ASSERT(work_queue_.empty()); michael@0: bool did_work = !deferred_non_nestable_work_queue_.empty(); michael@0: while (!deferred_non_nestable_work_queue_.empty()) { michael@0: Task* task = deferred_non_nestable_work_queue_.front().task; michael@0: deferred_non_nestable_work_queue_.pop(); michael@0: delete task; michael@0: } michael@0: did_work |= !delayed_work_queue_.empty(); michael@0: while (!delayed_work_queue_.empty()) { michael@0: Task* task = delayed_work_queue_.top().task; michael@0: delayed_work_queue_.pop(); michael@0: delete task; michael@0: } michael@0: return did_work; michael@0: } michael@0: michael@0: bool MessageLoop::DoWork() { michael@0: if (!nestable_tasks_allowed_) { michael@0: // Task can't be executed right now. michael@0: return false; michael@0: } michael@0: michael@0: for (;;) { michael@0: ReloadWorkQueue(); michael@0: if (work_queue_.empty()) michael@0: break; michael@0: michael@0: // Execute oldest task. michael@0: do { michael@0: PendingTask pending_task = work_queue_.front(); michael@0: work_queue_.pop(); michael@0: if (!pending_task.delayed_run_time.is_null()) { michael@0: AddToDelayedWorkQueue(pending_task); michael@0: // If we changed the topmost task, then it is time to re-schedule. michael@0: if (delayed_work_queue_.top().task == pending_task.task) michael@0: pump_->ScheduleDelayedWork(pending_task.delayed_run_time); michael@0: } else { michael@0: if (DeferOrRunPendingTask(pending_task)) michael@0: return true; michael@0: } michael@0: } while (!work_queue_.empty()); michael@0: } michael@0: michael@0: // Nothing happened. michael@0: return false; michael@0: } michael@0: michael@0: bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) { michael@0: if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) { michael@0: *next_delayed_work_time = TimeTicks(); michael@0: return false; michael@0: } michael@0: michael@0: if (delayed_work_queue_.top().delayed_run_time > TimeTicks::Now()) { michael@0: *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; michael@0: return false; michael@0: } michael@0: michael@0: PendingTask pending_task = delayed_work_queue_.top(); michael@0: delayed_work_queue_.pop(); michael@0: michael@0: if (!delayed_work_queue_.empty()) michael@0: *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; michael@0: michael@0: return DeferOrRunPendingTask(pending_task); michael@0: } michael@0: michael@0: bool MessageLoop::DoIdleWork() { michael@0: if (ProcessNextDelayedNonNestableTask()) michael@0: return true; michael@0: michael@0: if (state_->quit_received) michael@0: pump_->Quit(); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // MessageLoop::AutoRunState michael@0: michael@0: MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) { michael@0: // Make the loop reference us. michael@0: previous_state_ = loop_->state_; michael@0: if (previous_state_) { michael@0: run_depth = previous_state_->run_depth + 1; michael@0: } else { michael@0: run_depth = 1; michael@0: } michael@0: loop_->state_ = this; michael@0: michael@0: // Initialize the other fields: michael@0: quit_received = false; michael@0: #if defined(OS_WIN) michael@0: dispatcher = NULL; michael@0: #endif michael@0: } michael@0: michael@0: MessageLoop::AutoRunState::~AutoRunState() { michael@0: loop_->state_ = previous_state_; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // MessageLoop::PendingTask michael@0: michael@0: bool MessageLoop::PendingTask::operator<(const PendingTask& other) const { michael@0: // Since the top of a priority queue is defined as the "greatest" element, we michael@0: // need to invert the comparison here. We want the smaller time to be at the michael@0: // top of the heap. michael@0: michael@0: if (delayed_run_time < other.delayed_run_time) michael@0: return false; michael@0: michael@0: if (delayed_run_time > other.delayed_run_time) michael@0: return true; michael@0: michael@0: // If the times happen to match, then we use the sequence number to decide. michael@0: // Compare the difference to support integer roll-over. michael@0: return (sequence_num - other.sequence_num) > 0; michael@0: } michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // MessageLoopForUI michael@0: michael@0: #if defined(OS_WIN) michael@0: michael@0: void MessageLoopForUI::Run(Dispatcher* dispatcher) { michael@0: AutoRunState save_state(this); michael@0: state_->dispatcher = dispatcher; michael@0: RunHandler(); michael@0: } michael@0: michael@0: void MessageLoopForUI::AddObserver(Observer* observer) { michael@0: pump_win()->AddObserver(observer); michael@0: } michael@0: michael@0: void MessageLoopForUI::RemoveObserver(Observer* observer) { michael@0: pump_win()->RemoveObserver(observer); michael@0: } michael@0: michael@0: void MessageLoopForUI::WillProcessMessage(const MSG& message) { michael@0: pump_win()->WillProcessMessage(message); michael@0: } michael@0: void MessageLoopForUI::DidProcessMessage(const MSG& message) { michael@0: pump_win()->DidProcessMessage(message); michael@0: } michael@0: void MessageLoopForUI::PumpOutPendingPaintMessages() { michael@0: pump_ui()->PumpOutPendingPaintMessages(); michael@0: } michael@0: michael@0: #endif // defined(OS_WIN) michael@0: michael@0: //------------------------------------------------------------------------------ michael@0: // MessageLoopForIO michael@0: michael@0: #if defined(OS_WIN) michael@0: michael@0: void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) { michael@0: pump_io()->RegisterIOHandler(file, handler); michael@0: } michael@0: michael@0: bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { michael@0: return pump_io()->WaitForIOCompletion(timeout, filter); michael@0: } michael@0: michael@0: #elif defined(OS_POSIX) michael@0: michael@0: bool MessageLoopForIO::WatchFileDescriptor(int fd, michael@0: bool persistent, michael@0: Mode mode, michael@0: FileDescriptorWatcher *controller, michael@0: Watcher *delegate) { michael@0: return pump_libevent()->WatchFileDescriptor( michael@0: fd, michael@0: persistent, michael@0: static_cast(mode), michael@0: controller, michael@0: delegate); michael@0: } michael@0: michael@0: bool michael@0: MessageLoopForIO::CatchSignal(int sig, michael@0: SignalEvent* sigevent, michael@0: SignalWatcher* delegate) michael@0: { michael@0: return pump_libevent()->CatchSignal(sig, sigevent, delegate); michael@0: } michael@0: michael@0: #endif