Bug 1479035: Part 2 - Get rid of PRThread to nsThread map. r=erahm
☠☠ backed out by 0f22d244af4d ☠ ☠
authorKris Maglione <maglione.k@gmail.com>
Fri, 27 Jul 2018 15:26:08 -0700
changeset 496778 5f870621361012ba459943212d8c68a9ff81cb16
parent 496777 a03a61d6d724503c3b7c5e31fe32ced1f5d1c219
child 496779 92cde451be94e8872abb9068da5d89197bd3e925
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerserahm
bugs1479035
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1479035: Part 2 - Get rid of PRThread to nsThread map. r=erahm These maps hold strong references which complicate nsThread lifetime handling considerably, and only have a couple of fringe uses. We have a linked list of active threads that the thread manager can use for its internal enumeration purposes, and the external uses are easily done away with, so there doesn't seem to be much reason to keep the map around. MozReview-Commit-ID: x7dsj6C4x8
dom/workers/WorkerPrivate.cpp
storage/test/gtest/storage_test_harness.h
xpcom/threads/nsIThreadManager.idl
xpcom/threads/nsThread.cpp
xpcom/threads/nsThread.h
xpcom/threads/nsThreadManager.cpp
xpcom/threads/nsThreadManager.h
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5349,32 +5349,23 @@ WorkerPrivate::CreateDebuggerGlobalScope
   JS_FireOnNewGlobalObject(aCx, global);
 
   return mDebuggerScope;
 }
 
 bool
 WorkerPrivate::IsOnWorkerThread() const
 {
-  // This is much more complicated than it needs to be but we can't use mThread
-  // because it must be protected by mMutex and sometimes this method is called
-  // when mMutex is already locked. This method should always work.
+  // We can't use mThread because it must be protected by mMutex and sometimes
+  // this method is called when mMutex is already locked. This method should
+  // always work.
   MOZ_ASSERT(mPRThread,
              "AssertIsOnWorkerThread() called before a thread was assigned!");
 
-  nsCOMPtr<nsIThread> thread;
-  nsresult rv =
-    nsThreadManager::get().GetThreadFromPRThread(mPRThread,
-                                                 getter_AddRefs(thread));
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  MOZ_ASSERT(thread);
-
-  bool current;
-  rv = thread->IsOnCurrentThread(&current);
-  return NS_SUCCEEDED(rv) && current;
+  return mPRThread == PR_GetCurrentThread();
 }
 
 #ifdef DEBUG
 void
 WorkerPrivate::AssertIsOnWorkerThread() const
 {
   MOZ_ASSERT(IsOnWorkerThread());
 }
--- a/storage/test/gtest/storage_test_harness.h
+++ b/storage/test/gtest/storage_test_harness.h
@@ -220,29 +220,28 @@ PRThread *watched_thread = nullptr;
  * Ugly hack to let us figure out what a connection's async thread is.  If we
  * were MOZILLA_INTERNAL_API and linked as such we could just include
  * mozStorageConnection.h and just ask Connection directly.  But that turns out
  * poorly.
  *
  * When the thread a mutex is invoked on isn't watched_thread we save it to this
  * variable.
  */
-PRThread *last_non_watched_thread = nullptr;
+nsIThread* last_non_watched_thread = nullptr;
 
 /**
  * Set a flag if the mutex is used on the thread we are watching, but always
  * call the real mutex function.
  */
 extern "C" void wrapped_MutexEnter(sqlite3_mutex *mutex)
 {
-  PRThread *curThread = ::PR_GetCurrentThread();
-  if (curThread == watched_thread)
+  if (PR_GetCurrentThread() == watched_thread)
     mutex_used_on_watched_thread = true;
   else
-    last_non_watched_thread = curThread;
+    last_non_watched_thread = NS_GetCurrentThread();
   orig_mutex_methods.xMutexEnter(mutex);
 }
 
 extern "C" int wrapped_MutexTry(sqlite3_mutex *mutex)
 {
   if (::PR_GetCurrentThread() == watched_thread)
     mutex_used_on_watched_thread = true;
   return orig_mutex_methods.xMutexTry(mutex);
@@ -346,26 +345,20 @@ get_conn_async_thread(mozIStorageConnect
   // - statement with nothing to bind
   nsCOMPtr<mozIStorageAsyncStatement> stmt;
   db->CreateAsyncStatement(
     NS_LITERAL_CSTRING("SELECT 1"),
     getter_AddRefs(stmt));
   blocking_async_execute(stmt);
   stmt->Finalize();
 
-  nsCOMPtr<nsIThreadManager> threadMan =
-    do_GetService("@mozilla.org/thread-manager;1");
-  nsCOMPtr<nsIThread> asyncThread;
-  threadMan->GetThreadFromPRThread(last_non_watched_thread,
-                                   getter_AddRefs(asyncThread));
+  nsCOMPtr<nsIThread> asyncThread = last_non_watched_thread;
 
   // Additionally, check that the thread we get as the background thread is the
   // same one as the one we report from getInterface.
   nsCOMPtr<nsIEventTarget> target = do_GetInterface(db);
   nsCOMPtr<nsIThread> allegedAsyncThread = do_QueryInterface(target);
-  PRThread *allegedPRThread;
-  (void)allegedAsyncThread->GetPRThread(&allegedPRThread);
-  do_check_eq(allegedPRThread, last_non_watched_thread);
+  do_check_eq(allegedAsyncThread, asyncThread);
   return asyncThread.forget();
 }
 
 #endif // storage_test_harness_h__
 
--- a/xpcom/threads/nsIThreadManager.idl
+++ b/xpcom/threads/nsIThreadManager.idl
@@ -80,29 +80,16 @@ interface nsIThreadManager : nsISupports
    *   default.
    *
    * @returns
    *   The newly created nsIThread object.
    */
   [noscript] nsIThread newNamedThread(in ACString name, [optional] in unsigned long stackSize);
 
   /**
-   * Get the nsIThread object (if any) corresponding to the given PRThread.
-   * This method returns null if there is no corresponding nsIThread.
-   *
-   * @param prthread
-   *   The PRThread of the nsIThread being requested.
-   *
-   * @returns
-   *   The nsIThread object corresponding to the given PRThread or null if no
-   *   such nsIThread exists.
-   */
-  [noscript] nsIThread getThreadFromPRThread(in PRThread prthread);
-
-  /**
    * Get the main thread.
    */
   readonly attribute nsIThread mainThread;
 
   /**
    * Get the current thread.  If the calling thread does not already have a
    * nsIThread associated with it, then a new nsIThread will be created and
    * associated with the current PRThread.
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -110,16 +110,19 @@ static LazyLogModule sThreadLog("nsThrea
 #undef LOG
 #endif
 #define LOG(args) MOZ_LOG(sThreadLog, mozilla::LogLevel::Debug, args)
 
 NS_DECL_CI_INTERFACE_GETTER(nsThread)
 
 Array<char, nsThread::kRunnableNameBufSize> nsThread::sMainThreadRunnableName;
 
+uint32_t nsThread::sActiveThreads;
+uint32_t nsThread::sMaxActiveThreads;
+
 //-----------------------------------------------------------------------------
 // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
 // somewhat manually.
 
 class nsThreadClassInfo : public nsIClassInfo
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED  // no mRefCnt
@@ -411,16 +414,50 @@ nsThread::ClearThreadList()
 }
 
 /* static */ nsThreadEnumerator
 nsThread::Enumerate()
 {
   return {};
 }
 
+/* static */ uint32_t
+nsThread::MaxActiveThreads()
+{
+  OffTheBooksMutexAutoLock mal(ThreadListMutex());
+  return sMaxActiveThreads;
+}
+
+void
+nsThread::AddToThreadList()
+{
+  OffTheBooksMutexAutoLock mal(ThreadListMutex());
+  MOZ_ASSERT(!isInList());
+
+  sActiveThreads++;
+  sMaxActiveThreads = std::max(sActiveThreads, sMaxActiveThreads);
+
+  ThreadList().insertBack(this);
+}
+
+void
+nsThread::MaybeRemoveFromThreadList()
+{
+  // We shouldn't need to lock before checking isInList at this point. We're
+  // destroying the last reference to this object, so there's no way for anyone
+  // else to remove it in the middle of our check. And the not-in-list state is
+  // determined the element's next and previous members pointing to itself, so a
+  // non-atomic update to an adjacent member won't affect the outcome either.
+  if (isInList()) {
+    OffTheBooksMutexAutoLock mal(ThreadListMutex());
+    sActiveThreads--;
+    removeFrom(ThreadList());
+  }
+}
+
 /*static*/ void
 nsThread::ThreadFunc(void* aArg)
 {
   using mozilla::ipc::BackgroundChild;
 
   ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
   nsThread* self = initData->thread;  // strong reference
 
@@ -583,18 +620,17 @@ nsThread::InitCommon()
       ULONG_PTR stackBottom, stackTop;
       sGetStackLimits(&stackBottom, &stackTop);
       mStackBase = reinterpret_cast<void*>(stackBottom);
       mStackSize = stackTop - stackBottom;
     }
 #endif
   }
 
-  OffTheBooksMutexAutoLock mal(ThreadListMutex());
-  ThreadList().insertBack(this);
+  AddToThreadList();
 }
 
 //-----------------------------------------------------------------------------
 
 #ifdef MOZ_CANARY
 int sCanaryOutputFD = -1;
 #endif
 
@@ -640,25 +676,17 @@ nsThread::nsThread()
   MOZ_ASSERT(!NS_IsMainThread());
 }
 
 nsThread::~nsThread()
 {
   NS_ASSERTION(mRequestedShutdownContexts.IsEmpty(),
                "shouldn't be waiting on other threads to shutdown");
 
-  // We shouldn't need to lock before checking isInList at this point. We're
-  // destroying the last reference to this object, so there's no way for anyone
-  // else to remove it in the middle of our check. And the not-in-list state is
-  // determined the element's next and previous members pointing to itself, so a
-  // non-atomic update to an adjacent member won't affect the outcome either.
-  if (isInList()) {
-    OffTheBooksMutexAutoLock mal(ThreadListMutex());
-    removeFrom(ThreadList());
-  }
+  MaybeRemoveFromThreadList();
 
 #ifdef DEBUG
   // We deliberately leak these so they can be tracked by the leak checker.
   // If you're having nsThreadShutdownContext leaks, you can set:
   //   XPCOM_MEM_LOG_CLASSES=nsThreadShutdownContext
   // during a test run and that will at least tell you what thread is
   // requesting shutdown on another, which can be helpful for diagnosing
   // the leak.
@@ -816,22 +844,17 @@ nsThread::ShutdownInternal(bool aSync)
     return nullptr;
   }
 
   // Prevent multiple calls to this method
   if (!mShutdownRequired.compareExchange(true, false)) {
     return nullptr;
   }
 
-  {
-    OffTheBooksMutexAutoLock mal(ThreadListMutex());
-    if (isInList()) {
-      removeFrom(ThreadList());
-    }
-  }
+  MaybeRemoveFromThreadList();
 
   NotNull<nsThread*> currentThread =
     WrapNotNull(nsThreadManager::get().GetCurrentThread());
 
   nsAutoPtr<nsThreadShutdownContext>& context =
     *currentThread->mRequestedShutdownContexts.AppendElement();
   context = new nsThreadShutdownContext(WrapNotNull(this), currentThread, aSync);
 
@@ -851,22 +874,17 @@ nsThread::ShutdownInternal(bool aSync)
 void
 nsThread::ShutdownComplete(NotNull<nsThreadShutdownContext*> aContext)
 {
   MOZ_ASSERT(mEvents);
   MOZ_ASSERT(mEventTarget);
   MOZ_ASSERT(mThread);
   MOZ_ASSERT(aContext->mTerminatingThread == this);
 
-  {
-    OffTheBooksMutexAutoLock mal(ThreadListMutex());
-    if (isInList()) {
-      removeFrom(ThreadList());
-    }
-  }
+  MaybeRemoveFromThreadList();
 
   if (aContext->mAwaitingShutdownAck) {
     // We're in a synchronous shutdown, so tell whatever is up the stack that
     // we're done and unwind the stack so it can call us again.
     aContext->mAwaitingShutdownAck = false;
     return;
   }
 
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -145,16 +145,18 @@ public:
   // Returns the size of this object, its PRThread, and its shutdown contexts,
   // but excluding its event queues.
   size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   size_t SizeOfEventQueues(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   static nsThreadEnumerator Enumerate();
 
+  static uint32_t MaxActiveThreads();
+
 private:
   void DoMainThreadSpecificProcessing(bool aReallyWait);
 
 protected:
   friend class nsThreadShutdownEvent;
 
   friend class nsThreadEnumerator;
 
@@ -173,16 +175,25 @@ protected:
   struct nsThreadShutdownContext* ShutdownInternal(bool aSync);
 
   friend class nsThreadManager;
 
   static mozilla::OffTheBooksMutex& ThreadListMutex();
   static mozilla::LinkedList<nsThread>& ThreadList();
   static void ClearThreadList();
 
+  // The current number of active threads.
+  static uint32_t sActiveThreads;
+  // The maximum current number of active threads we've had in this session.
+  static uint32_t sMaxActiveThreads;
+
+  void AddToThreadList();
+  void MaybeRemoveFromThreadList();
+
+
   // Whether or not these members have a value determines whether the nsThread
   // is treated as a full XPCOM thread or as a thin wrapper.
   //
   // For full nsThreads, they will always contain valid pointers. For thin
   // wrappers around non-XPCOM threads, they will be null, and event dispatch
   // methods which rely on them will fail (and assert) if called.
   RefPtr<mozilla::SynchronizedEventQueue> mEvents;
   RefPtr<mozilla::ThreadEventTarget> mEventTarget;
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -104,18 +104,16 @@ nsThreadManager::ReleaseThread(void* aDa
   if (sShutdownComplete) {
     // We've already completed shutdown and released the references to all or
     // our TLS wrappers. Don't try to release them again.
     return;
   }
 
   auto* thread = static_cast<nsThread*>(aData);
 
-  get().UnregisterCurrentThread(*thread, true);
-
   if (thread->mHasTLSEntry) {
     thread->mHasTLSEntry = false;
     thread->Release();
   }
 }
 
 // statically allocated instance
 NS_IMETHODIMP_(MozExternalRefCountType)
@@ -316,62 +314,50 @@ nsThreadManager::Shutdown()
   //
   mInitialized = false;
 
   // Empty the main thread event queue before we begin shutting down threads.
   NS_ProcessPendingEvents(mMainThread);
 
   {
     // We gather the threads from the hashtable into a list, so that we avoid
-    // holding the hashtable lock while calling nsIThread::Shutdown.
-    nsThreadArray threads;
-    {
-      OffTheBooksMutexAutoLock lock(mLock);
-      for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) {
-        RefPtr<nsThread>& thread = iter.Data();
-        threads.AppendElement(WrapNotNull(thread));
-        iter.Remove();
+    // holding the enumerator lock while calling nsIThread::Shutdown.
+    nsTArray<RefPtr<nsThread>> threadsToShutdown;
+    for (auto* thread : nsThread::Enumerate()) {
+      if (thread->ShutdownRequired()) {
+        threadsToShutdown.AppendElement(thread);
       }
     }
 
     // It's tempting to walk the list of threads here and tell them each to stop
     // accepting new events, but that could lead to badness if one of those
     // threads is stuck waiting for a response from another thread.  To do it
     // right, we'd need some way to interrupt the threads.
     //
     // Instead, we process events on the current thread while waiting for threads
     // to shutdown.  This means that we have to preserve a mostly functioning
     // world until such time as the threads exit.
 
     // Shutdown all threads that require it (join with threads that we created).
-    for (uint32_t i = 0; i < threads.Length(); ++i) {
-      NotNull<nsThread*> thread = threads[i];
-      if (thread->ShutdownRequired()) {
-        thread->Shutdown();
-      }
+    for (auto& thread : threadsToShutdown) {
+      thread->Shutdown();
     }
   }
 
   // NB: It's possible that there are events in the queue that want to *start*
   // an asynchronous shutdown. But we have already shutdown the threads above,
   // so there's no need to worry about them. We only have to wait for all
   // in-flight asynchronous thread shutdowns to complete.
   mMainThread->WaitForAllAsynchronousShutdowns();
 
   // In case there are any more events somehow...
   NS_ProcessPendingEvents(mMainThread);
 
   // There are no more background threads at this point.
 
-  // Clear the table of threads.
-  {
-    OffTheBooksMutexAutoLock lock(mLock);
-    mThreadsByPRThread.Clear();
-  }
-
   // Normally thread shutdown clears the observer for the thread, but since the
   // main thread is special we do it manually here after we're sure all events
   // have been processed.
   mMainThread->SetObserver(nullptr);
 
   // Release main thread object.
   mMainThread = nullptr;
 
@@ -397,45 +383,25 @@ nsThreadManager::Shutdown()
   sShutdownComplete = true;
 }
 
 void
 nsThreadManager::RegisterCurrentThread(nsThread& aThread)
 {
   MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
 
-  OffTheBooksMutexAutoLock lock(mLock);
-
-  ++mCurrentNumberOfThreads;
-  if (mCurrentNumberOfThreads > mHighestNumberOfThreads) {
-    mHighestNumberOfThreads = mCurrentNumberOfThreads;
-  }
-
-  mThreadsByPRThread.Put(aThread.GetPRThread(), &aThread);  // XXX check OOM?
-
   aThread.AddRef();  // for TLS entry
   aThread.mHasTLSEntry = true;
   PR_SetThreadPrivate(mCurThreadIndex, &aThread);
 }
 
 void
-nsThreadManager::UnregisterCurrentThread(nsThread& aThread, bool aIfExists)
+nsThreadManager::UnregisterCurrentThread(nsThread& aThread)
 {
-  {
-    OffTheBooksMutexAutoLock lock(mLock);
-
-    if (aIfExists && !mThreadsByPRThread.GetWeak(aThread.GetPRThread())) {
-      return;
-    }
-
-    MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
-
-    --mCurrentNumberOfThreads;
-    mThreadsByPRThread.Remove(aThread.GetPRThread());
-  }
+  MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread");
 
   PR_SetThreadPrivate(mCurThreadIndex, nullptr);
   // Ref-count balanced via ReleaseThread
 }
 
 nsThread*
 nsThreadManager::CreateCurrentThread(SynchronizedEventQueue* aQueue,
                                      nsThread::MainThreadFlag aMainThread)
@@ -533,37 +499,16 @@ nsThreadManager::NewNamedThread(const ns
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   thr.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsThreadManager::GetThreadFromPRThread(PRThread* aThread, nsIThread** aResult)
-{
-  // Keep this functioning during Shutdown
-  if (NS_WARN_IF(!mMainThread)) {
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-  if (NS_WARN_IF(!aThread)) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  RefPtr<nsThread> temp;
-  {
-    OffTheBooksMutexAutoLock lock(mLock);
-    mThreadsByPRThread.Get(aThread, getter_AddRefs(temp));
-  }
-
-  NS_IF_ADDREF(*aResult = temp);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsThreadManager::GetMainThread(nsIThread** aResult)
 {
   // Keep this functioning during Shutdown
   if (NS_WARN_IF(!mMainThread)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
   NS_ADDREF(*aResult = mMainThread);
   return NS_OK;
@@ -655,18 +600,17 @@ nsThreadManager::GetSystemGroupEventTarg
   nsCOMPtr<nsIEventTarget> target = SystemGroup::EventTargetFor(TaskCategory::Other);
   target.forget(aTarget);
   return NS_OK;
 }
 
 uint32_t
 nsThreadManager::GetHighestNumberOfThreads()
 {
-  OffTheBooksMutexAutoLock lock(mLock);
-  return mHighestNumberOfThreads;
+  return nsThread::MaxActiveThreads();
 }
 
 NS_IMETHODIMP
 nsThreadManager::DispatchToMainThread(nsIRunnable *aEvent, uint32_t aPriority)
 {
   // Note: C++ callers should instead use NS_DispatchToMainThread.
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/xpcom/threads/nsThreadManager.h
+++ b/xpcom/threads/nsThreadManager.h
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsThreadManager_h__
 #define nsThreadManager_h__
 
 #include "mozilla/Mutex.h"
 #include "nsIThreadManager.h"
-#include "nsRefPtrHashtable.h"
 #include "nsThread.h"
 
 class nsIRunnable;
 
 class nsThreadManager : public nsIThreadManager
 {
 public:
   NS_DECL_ISUPPORTS
@@ -31,17 +30,17 @@ public:
   void Shutdown();
 
   // Called by nsThread to inform the ThreadManager it exists.  This method
   // must be called when the given thread is the current thread.
   void RegisterCurrentThread(nsThread& aThread);
 
   // Called by nsThread to inform the ThreadManager it is going away.  This
   // method must be called when the given thread is the current thread.
-  void UnregisterCurrentThread(nsThread& aThread, bool aIfExists = false);
+  void UnregisterCurrentThread(nsThread& aThread);
 
   // Returns the current thread.  Returns null if OOM or if ThreadManager isn't
   // initialized.  Creates the nsThread if one does not exist yet.
   nsThread* GetCurrentThread();
 
   // Returns true iff the currently running thread has an nsThread associated
   // with it (ie; whether this is a thread that we can dispatch runnables to).
   bool IsNSThread() const;
@@ -69,42 +68,32 @@ public:
   void FlushInputEventPrioritization();
   void SuspendInputEventPrioritization();
   void ResumeInputEventPrioritization();
 
 private:
   nsThreadManager()
     : mCurThreadIndex(0)
     , mMainPRThread(nullptr)
-    , mLock("nsThreadManager.mLock")
     , mInitialized(false)
-    , mCurrentNumberOfThreads(1)
-    , mHighestNumberOfThreads(1)
   {
   }
 
   nsresult
   SpinEventLoopUntilInternal(nsINestedEventLoopCondition* aCondition,
                              bool aCheckingShutdown);
 
   static void ReleaseThread(void* aData);
 
-  nsRefPtrHashtable<nsPtrHashKey<PRThread>, nsThread> mThreadsByPRThread;
   unsigned            mCurThreadIndex;  // thread-local-storage index
   RefPtr<nsThread>  mMainThread;
   PRThread*         mMainPRThread;
-  mozilla::OffTheBooksMutex mLock;  // protects tables
   mozilla::Atomic<bool,
                   mozilla::SequentiallyConsistent,
                   mozilla::recordreplay::Behavior::DontPreserve> mInitialized;
-
-  // The current number of threads
-  uint32_t            mCurrentNumberOfThreads;
-  // The highest number of threads encountered so far during the session
-  uint32_t            mHighestNumberOfThreads;
 };
 
 #define NS_THREADMANAGER_CID                       \
 { /* 7a4204c6-e45a-4c37-8ebb-6709a22c917c */       \
   0x7a4204c6,                                      \
   0xe45a,                                          \
   0x4c37,                                          \
   {0x8e, 0xbb, 0x67, 0x09, 0xa2, 0x2c, 0x91, 0x7c} \