Bug 1484524: Allow creating a StrongWorkerRef for IPC in the Canceling state r=asuth a=lizzard
authorYaron Tausky <ytausky@mozilla.com>
Mon, 04 Mar 2019 23:05:34 +0000
changeset 516323 f83ccafddfa29dc1b510d0d0d0803aac105b03b3
parent 516322 d5a11a79f7bdfa73a6033e5198ae48c07791a4f7
child 516324 0379572257905b9f89f87c8efdb5e0b96dad6d2c
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth, lizzard
bugs1484524
milestone66.0
Bug 1484524: Allow creating a StrongWorkerRef for IPC in the Canceling state r=asuth a=lizzard By allowing the creation of StrongWorkerRefs in the Canceling state we ensure that IPC will not fail and lead to crashes. Differential Revision: https://phabricator.services.mozilla.com/D21920
dom/workers/WorkerRef.cpp
dom/workers/WorkerRef.h
ipc/glue/IPCStreamSource.cpp
--- a/dom/workers/WorkerRef.cpp
+++ b/dom/workers/WorkerRef.cpp
@@ -138,32 +138,49 @@ WorkerPrivate* WeakWorkerRef::GetPrivate
 WorkerPrivate* WeakWorkerRef::GetUnsafePrivate() const {
   return mWorkerPrivate;
 }
 
 // ----------------------------------------------------------------------------
 // StrongWorkerRef
 
 /* static */ already_AddRefed<StrongWorkerRef> StrongWorkerRef::Create(
-    WorkerPrivate* aWorkerPrivate, const char* aName,
+    WorkerPrivate* const aWorkerPrivate, const char* const aName,
     std::function<void()>&& aCallback) {
+  if (RefPtr<StrongWorkerRef> ref =
+          CreateImpl(aWorkerPrivate, aName, Canceling)) {
+    ref->mCallback = std::move(aCallback);
+    return ref.forget();
+  }
+  return nullptr;
+}
+
+/* static */
+already_AddRefed<StrongWorkerRef> StrongWorkerRef::CreateForcibly(
+    WorkerPrivate* const aWorkerPrivate, const char* const aName) {
+  return CreateImpl(aWorkerPrivate, aName, Killing);
+}
+
+/* static */
+already_AddRefed<StrongWorkerRef> StrongWorkerRef::CreateImpl(
+    WorkerPrivate* const aWorkerPrivate, const char* const aName,
+    WorkerStatus const aFailStatus) {
   MOZ_ASSERT(aWorkerPrivate);
   MOZ_ASSERT(aName);
 
   RefPtr<StrongWorkerRef> ref = new StrongWorkerRef(aWorkerPrivate);
 
   // The worker is kept alive by this holder.
   UniquePtr<Holder> holder(
       new Holder(aName, ref, WorkerHolder::PreventIdleShutdownStart));
-  if (NS_WARN_IF(!holder->HoldWorker(aWorkerPrivate, Canceling))) {
+  if (NS_WARN_IF(!holder->HoldWorker(aWorkerPrivate, aFailStatus))) {
     return nullptr;
   }
 
   ref->mHolder = std::move(holder);
-  ref->mCallback = std::move(aCallback);
 
   return ref.forget();
 }
 
 StrongWorkerRef::StrongWorkerRef(WorkerPrivate* aWorkerPrivate)
     : WorkerRef(aWorkerPrivate) {}
 
 StrongWorkerRef::~StrongWorkerRef() { NS_ASSERT_OWNINGTHREAD(StrongWorkerRef); }
--- a/dom/workers/WorkerRef.h
+++ b/dom/workers/WorkerRef.h
@@ -124,22 +124,41 @@ class WeakWorkerRef final : public Worke
 };
 
 class StrongWorkerRef final : public WorkerRef {
  public:
   static already_AddRefed<StrongWorkerRef> Create(
       WorkerPrivate* aWorkerPrivate, const char* aName,
       std::function<void()>&& aCallback = nullptr);
 
+  // This function creates a StrongWorkerRef even when in the Canceling state of
+  // the worker's lifecycle. It's intended to be used by system code, e.g. code
+  // that needs to perform IPC.
+  //
+  // This method should only be used in cases where the StrongWorkerRef will be
+  // used for an extremely bounded duration that cannot be impacted by content.
+  // For example, IPCStreams use this type of ref in order to immediately
+  // migrate to an actor on another thread. Whether the IPCStream ever actually
+  // is streamed does not matter; the ref will be dropped once the new actor is
+  // created. For this reason, this method does not take a callback. It's
+  // expected and required that callers will drop the reference when they are
+  // done.
+  static already_AddRefed<StrongWorkerRef> CreateForcibly(
+      WorkerPrivate* aWorkerPrivate, const char* aName);
+
   WorkerPrivate* Private() const;
 
  private:
   friend class WeakWorkerRef;
   friend class ThreadSafeWorkerRef;
 
+  static already_AddRefed<StrongWorkerRef> CreateImpl(
+      WorkerPrivate* aWorkerPrivate, const char* aName,
+      WorkerStatus aFailStatus);
+
   explicit StrongWorkerRef(WorkerPrivate* aWorkerPrivate);
   ~StrongWorkerRef();
 };
 
 class ThreadSafeWorkerRef final {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSafeWorkerRef)
 
--- a/ipc/glue/IPCStreamSource.cpp
+++ b/ipc/glue/IPCStreamSource.cpp
@@ -114,22 +114,20 @@ bool IPCStreamSource::Initialize() {
 
   // A source can be used on any thread, but we only support IPCStream on
   // main thread, Workers and PBackground thread right now.  This is due
   // to the requirement  that the thread be guaranteed to live long enough to
   // receive messages. We can enforce this guarantee with a StrongWorkerRef on
   // worker threads, but not other threads. Main-thread and PBackground thread
   // do not need anything special in order to be kept alive.
   if (!NS_IsMainThread()) {
-    mozilla::dom::WorkerPrivate* workerPrivate =
-        mozilla::dom::GetCurrentThreadWorkerPrivate();
-    if (workerPrivate) {
-      RefPtr<mozilla::dom::StrongWorkerRef> workerRef =
-          mozilla::dom::StrongWorkerRef::Create(workerPrivate,
-                                                "IPCStreamSource");
+    if (const auto workerPrivate = dom::GetCurrentThreadWorkerPrivate()) {
+      RefPtr<dom::StrongWorkerRef> workerRef =
+          dom::StrongWorkerRef::CreateForcibly(workerPrivate,
+                                               "IPCStreamSource");
       if (NS_WARN_IF(!workerRef)) {
         return false;
       }
 
       mWorkerRef = std::move(workerRef);
     } else {
       AssertIsOnBackgroundThread();
     }