Commit 8c0dd1bc authored by Alex Thayer's avatar Alex Thayer
Browse files

Bug 1510226 - Do not block main thread in nsThread::Init r=froydnj,KrisWright

parent ca66dacd
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -32,7 +32,9 @@ ThreadEventTarget::ThreadEventTarget(ThreadTargetSink* aSink,
  mThread = PR_GetCurrentThread();
}

void ThreadEventTarget::SetCurrentThread() { mThread = PR_GetCurrentThread(); }
void ThreadEventTarget::SetCurrentThread(PRThread* aThread) {
  mThread = aThread;
}

void ThreadEventTarget::ClearCurrentThread() { mThread = nullptr; }

+1 −1
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ class ThreadEventTarget final : public nsISerialEventTarget {

  // Sets the thread for which IsOnCurrentThread returns true to the current
  // thread.
  void SetCurrentThread();
  void SetCurrentThread(PRThread* aThread);
  // Call ClearCurrentThread() before the PRThread is deleted on thread join.
  void ClearCurrentThread();

+25 −62
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include "pratom.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Logging.h"
#include "nsIObserverService.h"
#include "mozilla/IOInterposer.h"
@@ -201,37 +202,6 @@ NS_IMPL_CI_INTERFACE_GETTER(nsThread, nsIThread, nsIThreadInternal,

//-----------------------------------------------------------------------------

class nsThreadStartupEvent final : public Runnable {
 public:
  nsThreadStartupEvent()
      : Runnable("nsThreadStartupEvent"),
        mMon("nsThreadStartupEvent.mMon"),
        mInitialized(false) {}

  // This method does not return until the thread startup object is in the
  // completion state.
  void Wait() {
    ReentrantMonitorAutoEnter mon(mMon);
    while (!mInitialized) {
      mon.Wait();
    }
  }

 private:
  ~nsThreadStartupEvent() = default;

  NS_IMETHOD Run() override {
    ReentrantMonitorAutoEnter mon(mMon);
    mInitialized = true;
    mon.Notify();
    return NS_OK;
  }

  ReentrantMonitor mMon;
  bool mInitialized;
};
//-----------------------------------------------------------------------------

bool nsThread::ShutdownContextsComp::Equals(
    const ShutdownContexts::elem_type& a,
    const ShutdownContexts::elem_type::Pointer b) const {
@@ -340,7 +310,7 @@ namespace {

struct ThreadInitData {
  nsThread* thread;
  const nsACString& name;
  nsCString name;
};

}  // namespace
@@ -393,14 +363,16 @@ void nsThread::MaybeRemoveFromThreadList() {
void nsThread::ThreadFunc(void* aArg) {
  using mozilla::ipc::BackgroundChild;

  ThreadInitData* initData = static_cast<ThreadInitData*>(aArg);
  UniquePtr<ThreadInitData> initData(static_cast<ThreadInitData*>(aArg));
  nsThread* self = initData->thread;  // strong reference

  MOZ_ASSERT(self->mEventTarget);
  MOZ_ASSERT(self->mEvents);

  self->mThread = PR_GetCurrentThread();
  self->mEventTarget->SetCurrentThread();
  // Note: see the comment in nsThread::Init, where we set these same values.
  DebugOnly<PRThread*> prev = self->mThread.exchange(PR_GetCurrentThread());
  MOZ_ASSERT(!prev || prev == PR_GetCurrentThread());
  self->mEventTarget->SetCurrentThread(self->mThread);
  SetupCurrentThreadForChaosMode();

  if (!initData->name.IsEmpty()) {
@@ -424,15 +396,6 @@ void nsThread::ThreadFunc(void* aArg) {
  }
#endif  // MOZ_GECKO_PROFILER

  // Wait for and process startup event
  nsCOMPtr<nsIRunnable> event = self->mEvents->GetEvent(true, nullptr);
  MOZ_ASSERT(event);

  initData = nullptr;  // clear before unblocking nsThread::Init

  event->Run();  // unblocks nsThread::Init
  event = nullptr;

  {
    // Scope for MessageLoop.
    MessageLoop loop(MessageLoop::TYPE_MOZILLA_NONMAINTHREAD, self);
@@ -478,7 +441,8 @@ void nsThread::ThreadFunc(void* aArg) {
  NotNull<nsThreadShutdownContext*> context =
      WrapNotNull(self->mShutdownContext);
  MOZ_ASSERT(context->mTerminatingThread == self);
  event = do_QueryObject(new nsThreadShutdownAckEvent(context));
  nsCOMPtr<nsIRunnable> event =
      do_QueryObject(new nsThreadShutdownAckEvent(context));
  if (context->mIsMainThreadJoining) {
    SchedulerGroup::Dispatch(TaskCategory::Other, event.forget());
  } else {
@@ -644,35 +608,34 @@ nsThread::~nsThread() {
nsresult nsThread::Init(const nsACString& aName) {
  MOZ_ASSERT(mEvents);
  MOZ_ASSERT(mEventTarget);

  // spawn thread and wait until it is fully setup
  RefPtr<nsThreadStartupEvent> startup = new nsThreadStartupEvent();
  MOZ_ASSERT(!mThread);

  NS_ADDREF_THIS();

  mShutdownRequired = true;

  ThreadInitData initData = {this, aName};
  UniquePtr<ThreadInitData> initData(
      new ThreadInitData{this, nsCString(aName)});

  PRThread* thread = nullptr;
  // ThreadFunc is responsible for setting mThread
  if (!PR_CreateThread(PR_USER_THREAD, ThreadFunc, &initData,
                       PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
                       mStackSize)) {
  if (!(thread = PR_CreateThread(PR_USER_THREAD, ThreadFunc, initData.get(),
                                 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
  // that mThread is set properly.
  {
    mEvents->PutEvent(do_AddRef(startup),
                      EventQueuePriority::Normal);  // retain a reference
  }
  // The created thread now owns initData, so release our ownership of it.
  Unused << initData.release();

  // Note: we set these both here and inside ThreadFunc, to what should be
  // the same value. This is because calls within ThreadFunc need these values
  // to be set, and our callers need these values to be set.
  DebugOnly<PRThread*> prev = mThread.exchange(thread);
  MOZ_ASSERT(!prev || prev == thread);

  // Wait for thread to call ThreadManager::SetupCurrentThread, which completes
  // initialization of ThreadFunc.
  startup->Wait();
  mEventTarget->SetCurrentThread(thread);
  return NS_OK;
}