bug 1328964 terminate worklet thread during xpcom shutdown r=baku draft
authorKarl Tomlinson <karlt+@karlt.net>
Thu, 12 Apr 2018 16:26:08 +1200
changeset 780866 58e4aeef280ca23466367c713f3669a74e468f6e
parent 780865 5867b4d64462c598b657634d9ca9fb6893977c42
child 780867 8069d1adb73660787c62993ddebf80c2f900cc32
push id106145
push userktomlinson@mozilla.com
push dateThu, 12 Apr 2018 05:09:40 +0000
reviewersbaku
bugs1328964
milestone61.0a1
bug 1328964 terminate worklet thread during xpcom shutdown r=baku The final CC does not happen until after threads are shutdown and so we can't depend on CC to trigger termination. This management of the worklet thread by worklet code is an intermediate situation until worklets run on the threads managed by other objects. MozReview-Commit-ID: 8hWsdRCppC2
dom/worklet/WorkletThread.cpp
dom/worklet/WorkletThread.h
--- a/dom/worklet/WorkletThread.cpp
+++ b/dom/worklet/WorkletThread.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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/. */
 
 #include "WorkletThread.h"
 #include "prthread.h"
+#include "nsContentUtils.h"
 #include "nsCycleCollector.h"
 #include "mozilla/dom/AtomList.h"
 #include "mozilla/EventQueue.h"
 #include "mozilla/ThreadEventQueue.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -290,17 +291,20 @@ private:
 
 WorkletThread::WorkletThread(const WorkletLoadInfo& aWorkletLoadInfo)
   : nsThread(MakeNotNull<ThreadEventQueue<mozilla::EventQueue>*>(
                MakeUnique<mozilla::EventQueue>()),
              nsThread::NOT_MAIN_THREAD, kWorkletStackSize)
   , mWorkletLoadInfo(aWorkletLoadInfo)
   , mCreationTimeStamp(TimeStamp::Now())
   , mJSContext(nullptr)
+  , mIsTerminating(false)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  nsContentUtils::RegisterShutdownObserver(this);
 }
 
 WorkletThread::~WorkletThread()
 {
   // This should be gone during the termination step.
   MOZ_ASSERT(!mJSContext);
 }
 
@@ -394,16 +398,26 @@ WorkletThread::RunEventLoop(JSRuntime* a
   MOZ_ASSERT(mJSContext == nullptr);
 }
 
 void
 WorkletThread::Terminate()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  if (mIsTerminating) {
+    // nsThread::Dispatch() would leak the runnable if the event queue is no
+    // longer accepting runnables.
+    return;
+  }
+
+  mIsTerminating = true;
+
+  nsContentUtils::UnregisterShutdownObserver(this);
+
   RefPtr<TerminateRunnable> runnable = new TerminateRunnable(this);
   DispatchRunnable(runnable.forget());
 }
 
 void
 WorkletThread::TerminateInternal()
 {
   AssertIsOnWorkletThread();
@@ -453,12 +467,23 @@ WorkletThread::Get()
 
   void* cxPrivate = JS_GetContextPrivate(ccjscx->Context());
   MOZ_ASSERT(cxPrivate);
 
   return
     static_cast<WorkletThreadContextPrivate*>(cxPrivate)->GetWorkletThread();
 }
 
-NS_IMPL_ISUPPORTS_INHERITED0(WorkletThread, nsThread)
+// nsIObserver
+NS_IMETHODIMP
+WorkletThread::Observe(nsISupports* aSubject, const char* aTopic,
+                       const char16_t*)
+{
+  MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
+
+  Terminate();
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(WorkletThread, nsThread, nsIObserver)
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/worklet/WorkletThread.h
+++ b/dom/worklet/WorkletThread.h
@@ -15,20 +15,21 @@
 #include "mozilla/TimeStamp.h"
 #include "nsThread.h"
 
 class nsIRunnable;
 
 namespace mozilla {
 namespace dom {
 
-class WorkletThread final : public nsThread
+class WorkletThread final : public nsThread, public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIOBSERVER
 
   static already_AddRefed<WorkletThread>
   Create(const WorkletLoadInfo& aWorkletLoadInfo);
 
   static WorkletThread*
   Get();
 
   static bool
@@ -84,14 +85,16 @@ private:
   DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override;
 
   const WorkletLoadInfo mWorkletLoadInfo;
   TimeStamp mCreationTimeStamp;
 
   // Touched only on the worklet thread. This is a raw pointer because it's set
   // and nullified by RunEventLoop().
   JSContext* mJSContext;
+
+  bool mIsTerminating; // main thread
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_worklet_WorkletThread_h