1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
7 #include "base/message_loop.h"
11 #include "base/compiler_specific.h"
12 #include "base/logging.h"
13 #include "base/message_pump_default.h"
14 #include "base/string_util.h"
15 #include "base/thread_local.h"
16 #include "mozilla/Atomics.h"
17 #include "mozilla/Mutex.h"
18 #include "mozilla/ProfilerRunnable.h"
19 #include "nsIEventTarget.h"
20 #include "nsITargetShutdownTask.h"
21 #include "nsThreadUtils.h"
23 #if defined(XP_DARWIN)
24 # include "base/message_pump_mac.h"
27 # include "base/message_pump_libevent.h"
29 #if defined(XP_LINUX) || defined(__DragonFly__) || defined(XP_FREEBSD) || \
30 defined(XP_NETBSD) || defined(XP_OPENBSD)
31 # if defined(MOZ_WIDGET_GTK)
32 # include "base/message_pump_glib.h"
36 # include "base/message_pump_android.h"
38 #include "nsISerialEventTarget.h"
40 #include "mozilla/ipc/MessagePump.h"
41 #include "nsThreadUtils.h"
44 using base::TimeDelta
;
45 using base::TimeTicks
;
47 using mozilla::Runnable
;
49 static base::ThreadLocalPointer
<MessageLoop
>& get_tls_ptr() {
50 static base::ThreadLocalPointer
<MessageLoop
> tls_ptr
;
54 //------------------------------------------------------------------------------
58 // Upon a SEH exception in this thread, it restores the original unhandled
60 static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter
) {
61 ::SetUnhandledExceptionFilter(old_filter
);
62 return EXCEPTION_CONTINUE_SEARCH
;
65 // Retrieves a pointer to the current unhandled exception filter. There
66 // is no standalone getter method.
67 static LPTOP_LEVEL_EXCEPTION_FILTER
GetTopSEHFilter() {
68 LPTOP_LEVEL_EXCEPTION_FILTER top_filter
= NULL
;
69 top_filter
= ::SetUnhandledExceptionFilter(0);
70 ::SetUnhandledExceptionFilter(top_filter
);
74 #endif // defined(XP_WIN)
76 //------------------------------------------------------------------------------
78 class MessageLoop::EventTarget
: public nsISerialEventTarget
,
79 public nsITargetShutdownTask
,
80 public MessageLoop::DestructionObserver
{
82 NS_DECL_THREADSAFE_ISUPPORTS
83 NS_DECL_NSIEVENTTARGET_FULL
85 void TargetShutdown() override
{
86 nsTArray
<nsCOMPtr
<nsITargetShutdownTask
>> shutdownTasks
;
88 mozilla::MutexAutoLock
lock(mMutex
);
89 if (mShutdownTasksRun
) {
92 mShutdownTasksRun
= true;
93 shutdownTasks
= std::move(mShutdownTasks
);
94 mShutdownTasks
.Clear();
97 for (auto& task
: shutdownTasks
) {
98 task
->TargetShutdown();
102 explicit EventTarget(MessageLoop
* aLoop
)
103 : mMutex("MessageLoop::EventTarget"), mLoop(aLoop
) {
104 aLoop
->AddDestructionObserver(this);
108 virtual ~EventTarget() {
110 mLoop
->RemoveDestructionObserver(this);
112 MOZ_ASSERT(mShutdownTasks
.IsEmpty());
115 void WillDestroyCurrentMessageLoop() override
{
117 mozilla::MutexAutoLock
lock(mMutex
);
118 // The MessageLoop is being destroyed and we are called from its
119 // destructor There's no real need to remove ourselves from the
120 // destruction observer list. But it makes things look tidier.
121 mLoop
->RemoveDestructionObserver(this);
128 mozilla::Mutex mMutex
;
129 bool mShutdownTasksRun
MOZ_GUARDED_BY(mMutex
) = false;
130 nsTArray
<nsCOMPtr
<nsITargetShutdownTask
>> mShutdownTasks
131 MOZ_GUARDED_BY(mMutex
);
132 MessageLoop
* mLoop
MOZ_GUARDED_BY(mMutex
);
135 NS_IMPL_ISUPPORTS(MessageLoop::EventTarget
, nsIEventTarget
,
136 nsISerialEventTarget
)
139 MessageLoop::EventTarget::IsOnCurrentThreadInfallible() {
140 mozilla::MutexAutoLock
lock(mMutex
);
141 return mLoop
== MessageLoop::current();
145 MessageLoop::EventTarget::IsOnCurrentThread(bool* aResult
) {
146 *aResult
= IsOnCurrentThreadInfallible();
151 MessageLoop::EventTarget::DispatchFromScript(nsIRunnable
* aEvent
,
153 nsCOMPtr
<nsIRunnable
> event(aEvent
);
154 return Dispatch(event
.forget(), aFlags
);
158 MessageLoop::EventTarget::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
,
160 mozilla::MutexAutoLock
lock(mMutex
);
162 return NS_ERROR_NOT_INITIALIZED
;
165 if (aFlags
!= NS_DISPATCH_NORMAL
) {
166 return NS_ERROR_NOT_IMPLEMENTED
;
169 mLoop
->PostTask(std::move(aEvent
));
174 MessageLoop::EventTarget::DelayedDispatch(already_AddRefed
<nsIRunnable
> aEvent
,
176 mozilla::MutexAutoLock
lock(mMutex
);
178 return NS_ERROR_NOT_INITIALIZED
;
181 mLoop
->PostDelayedTask(std::move(aEvent
), aDelayMs
);
186 MessageLoop::EventTarget::RegisterShutdownTask(nsITargetShutdownTask
* aTask
) {
187 mozilla::MutexAutoLock
lock(mMutex
);
188 if (!mLoop
|| mShutdownTasksRun
) {
189 return NS_ERROR_UNEXPECTED
;
191 MOZ_ASSERT(!mShutdownTasks
.Contains(aTask
));
192 mShutdownTasks
.AppendElement(aTask
);
197 MessageLoop::EventTarget::UnregisterShutdownTask(nsITargetShutdownTask
* aTask
) {
198 mozilla::MutexAutoLock
lock(mMutex
);
199 if (!mLoop
|| mShutdownTasksRun
) {
200 return NS_ERROR_UNEXPECTED
;
202 return mShutdownTasks
.RemoveElement(aTask
) ? NS_OK
: NS_ERROR_UNEXPECTED
;
205 //------------------------------------------------------------------------------
208 MessageLoop
* MessageLoop::current() { return get_tls_ptr().Get(); }
211 void MessageLoop::set_current(MessageLoop
* loop
) { get_tls_ptr().Set(loop
); }
213 static mozilla::Atomic
<int32_t> message_loop_id_seq(0);
215 MessageLoop::MessageLoop(Type type
, nsISerialEventTarget
* aEventTarget
)
217 id_(++message_loop_id_seq
),
218 nestable_tasks_allowed_(true),
219 exception_restoration_(false),
220 incoming_queue_lock_("MessageLoop Incoming Queue Lock"),
223 shutting_down_(false),
225 os_modal_loop_(false),
227 transient_hang_timeout_(0),
228 permanent_hang_timeout_(0),
229 next_sequence_num_(0) {
230 DCHECK(!current()) << "should only have one message loop per thread";
231 get_tls_ptr().Set(this);
233 // Must initialize after current() is initialized.
234 mEventTarget
= new EventTarget(this);
237 case TYPE_MOZILLA_PARENT
:
238 MOZ_RELEASE_ASSERT(!aEventTarget
);
239 pump_
= new mozilla::ipc::MessagePump(aEventTarget
);
241 case TYPE_MOZILLA_CHILD
:
242 MOZ_RELEASE_ASSERT(!aEventTarget
);
243 pump_
= new mozilla::ipc::MessagePumpForChildProcess();
244 // There is a MessageLoop Run call from XRE_InitChildProcess
245 // and another one from MessagePumpForChildProcess. The one
246 // from MessagePumpForChildProcess becomes the base, so we need
247 // to set run_depth_base_ to 2 or we'll never be able to process
251 case TYPE_MOZILLA_NONMAINTHREAD
:
252 pump_
= new mozilla::ipc::MessagePumpForNonMainThreads(aEventTarget
);
254 #if defined(XP_WIN) || defined(XP_DARWIN)
255 case TYPE_MOZILLA_NONMAINUITHREAD
:
256 pump_
= new mozilla::ipc::MessagePumpForNonMainUIThreads(aEventTarget
);
258 #elif defined(MOZ_WIDGET_ANDROID)
259 case TYPE_MOZILLA_ANDROID_UI
:
260 MOZ_RELEASE_ASSERT(aEventTarget
);
261 pump_
= new mozilla::ipc::MessagePumpForAndroidUI(aEventTarget
);
263 #endif // defined(MOZ_WIDGET_ANDROID)
265 // Create one of Chromium's standard MessageLoop types below.
270 // TODO(rvargas): Get rid of the OS guards.
271 if (type_
== TYPE_DEFAULT
) {
272 pump_
= new base::MessagePumpDefault();
273 } else if (type_
== TYPE_IO
) {
274 pump_
= new base::MessagePumpForIO();
276 DCHECK(type_
== TYPE_UI
);
277 pump_
= new base::MessagePumpForUI();
280 if (type_
== TYPE_UI
) {
281 # if defined(XP_DARWIN)
282 pump_
= base::MessagePumpMac::Create();
283 # elif defined(XP_LINUX) || defined(__DragonFly__) || defined(XP_FREEBSD) || \
284 defined(XP_NETBSD) || defined(XP_OPENBSD)
285 pump_
= new base::MessagePumpForUI();
287 } else if (type_
== TYPE_IO
) {
288 pump_
= new base::MessagePumpLibevent();
290 pump_
= new base::MessagePumpDefault();
294 // We want GetCurrentSerialEventTarget() to return the real nsThread if it
295 // will be used to dispatch tasks. However, under all other cases; we'll want
296 // it to return this MessageLoop's EventTarget.
297 if (nsISerialEventTarget
* thread
= pump_
->GetXPCOMThread()) {
298 MOZ_ALWAYS_SUCCEEDS(thread
->RegisterShutdownTask(mEventTarget
));
300 mozilla::SerialEventTargetGuard::Set(mEventTarget
);
304 MessageLoop::~MessageLoop() {
305 DCHECK(this == current());
307 // Let interested parties have one last shot at accessing this.
308 FOR_EACH_OBSERVER(DestructionObserver
, destruction_observers_
,
309 WillDestroyCurrentMessageLoop());
313 // Clean up any unprocessed tasks, but take care: deleting a task could
314 // result in the addition of more tasks (e.g., via DeleteSoon). We set a
315 // limit on the number of times we will allow a deleted task to generate more
316 // tasks. Normally, we should only pass through this loop once or twice. If
317 // we end up hitting the loop limit, then it is probably due to one task that
318 // is being stubborn. Inspect the queues to see who is left.
320 for (int i
= 0; i
< 100; ++i
) {
321 DeletePendingTasks();
323 // If we end up with empty queues, then break out of the loop.
324 did_work
= DeletePendingTasks();
325 if (!did_work
) break;
329 // OK, now make it so that no one can find us.
330 get_tls_ptr().Set(NULL
);
333 void MessageLoop::AddDestructionObserver(DestructionObserver
* obs
) {
334 DCHECK(this == current());
335 destruction_observers_
.AddObserver(obs
);
338 void MessageLoop::RemoveDestructionObserver(DestructionObserver
* obs
) {
339 DCHECK(this == current());
340 destruction_observers_
.RemoveObserver(obs
);
343 void MessageLoop::Run() {
344 AutoRunState
save_state(this);
348 // Runs the loop in two different SEH modes:
349 // enable_SEH_restoration_ = false : any unhandled exception goes to the last
350 // one that calls SetUnhandledExceptionFilter().
351 // enable_SEH_restoration_ = true : any unhandled exception goes to the filter
352 // that was existed before the loop was run.
353 void MessageLoop::RunHandler() {
355 if (exception_restoration_
) {
356 LPTOP_LEVEL_EXCEPTION_FILTER current_filter
= GetTopSEHFilter();
357 MOZ_SEH_TRY
{ RunInternal(); }
358 MOZ_SEH_EXCEPT(SEHFilter(current_filter
)) {}
366 //------------------------------------------------------------------------------
368 void MessageLoop::RunInternal() {
369 DCHECK(this == current());
373 //------------------------------------------------------------------------------
374 // Wrapper functions for use in above message loop framework.
376 bool MessageLoop::ProcessNextDelayedNonNestableTask() {
377 if (state_
->run_depth
> run_depth_base_
) return false;
379 if (deferred_non_nestable_work_queue_
.empty()) return false;
381 nsCOMPtr
<nsIRunnable
> task
=
382 std::move(deferred_non_nestable_work_queue_
.front().task
);
383 deferred_non_nestable_work_queue_
.pop();
385 RunTask(task
.forget());
389 //------------------------------------------------------------------------------
391 void MessageLoop::Quit() {
392 DCHECK(current() == this);
394 state_
->quit_received
= true;
396 NOTREACHED() << "Must be inside Run to call Quit";
400 void MessageLoop::PostTask(already_AddRefed
<nsIRunnable
> task
) {
401 PostTask_Helper(std::move(task
), 0);
404 void MessageLoop::PostDelayedTask(already_AddRefed
<nsIRunnable
> task
,
406 PostTask_Helper(std::move(task
), delay_ms
);
409 void MessageLoop::PostIdleTask(already_AddRefed
<nsIRunnable
> task
) {
410 DCHECK(current() == this);
411 MOZ_ASSERT(NS_IsMainThread());
413 PendingTask
pending_task(std::move(task
), false);
414 mozilla::LogRunnable::LogDispatch(pending_task
.task
.get());
415 deferred_non_nestable_work_queue_
.push(std::move(pending_task
));
418 // Possibly called on a background thread!
419 void MessageLoop::PostTask_Helper(already_AddRefed
<nsIRunnable
> task
,
421 if (nsISerialEventTarget
* target
= pump_
->GetXPCOMThread()) {
424 rv
= target
->DelayedDispatch(std::move(task
), delay_ms
);
426 rv
= target
->Dispatch(std::move(task
), 0);
428 MOZ_ALWAYS_SUCCEEDS(rv
);
432 // Tasks should only be queued before or during the Run loop, not after.
433 MOZ_ASSERT(!shutting_down_
);
435 PendingTask
pending_task(std::move(task
), true);
438 pending_task
.delayed_run_time
=
439 TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms
);
441 DCHECK(delay_ms
== 0) << "delay should not be negative";
444 // Warning: Don't try to short-circuit, and handle this thread's tasks more
445 // directly, as it could starve handling of foreign threads. Put every task
448 RefPtr
<base::MessagePump
> pump
;
450 mozilla::MutexAutoLock
locked(incoming_queue_lock_
);
451 mozilla::LogRunnable::LogDispatch(pending_task
.task
.get());
452 incoming_queue_
.push(std::move(pending_task
));
455 // Since the incoming_queue_ may contain a task that destroys this message
456 // loop, we cannot exit incoming_queue_lock_ until we are done with |this|.
457 // We use a stack-based reference to the message pump so that we can call
458 // ScheduleWork outside of incoming_queue_lock_.
460 pump
->ScheduleWork();
463 void MessageLoop::SetNestableTasksAllowed(bool allowed
) {
464 if (nestable_tasks_allowed_
!= allowed
) {
465 nestable_tasks_allowed_
= allowed
;
466 if (!nestable_tasks_allowed_
) return;
467 // Start the native pump if we are not already pumping.
468 pump_
->ScheduleWorkForNestedLoop();
472 void MessageLoop::ScheduleWork() {
473 // Start the native pump if we are not already pumping.
474 pump_
->ScheduleWork();
477 bool MessageLoop::NestableTasksAllowed() const {
478 return nestable_tasks_allowed_
;
481 //------------------------------------------------------------------------------
483 void MessageLoop::RunTask(already_AddRefed
<nsIRunnable
> aTask
) {
484 DCHECK(nestable_tasks_allowed_
);
485 // Execute the task and assume the worst: It is probably not reentrant.
486 nestable_tasks_allowed_
= false;
488 nsCOMPtr
<nsIRunnable
> task
= aTask
;
491 mozilla::LogRunnable::Run
log(task
.get());
492 AUTO_PROFILE_FOLLOWING_RUNNABLE(task
);
497 nestable_tasks_allowed_
= true;
500 bool MessageLoop::DeferOrRunPendingTask(PendingTask
&& pending_task
) {
501 if (pending_task
.nestable
|| state_
->run_depth
<= run_depth_base_
) {
502 RunTask(pending_task
.task
.forget());
503 // Show that we ran a task (Note: a new one might arrive as a
508 // We couldn't run the task now because we're in a nested message loop
509 // and the task isn't nestable.
510 mozilla::LogRunnable::LogDispatch(pending_task
.task
.get());
511 deferred_non_nestable_work_queue_
.push(std::move(pending_task
));
515 void MessageLoop::AddToDelayedWorkQueue(const PendingTask
& pending_task
) {
516 // Move to the delayed work queue. Initialize the sequence number
517 // before inserting into the delayed_work_queue_. The sequence number
518 // is used to faciliate FIFO sorting when two tasks have the same
519 // delayed_run_time value.
520 PendingTask
new_pending_task(pending_task
);
521 new_pending_task
.sequence_num
= next_sequence_num_
++;
522 mozilla::LogRunnable::LogDispatch(new_pending_task
.task
.get());
523 delayed_work_queue_
.push(std::move(new_pending_task
));
526 void MessageLoop::ReloadWorkQueue() {
527 // We can improve performance of our loading tasks from incoming_queue_ to
528 // work_queue_ by waiting until the last minute (work_queue_ is empty) to
529 // load. That reduces the number of locks-per-task significantly when our
531 if (!work_queue_
.empty())
532 return; // Wait till we *really* need to lock and load.
534 // Acquire all we can from the inter-thread queue with one lock acquisition.
536 mozilla::MutexAutoLock
lock(incoming_queue_lock_
);
537 if (incoming_queue_
.empty()) return;
538 std::swap(incoming_queue_
, work_queue_
);
539 DCHECK(incoming_queue_
.empty());
543 bool MessageLoop::DeletePendingTasks() {
544 MOZ_ASSERT(work_queue_
.empty());
545 bool did_work
= !deferred_non_nestable_work_queue_
.empty();
546 while (!deferred_non_nestable_work_queue_
.empty()) {
547 deferred_non_nestable_work_queue_
.pop();
549 did_work
|= !delayed_work_queue_
.empty();
550 while (!delayed_work_queue_
.empty()) {
551 delayed_work_queue_
.pop();
556 bool MessageLoop::DoWork() {
557 if (!nestable_tasks_allowed_
) {
558 // Task can't be executed right now.
564 if (work_queue_
.empty()) break;
566 // Execute oldest task.
568 PendingTask pending_task
= std::move(work_queue_
.front());
570 if (!pending_task
.delayed_run_time
.is_null()) {
571 // NB: Don't move, because we use this later!
572 AddToDelayedWorkQueue(pending_task
);
573 // If we changed the topmost task, then it is time to re-schedule.
574 if (delayed_work_queue_
.top().task
== pending_task
.task
)
575 pump_
->ScheduleDelayedWork(pending_task
.delayed_run_time
);
577 if (DeferOrRunPendingTask(std::move(pending_task
))) return true;
579 } while (!work_queue_
.empty());
586 bool MessageLoop::DoDelayedWork(TimeTicks
* next_delayed_work_time
) {
587 if (!nestable_tasks_allowed_
|| delayed_work_queue_
.empty()) {
588 *next_delayed_work_time
= TimeTicks();
592 if (delayed_work_queue_
.top().delayed_run_time
> TimeTicks::Now()) {
593 *next_delayed_work_time
= delayed_work_queue_
.top().delayed_run_time
;
597 PendingTask pending_task
= delayed_work_queue_
.top();
598 delayed_work_queue_
.pop();
600 if (!delayed_work_queue_
.empty())
601 *next_delayed_work_time
= delayed_work_queue_
.top().delayed_run_time
;
603 return DeferOrRunPendingTask(std::move(pending_task
));
606 bool MessageLoop::DoIdleWork() {
607 if (ProcessNextDelayedNonNestableTask()) return true;
609 if (state_
->quit_received
) pump_
->Quit();
614 //------------------------------------------------------------------------------
615 // MessageLoop::AutoRunState
617 MessageLoop::AutoRunState::AutoRunState(MessageLoop
* loop
) : loop_(loop
) {
618 // Top-level Run should only get called once.
619 MOZ_ASSERT(!loop_
->shutting_down_
);
621 // Make the loop reference us.
622 previous_state_
= loop_
->state_
;
623 if (previous_state_
) {
624 run_depth
= previous_state_
->run_depth
+ 1;
628 loop_
->state_
= this;
630 // Initialize the other fields:
631 quit_received
= false;
637 MessageLoop::AutoRunState::~AutoRunState() {
638 loop_
->state_
= previous_state_
;
640 // If exiting a top-level Run, then we're shutting down.
641 loop_
->shutting_down_
= !previous_state_
;
644 //------------------------------------------------------------------------------
645 // MessageLoop::PendingTask
647 bool MessageLoop::PendingTask::operator<(const PendingTask
& other
) const {
648 // Since the top of a priority queue is defined as the "greatest" element, we
649 // need to invert the comparison here. We want the smaller time to be at the
652 if (delayed_run_time
< other
.delayed_run_time
) return false;
654 if (delayed_run_time
> other
.delayed_run_time
) return true;
656 // If the times happen to match, then we use the sequence number to decide.
657 // Compare the difference to support integer roll-over.
658 return (sequence_num
- other
.sequence_num
) > 0;
661 //------------------------------------------------------------------------------
662 // MessageLoop::SerialEventTarget
664 nsISerialEventTarget
* MessageLoop::SerialEventTarget() { return mEventTarget
; }
666 //------------------------------------------------------------------------------
671 void MessageLoopForUI::Run(Dispatcher
* dispatcher
) {
672 AutoRunState
save_state(this);
673 state_
->dispatcher
= dispatcher
;
677 void MessageLoopForUI::AddObserver(Observer
* observer
) {
678 pump_win()->AddObserver(observer
);
681 void MessageLoopForUI::RemoveObserver(Observer
* observer
) {
682 pump_win()->RemoveObserver(observer
);
685 void MessageLoopForUI::WillProcessMessage(const MSG
& message
) {
686 pump_win()->WillProcessMessage(message
);
688 void MessageLoopForUI::DidProcessMessage(const MSG
& message
) {
689 pump_win()->DidProcessMessage(message
);
691 void MessageLoopForUI::PumpOutPendingPaintMessages() {
692 pump_ui()->PumpOutPendingPaintMessages();
695 #endif // defined(XP_WIN)
697 //------------------------------------------------------------------------------
702 void MessageLoopForIO::RegisterIOHandler(HANDLE file
, IOHandler
* handler
) {
703 pump_io()->RegisterIOHandler(file
, handler
);
706 bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout
, IOHandler
* filter
) {
707 return pump_io()->WaitForIOCompletion(timeout
, filter
);
712 bool MessageLoopForIO::WatchFileDescriptor(int fd
, bool persistent
, Mode mode
,
713 FileDescriptorWatcher
* controller
,
715 return pump_libevent()->WatchFileDescriptor(
716 fd
, persistent
, static_cast<base::MessagePumpLibevent::Mode
>(mode
),
717 controller
, delegate
);
720 bool MessageLoopForIO::CatchSignal(int sig
, SignalEvent
* sigevent
,
721 SignalWatcher
* delegate
) {
722 return pump_libevent()->CatchSignal(sig
, sigevent
, delegate
);