Bug 429592 - Add a monitor thread for process hangs and crash by default if a chrome process doesn't end up back in the event loop for more than 30 seconds. By default this affects non-debug builds only. r=cjones/bent
☠☠ backed out by 35cfc34bdc44 ☠ ☠
authorBenjamin Smedberg <benjamin@smedbergs.us>
Wed, 12 Oct 2011 13:52:26 -0400
changeset 79147 564e841f1f57234109ff6e071367bf0dd398bb1d
parent 79146 52b481205766e583307c5de01d96079133487735
child 79148 3db099ca452a6d1d063b4c824cc5fc6a4607be77
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewerscjones, bent
bugs429592
milestone10.0a1
Bug 429592 - Add a monitor thread for process hangs and crash by default if a chrome process doesn't end up back in the event loop for more than 30 seconds. By default this affects non-debug builds only. r=cjones/bent
modules/libpref/src/init/all.js
widget/src/windows/nsAppShell.cpp
xpcom/build/nsXPComInit.cpp
xpcom/threads/HangMonitor.cpp
xpcom/threads/HangMonitor.h
xpcom/threads/Makefile.in
xpcom/threads/nsThread.cpp
xpcom/threads/nsThread.h
xpcom/threads/nsThreadManager.cpp
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1465,16 +1465,24 @@ pref("gestures.enable_single_finger_inpu
 //pref("editor.encode_entity",                 "html");
 
 pref("editor.resizing.preserve_ratio",       true);
 pref("editor.positioning.offset",            0);
 
 pref("dom.max_chrome_script_run_time", 20);
 pref("dom.max_script_run_time", 10);
 
+// Hang monitor timeout after which we kill the browser, in seconds
+// (0 is disabled)
+#ifndef DEBUG
+pref("hangmonitor.timeout", 30);
+#else
+pref("hangmonitor.timeout", 0);
+#endif
+
 #ifndef DEBUG
 // How long a plugin is allowed to process a synchronous IPC message
 // before we consider it "hung".
 pref("dom.ipc.plugins.timeoutSecs", 45);
 // How long a plugin process will wait for a response from the parent
 // to a synchronous request before terminating itself. After this
 // point the child assumes the parent is hung.
 pref("dom.ipc.plugins.parentTimeoutSecs", 15);
--- a/widget/src/windows/nsAppShell.cpp
+++ b/widget/src/windows/nsAppShell.cpp
@@ -41,16 +41,17 @@
 #include "mozilla/ipc/RPCChannel.h"
 #include "nsAppShell.h"
 #include "nsToolkit.h"
 #include "nsThreadUtils.h"
 #include "WinTaskbar.h"
 #include "nsString.h"
 #include "nsIMM32Handler.h"
 #include "mozilla/widget/AudioSession.h"
+#include "mozilla/HangMonitor.h"
 
 // For skidmark code
 #include <windows.h> 
 #include <tlhelp32.h> 
 
 const PRUnichar* kAppShellEventId = L"nsAppShell:EventID";
 const PRUnichar* kTaskbarButtonEventId = L"TaskbarButtonCreated";
 
@@ -337,21 +338,23 @@ nsAppShell::ProcessNextNativeEvent(bool 
     // Give priority to keyboard and mouse messages.
     if (PeekUIMessage(&msg) ||
         ::PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) {
       gotMessage = true;
       if (msg.message == WM_QUIT) {
         ::PostQuitMessage(msg.wParam);
         Exit();
       } else {
+        mozilla::HangMonitor::NotifyActivity();
         ::TranslateMessage(&msg);
         ::DispatchMessageW(&msg);
       }
     } else if (mayWait) {
       // Block and wait for any posted application message
+      mozilla::HangMonitor::Suspend();
       ::WaitMessage();
     }
   } while (!gotMessage && mayWait);
 
   // See DoProcessNextNativeEvent, mEventloopNestingLevel will be
   // one when a modal loop unwinds.
   if (mNativeCallbackPending && mEventloopNestingLevel == 1)
     DoProcessMoreGeckoEvents();
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -134,16 +134,17 @@ extern nsresult nsStringInputStreamConst
 
 #include "nsSystemInfo.h"
 #include "nsMemoryReporterManager.h"
 
 #include <locale.h>
 #include "mozilla/Services.h"
 #include "mozilla/FunctionTimer.h"
 #include "mozilla/Omnijar.h"
+#include "mozilla/HangMonitor.h"
 
 #include "nsChromeRegistry.h"
 #include "nsChromeProtocolHandler.h"
 
 #include "mozilla/scache/StartupCache.h"
 
 #include "base/at_exit.h"
 #include "base/command_line.h"
@@ -525,16 +526,18 @@ NS_InitXPCOM2(nsIServiceManager* *result
                                   nsnull,
                                   NS_XPCOM_STARTUP_OBSERVER_ID);
 #ifdef XP_WIN
     ScheduleMediaCacheRemover();
 #endif
 
     mozilla::MapsMemoryReporter::Init();
 
+    mozilla::HangMonitor::Startup();
+
     return NS_OK;
 }
 
 
 //
 // NS_ShutdownXPCOM()
 //
 // The shutdown sequence for xpcom would be
@@ -561,16 +564,19 @@ NS_ShutdownXPCOM(nsIServiceManager* serv
     return mozilla::ShutdownXPCOM(servMgr);
 }
 
 namespace mozilla {
 
 nsresult
 ShutdownXPCOM(nsIServiceManager* servMgr)
 {
+    // Make sure the hang monitor is enabled for shutdown.
+    HangMonitor::NotifyActivity();
+
     NS_ENSURE_STATE(NS_IsMainThread());
 
     nsresult rv;
     nsCOMPtr<nsISimpleEnumerator> moduleLoaders;
 
     // Notify observers of xpcom shutting down
     {
         // Block it so that the COMPtr will get deleted before we hit
@@ -618,16 +624,18 @@ ShutdownXPCOM(nsIServiceManager* servMgr
 
         // Shutdown all remaining threads.  This method does not return until
         // all threads created using the thread manager (with the exception of
         // the main thread) have exited.
         nsThreadManager::get()->Shutdown();
 
         NS_ProcessPendingEvents(thread);
 
+        HangMonitor::NotifyActivity();
+
         // We save the "xpcom-shutdown-loaders" observers to notify after
         // the observerservice is gone.
         if (observerService) {
             observerService->
                 EnumerateObservers(NS_XPCOM_SHUTDOWN_LOADERS_OBSERVER_ID,
                                    getter_AddRefs(moduleLoaders));
 
             observerService->Shutdown();
@@ -727,16 +735,18 @@ ShutdownXPCOM(nsIServiceManager* servMgr
         CommandLine::Terminate();
         sCommandLineWasInitialized = false;
     }
     if (sExitManager) {
         delete sExitManager;
         sExitManager = nsnull;
     }
 
-    mozilla::Omnijar::CleanUp();
+    Omnijar::CleanUp();
+
+    HangMonitor::Shutdown();
 
     NS_LogTerm();
 
     return NS_OK;
 }
 
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/HangMonitor.cpp
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Firefox.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation <http://www.mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "mozilla/HangMonitor.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Preferences.h"
+#include "nsExceptionHandler.h"
+#include "nsXULAppAPI.h"
+#include "nsThreadUtils.h"
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+namespace mozilla { namespace HangMonitor {
+
+/**
+ * A flag which may be set from within a debugger to disable the hang
+ * monitor.
+ */
+volatile bool gDebugDisableHangMonitor = false;
+
+const char kHangMonitorPrefName[] = "hangmonitor.timeout";
+
+// Monitor protects gShutdown and gTimeout, but not gTimestamp which rely on
+// being atomically set by the processor; synchronization doesn't really matter
+// in this use case.
+Monitor* gMonitor;
+
+// The timeout preference, in seconds.
+PRInt32 gTimeout;
+
+PRThread* gThread;
+
+// Set when shutdown begins to signal the thread to exit immediately.
+bool gShutdown;
+
+// The timestamp of the last event notification, or PR_INTERVAL_NO_WAIT if
+// we're currently not processing events.
+volatile PRIntervalTime gTimestamp;
+
+// PrefChangedFunc
+int
+PrefChanged(const char*, void*)
+{
+  PRInt32 newval = Preferences::GetInt(kHangMonitorPrefName);
+  MonitorAutoLock lock(*gMonitor);
+  if (newval != gTimeout) {
+    gTimeout = newval;
+    lock.Notify();
+  }
+
+  return 0;
+}
+
+void
+Crash()
+{
+  if (gDebugDisableHangMonitor) {
+    return;
+  }
+
+#ifdef XP_WIN
+  if (::IsDebuggerPresent()) {
+    return;
+  }
+#endif
+
+  CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Hang"),
+                                     NS_LITERAL_CSTRING("1"));
+
+  NS_RUNTIMEABORT("HangMonitor triggered");
+}
+
+void
+ThreadMain(void*)
+{
+  MonitorAutoLock lock(*gMonitor);
+
+  // In order to avoid issues with the hang monitor incorrectly triggering
+  // during a general system stop such as sleeping, the monitor thread must
+  // run twice to trigger hang protection.
+  PRIntervalTime lastTimestamp = 0;
+  int waitCount = 0;
+
+  while (true) {
+    if (gShutdown) {
+      return; // Exit the thread
+    }
+
+    // avoid rereading the volatile value in this loop
+    PRIntervalTime timestamp = gTimestamp;
+
+    PRIntervalTime now = PR_IntervalNow();
+
+    if (timestamp != PR_INTERVAL_NO_WAIT &&
+        now < timestamp) {
+      // 32-bit overflow, reset for another waiting period
+      timestamp = 1; // lowest legal PRInterval value
+    }
+
+    if (timestamp != PR_INTERVAL_NO_WAIT &&
+        timestamp == lastTimestamp &&
+        gTimeout > 0) {
+      ++waitCount;
+      if (waitCount == 2) {
+        PRInt32 delay =
+          PRInt32(PR_IntervalToSeconds(now - timestamp));
+        if (delay > gTimeout) {
+          MonitorAutoUnlock unlock(*gMonitor);
+          Crash();
+        }
+      }
+    }
+    else {
+      lastTimestamp = timestamp;
+      waitCount = 0;
+    }
+
+    PRIntervalTime timeout;
+    if (gTimeout <= 0) {
+      timeout = PR_INTERVAL_NO_TIMEOUT;
+    }
+    else {
+      timeout = PR_MillisecondsToInterval(gTimeout * 500);
+    }
+    lock.Wait(timeout);
+  }
+}
+
+void
+Startup()
+{
+  // The hang detector only runs in chrome processes. If you change this,
+  // you must also deal with the threadsafety of AnnotateCrashReport in
+  // non-chrome processes!
+  if (GeckoProcessType_Default != XRE_GetProcessType())
+    return;
+
+  NS_ASSERTION(!gMonitor, "Hang monitor already initialized");
+  gMonitor = new Monitor("HangMonitor");
+
+  Preferences::RegisterCallback(PrefChanged, kHangMonitorPrefName, NULL);
+  PrefChanged(NULL, NULL);
+
+  // Don't actually start measuring hangs until we hit the main event loop.
+  // This potentially misses a small class of really early startup hangs,
+  // but avoids dealing with some xpcshell tests and other situations which
+  // start XPCOM but don't ever start the event loop.
+  Suspend();
+
+  gThread = PR_CreateThread(PR_USER_THREAD,
+                            ThreadMain,
+                            NULL, PR_PRIORITY_LOW, PR_GLOBAL_THREAD,
+                            PR_JOINABLE_THREAD, 0);
+}
+
+void
+Shutdown()
+{
+  if (GeckoProcessType_Default != XRE_GetProcessType())
+    return;
+
+  NS_ASSERTION(gMonitor, "Hang monitor not started");
+
+  { // Scope the lock we're going to delete later
+    MonitorAutoLock lock(*gMonitor);
+    gShutdown = true;
+    lock.Notify();
+  }
+
+  // thread creation could theoretically fail
+  if (gThread) {
+    PR_JoinThread(gThread);
+    gThread = NULL;
+  }
+
+  delete gMonitor;
+  gMonitor = NULL;
+}
+
+void
+NotifyActivity()
+{
+  NS_ASSERTION(NS_IsMainThread(), "HangMonitor::Notify called from off the main thread.");
+
+  // This is not a locked activity because PRTimeStamp is a 32-bit quantity
+  // which can be read/written atomically, and we don't want to pay locking
+  // penalties here.
+  gTimestamp = PR_IntervalNow();
+}
+
+void
+Suspend()
+{
+  NS_ASSERTION(NS_IsMainThread(), "HangMonitor::Suspend called from off the main thread.");
+
+  // Because gTimestamp changes this resets the wait count.
+  gTimestamp = PR_INTERVAL_NO_WAIT;
+}
+
+} } // namespace mozilla::HangMonitor
new file mode 100644
--- /dev/null
+++ b/xpcom/threads/HangMonitor.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Firefox.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation <http://www.mozilla.org>.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_HangMonitor_h
+#define mozilla_HangMonitor_h
+
+namespace mozilla { namespace HangMonitor {
+
+/**
+ * Start monitoring hangs. Should be called by the XPCOM startup process only.
+ */
+void Startup();
+
+/**
+ * Stop monitoring hangs and join the thread.
+ */
+void Shutdown();
+
+/**
+ * Notify the hang monitor of new activity which should reset its internal
+ * timer.
+ */
+void NotifyActivity();
+
+/**
+ * Notify the hang monitor that the browser is now idle and no detection should
+ * be done.
+ */
+void Suspend();
+
+} } // namespace mozilla::HangMonitor
+
+#endif // mozilla_HangMonitor_h
--- a/xpcom/threads/Makefile.in
+++ b/xpcom/threads/Makefile.in
@@ -42,35 +42,42 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= xpcom
 XPIDL_MODULE	= xpcom_threads
 LIBRARY_NAME	= xpcomthreads_s
 GRE_MODULE	= 1
 MOZILLA_INTERNAL_API = 1
+LIBXUL_LIBRARY = 1
 
+EXPORTS_NAMESPACES = mozilla
 
 CPPSRCS		= \
 		nsEventQueue.cpp \
 		nsEnvironment.cpp \
 		nsThread.cpp \
 		nsThreadManager.cpp \
 		nsThreadPool.cpp \
 		nsProcessCommon.cpp \
 		nsTimerImpl.cpp \
 		TimerThread.cpp \
+		HangMonitor.cpp \
 		$(NULL)
 
 EXPORTS		= \
 		nsProcess.h \
 		nsEventQueue.h \
 		nsThreadUtilsInternal.h \
 		$(NULL)
 
+EXPORTS_mozilla = \
+  HangMonitor.h \
+  $(NULL)
+
 XPIDLSRCS	= \
 		nsIEventTarget.idl \
 		nsIThread.idl \
 		nsIThreadInternal.idl \
 		nsIThreadManager.idl \
 		nsIThreadPool.idl \
 		nsITimer.idl \
 		nsIRunnable.idl \
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -40,16 +40,17 @@
 #include "nsThread.h"
 #include "nsThreadManager.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIProgrammingLanguage.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "prlog.h"
 #include "nsThreadUtilsInternal.h"
+#include "mozilla/HangMonitor.h"
 
 #define HAVE_UALARM _BSD_SOURCE || (_XOPEN_SOURCE >= 500 ||                 \
                       _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED) &&           \
                       !(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
 
 #if defined(XP_UNIX) && !defined(ANDROID) && !defined(DEBUG) && HAVE_UALARM \
   && defined(_GNU_SOURCE)
 # define MOZ_CANARY
@@ -301,39 +302,27 @@ nsThread::ThreadFunc(void *arg)
   // Release any observer of the thread here.
   self->SetObserver(nsnull);
 
   NS_RELEASE(self);
 }
 
 //-----------------------------------------------------------------------------
 
-nsThread::nsThread()
-  : mLock("nsThread.mLock")
-  , mEvents(&mEventsRoot)
-  , mPriority(PRIORITY_NORMAL)
-  , mThread(nsnull)
-  , mRunningEvent(0)
-  , mStackSize(0)
-  , mShutdownContext(nsnull)
-  , mShutdownRequired(false)
-  , mEventsAreDoomed(false)
-{
-}
-
-nsThread::nsThread(PRUint32 aStackSize)
+nsThread::nsThread(MainThreadFlag aMainThread, PRUint32 aStackSize)
   : mLock("nsThread.mLock")
   , mEvents(&mEventsRoot)
   , mPriority(PRIORITY_NORMAL)
   , mThread(nsnull)
   , mRunningEvent(0)
   , mStackSize(aStackSize)
   , mShutdownContext(nsnull)
   , mShutdownRequired(false)
   , mEventsAreDoomed(false)
+  , mIsMainThread(aMainThread)
 {
 }
 
 nsThread::~nsThread()
 {
 }
 
 nsresult
@@ -580,16 +569,19 @@ void canary_alarm_handler (int signum)
 
 NS_IMETHODIMP
 nsThread::ProcessNextEvent(bool mayWait, bool *result)
 {
   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, mayWait, mRunningEvent));
 
   NS_ENSURE_STATE(PR_GetCurrentThread() == mThread);
 
+  if (MAIN_THREAD == mIsMainThread && mayWait && !ShuttingDown())
+    HangMonitor::Suspend();
+
   bool notifyGlobalObserver = (sGlobalObserver != nsnull);
   if (notifyGlobalObserver) 
     sGlobalObserver->OnProcessNextEvent(this, mayWait && !ShuttingDown(),
                                         mRunningEvent);
 
   nsCOMPtr<nsIThreadObserver> obs = mObserver;
   if (obs)
     obs->OnProcessNextEvent(this, mayWait && !ShuttingDown(), mRunningEvent);
@@ -610,29 +602,31 @@ nsThread::ProcessNextEvent(bool mayWait,
     // also do work.
 
     // If we are shutting down, then do not wait for new events.
     nsCOMPtr<nsIRunnable> event;
     mEvents->GetEvent(mayWait && !ShuttingDown(), getter_AddRefs(event));
 
 #ifdef NS_FUNCTION_TIMER
     char message[1024] = {'\0'};
-    if (NS_IsMainThread()) {
+    if (MAIN_THREAD == mIsMainThread) {
         mozilla::FunctionTimer::ft_snprintf(message, sizeof(message), 
                                             "@ Main Thread Event %p", (void*)event.get());
     }
     // If message is empty, it means that we're not on the main thread, and
     // FunctionTimer won't time this function.
     NS_TIME_FUNCTION_MIN_FMT(5.0, message);
 #endif
 
     *result = (event.get() != nsnull);
 
     if (event) {
       LOG(("THRD(%p) running [%p]\n", this, event.get()));
+      if (MAIN_THREAD == mIsMainThread)
+        HangMonitor::NotifyActivity();
       event->Run();
     } else if (mayWait) {
       NS_ASSERTION(ShuttingDown(),
                    "This should only happen when shutting down");
       rv = NS_ERROR_UNEXPECTED;
     }
   }
 
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -52,18 +52,22 @@ class nsThread : public nsIThreadInterna
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIEVENTTARGET
   NS_DECL_NSITHREAD
   NS_DECL_NSITHREADINTERNAL
   NS_DECL_NSISUPPORTSPRIORITY
 
-  nsThread();
-  nsThread(PRUint32 aStackSize);
+  enum MainThreadFlag {
+    MAIN_THREAD,
+    NOT_MAIN_THREAD
+  };
+
+  nsThread(MainThreadFlag aMainThread, PRUint32 aStackSize);
 
   // Initialize this as a wrapper for a new PRThread.
   nsresult Init();
 
   // Initialize this as a wrapper for the current PRThread.
   nsresult InitCurrentThread();
 
   // The PRThread corresponding to this thread.
@@ -142,16 +146,17 @@ private:
   PRUint32  mStackSize;
 
   struct nsThreadShutdownContext *mShutdownContext;
 
   bool mShutdownRequired;
   bool mShutdownPending;
   // Set to true when events posted to this thread will never run.
   bool mEventsAreDoomed;
+  MainThreadFlag mIsMainThread;
 };
 
 //-----------------------------------------------------------------------------
 
 class nsThreadSyncDispatch : public nsRunnable {
 public:
   nsThreadSyncDispatch(nsIThread *origin, nsIRunnable *task)
     : mOrigin(origin), mSyncTask(task), mResult(NS_ERROR_NOT_INITIALIZED) {
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -93,17 +93,17 @@ nsThreadManager::Init()
     return NS_ERROR_OUT_OF_MEMORY;
 
   if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseObject) == PR_FAILURE)
     return NS_ERROR_FAILURE;
 
   mLock = new Mutex("nsThreadManager.mLock");
 
   // Setup "main" thread
-  mMainThread = new nsThread();
+  mMainThread = new nsThread(nsThread::MAIN_THREAD, 0);
   if (!mMainThread)
     return NS_ERROR_OUT_OF_MEMORY;
 
   nsresult rv = mMainThread->InitCurrentThread();
   if (NS_FAILED(rv)) {
     mMainThread = nsnull;
     return rv;
   }
@@ -219,32 +219,32 @@ nsThreadManager::GetCurrentThread()
   if (data)
     return static_cast<nsThread *>(data);
 
   if (!mInitialized) {
     return nsnull;
   }
 
   // OK, that's fine.  We'll dynamically create one :-)
-  nsRefPtr<nsThread> thread = new nsThread();
+  nsRefPtr<nsThread> thread = new nsThread(nsThread::NOT_MAIN_THREAD, 0);
   if (!thread || NS_FAILED(thread->InitCurrentThread()))
     return nsnull;
 
   return thread.get();  // reference held in TLS
 }
 
 NS_IMETHODIMP
 nsThreadManager::NewThread(PRUint32 creationFlags,
                            PRUint32 stackSize,
                            nsIThread **result)
 {
   // No new threads during Shutdown
   NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
 
-  nsThread *thr = new nsThread(stackSize);
+  nsThread *thr = new nsThread(nsThread::NOT_MAIN_THREAD, stackSize);
   if (!thr)
     return NS_ERROR_OUT_OF_MEMORY;
   NS_ADDREF(thr);
 
   nsresult rv = thr->Init();
   if (NS_FAILED(rv)) {
     NS_RELEASE(thr);
     return rv;