Bug 1323100 - Add nsThreadManager::NewNamedThread API. r?froydnj draft
authorMarkus Stange <mstange@themasta.com>
Tue, 20 Dec 2016 15:10:20 +0100
changeset 456884 bd93ef94d0b494db38464df65ed14a0c000b639c
parent 456883 05bdf050aba962bb73537e11c1f05efea6018a5a
child 456885 e31e965d18dccbf9fdc5f98feb8f721139c099f7
push id40636
push userbmo:mstange@themasta.com
push dateFri, 06 Jan 2017 12:38:48 +0000
reviewersfroydnj
bugs1323100
milestone53.0a1
Bug 1323100 - Add nsThreadManager::NewNamedThread API. r?froydnj The point of this exercise is to make the thread name available in the thread func of the thread, so that we can register the thread with the profiler from the very start of its lifetime, and so that registration and unregistration can be inside the same function. MozReview-Commit-ID: DiiMKUQVr55
xpcom/threads/nsIThreadManager.idl
xpcom/threads/nsThread.cpp
xpcom/threads/nsThread.h
xpcom/threads/nsThreadManager.cpp
--- a/xpcom/threads/nsIThreadManager.idl
+++ b/xpcom/threads/nsIThreadManager.idl
@@ -31,16 +31,30 @@ interface nsIThreadManager : nsISupports
    *   Number of bytes to reserve for the thread's stack.
    *
    * @returns
    *   The newly created nsIThread object.
    */
   nsIThread newThread(in unsigned long creationFlags, [optional] in unsigned long stackSize);
 
   /**
+   * Create a new thread (a global, user PRThread) with the specified name.
+   *
+   * @param name
+   *   The name of the thread. Passing an empty name is equivalent to
+   *   calling newThread(0, stackSize), i.e. the thread will not be named.
+   * @param stackSize
+   *   Number of bytes to reserve for the thread's stack.
+   *
+   * @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
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -427,39 +427,57 @@ SetupCurrentThreadForChaosMode()
 #endif
 
   // Force half the threads to CPU 0 so they compete for CPU
   if (ChaosMode::randomUint32LessThan(2)) {
     SetThreadAffinity(0);
   }
 }
 
+namespace {
+
+struct ThreadInitData {
+  nsThread* thread;
+  const nsACString& name;
+};
+
+}
+
 /*static*/ void
 nsThread::ThreadFunc(void* aArg)
 {
   using mozilla::ipc::BackgroundChild;
 
-  nsThread* self = static_cast<nsThread*>(aArg);  // strong reference
+  ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
+  nsThread* self = initData->thread;  // strong reference
+
   self->mThread = PR_GetCurrentThread();
   SetupCurrentThreadForChaosMode();
 
+  if (initData->name.Length() > 0) {
+    PR_SetCurrentThreadName(initData->name.BeginReading());
+  }
+
   // Inform the ThreadManager
   nsThreadManager::get().RegisterCurrentThread(*self);
 
   mozilla::IOInterposer::RegisterCurrentThread();
 
   // Wait for and process startup event
   nsCOMPtr<nsIRunnable> event;
   {
     MutexAutoLock lock(self->mLock);
     if (!self->mEvents->GetEvent(true, getter_AddRefs(event), lock)) {
       NS_WARNING("failed waiting for thread startup event");
       return;
     }
   }
+
+  initData = nullptr; // clear before unblocking nsThread::Init
+
   event->Run();  // unblocks nsThread::Init
   event = nullptr;
 
   {
     // Scope for MessageLoop.
     nsAutoPtr<MessageLoop> loop(
       new MessageLoop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD, self));
 
@@ -623,29 +641,31 @@ nsThread::~nsThread()
   // the leak.
   for (size_t i = 0; i < mRequestedShutdownContexts.Length(); ++i) {
     Unused << mRequestedShutdownContexts[i].forget();
   }
 #endif
 }
 
 nsresult
-nsThread::Init()
+nsThread::Init(const nsACString& aName)
 {
   // spawn thread and wait until it is fully setup
   RefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
 
   NS_ADDREF_THIS();
 
   mIdlePeriod = new IdlePeriod();
 
   mShutdownRequired = true;
 
+  ThreadInitData initData = { this, aName };
+
   // ThreadFunc is responsible for setting mThread
-  if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, this,
+  if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, &initData,
                        PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
                        PR_JOINABLE_THREAD, mStackSize)) {
     NS_RELEASE_THIS();
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // ThreadFunc will wait for this event to be run before it tries to access
   // mThread.  By delaying insertion of this event into the queue, we ensure
--- a/xpcom/threads/nsThread.h
+++ b/xpcom/threads/nsThread.h
@@ -43,18 +43,18 @@ public:
   enum MainThreadFlag
   {
     MAIN_THREAD,
     NOT_MAIN_THREAD
   };
 
   nsThread(MainThreadFlag aMainThread, uint32_t aStackSize);
 
-  // Initialize this as a wrapper for a new PRThread.
-  nsresult Init();
+  // Initialize this as a wrapper for a new PRThread, and optionally give it a name.
+  nsresult Init(const nsACString& aName = NS_LITERAL_CSTRING(""));
 
   // Initialize this as a wrapper for the current PRThread.
   nsresult InitCurrentThread();
 
   // The PRThread corresponding to this thread.
   PRThread* GetPRThread()
   {
     return mThread;
--- a/xpcom/threads/nsThreadManager.cpp
+++ b/xpcom/threads/nsThreadManager.cpp
@@ -244,25 +244,33 @@ nsThreadManager::GetCurrentThread()
   return thread.get();  // reference held in TLS
 }
 
 NS_IMETHODIMP
 nsThreadManager::NewThread(uint32_t aCreationFlags,
                            uint32_t aStackSize,
                            nsIThread** aResult)
 {
+  return NewNamedThread(NS_LITERAL_CSTRING(""), aStackSize, aResult);
+}
+
+NS_IMETHODIMP
+nsThreadManager::NewNamedThread(const nsACString& aName,
+                                uint32_t aStackSize,
+                                nsIThread** aResult)
+{
   // Note: can be called from arbitrary threads
   
   // No new threads during Shutdown
   if (NS_WARN_IF(!mInitialized)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   RefPtr<nsThread> thr = new nsThread(nsThread::NOT_MAIN_THREAD, aStackSize);
-  nsresult rv = thr->Init();  // Note: blocks until the new thread has been set up
+  nsresult rv = thr->Init(aName);  // Note: blocks until the new thread has been set up
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // At this point, we expect that the thread has been registered in mThreadByPRThread;
   // however, it is possible that it could have also been replaced by now, so
   // we cannot really assert that it was added.  Instead, kill it if we entered
   // Shutdown() during/before Init()