Bug 1487249 - Part 2: Add a new PInProcess actor to manage intra-thread actors, r=mccr8
authorNika Layzell <nika@thelayzells.com>
Wed, 29 Aug 2018 18:18:04 -0400
changeset 508675 222e2bcb25df56bddcf3b1448f4176b893827caa
parent 508674 8976adedb5cb800a1cd7fd840555a605c28cf0f7
child 508676 20554da7b303f2f6442ecacfb846a5ad663c8fee
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmccr8
bugs1487249
milestone65.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 1487249 - Part 2: Add a new PInProcess actor to manage intra-thread actors, r=mccr8 This will be useful as a basis for asynchronous actors which would like to exist both when crossing the process boundary (managed by PContent), and when displaying an in-process window. Differential Revision: https://phabricator.services.mozilla.com/D4622
ipc/glue/InProcessChild.cpp
ipc/glue/InProcessChild.h
ipc/glue/InProcessImpl.cpp
ipc/glue/InProcessParent.cpp
ipc/glue/InProcessParent.h
ipc/glue/PInProcess.ipdl
ipc/glue/moz.build
new file mode 100644
--- /dev/null
+++ b/ipc/glue/InProcessChild.cpp
@@ -0,0 +1,13 @@
+/* -*- 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 "mozilla/ipc/InProcessChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/glue/InProcessChild.h
@@ -0,0 +1,54 @@
+/* -*- 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/. */
+
+#ifndef mozilla_ipc_InProcessChild_h
+#define mozilla_ipc_InProcessChild_h
+
+#include "mozilla/ipc/PInProcessChild.h"
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla {
+namespace ipc {
+
+class InProcessParent;
+
+/**
+ * The `InProcessChild` class represents the child half of a main-thread to
+ * main-thread actor.
+ *
+ * The `PInProcess` actor should be used as an alternate manager to `PContent`
+ * for async actors which want to communicate uniformly between Content->Chrome
+ * and Chrome->Chrome situations.
+ */
+class InProcessChild : public PInProcessChild
+{
+public:
+  friend class InProcessParent;
+
+  NS_INLINE_DECL_REFCOUNTING(InProcessChild)
+
+  // Get the singleton instance of this actor.
+  static InProcessChild* Singleton();
+
+  // Get the parent side of the in-process child actor |aActor|. If |aActor| is
+  // not an in-process actor, or is not connected, this method will return
+  // |nullptr|.
+  static IProtocol* ParentActorFor(IProtocol* aActor);
+
+private:
+  // NOTE: PInProcess lifecycle management is declared as staic methods and
+  // state on InProcessParent, and implemented in InProcessImpl.cpp.
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+  virtual void DeallocPInProcessChild() override;
+  ~InProcessChild() = default;
+
+  static StaticRefPtr<InProcessChild> sSingleton;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // defined(mozilla_ipc_InProcessChild_h)
new file mode 100644
--- /dev/null
+++ b/ipc/glue/InProcessImpl.cpp
@@ -0,0 +1,210 @@
+/* -*- 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 "mozilla/ipc/InProcessParent.h"
+#include "mozilla/ipc/InProcessChild.h"
+
+// This file contains the implementation of core InProcess lifecycle management
+// facilities.
+
+namespace mozilla {
+namespace ipc {
+
+StaticRefPtr<InProcessParent> InProcessParent::sSingleton;
+StaticRefPtr<InProcessChild> InProcessChild::sSingleton;
+bool InProcessParent::sShutdown = false;
+
+
+//////////////////////////////////////////
+// InProcess actor lifecycle management //
+//////////////////////////////////////////
+
+/* static */ InProcessChild*
+InProcessChild::Singleton() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sSingleton) {
+    InProcessParent::Startup();
+  }
+  return sSingleton;
+}
+
+/* static */ InProcessParent*
+InProcessParent::Singleton() {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sSingleton) {
+    InProcessParent::Startup();
+  }
+  return sSingleton;
+}
+
+/* static */ void
+InProcessParent::Startup()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (sShutdown) {
+    NS_WARNING("Could not get in-process actor while shutting down!");
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (!obs) {
+    sShutdown = true;
+    NS_WARNING("Failed to get nsIObserverService for in-process actor");
+    return;
+  }
+
+  RefPtr<InProcessParent> parent = new InProcessParent();
+  RefPtr<InProcessChild> child = new InProcessChild();
+
+  // Observe the shutdown event to close & clean up after ourselves.
+  nsresult rv = obs->AddObserver(parent, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  // Link the two actors
+  if (!child->OpenOnSameThread(parent->GetIPCChannel(), ChildSide)) {
+    MOZ_CRASH("Failed to open InProcessChild!");
+  }
+
+  parent->SetOtherProcessId(base::GetCurrentProcId());
+
+  // Create references held by the IPC layer which will be freed in
+  // DeallocPInProcess{Parent,Child}.
+  parent.get()->AddRef();
+  child.get()->AddRef();
+
+  // Stash global references to fetch the other side of the reference.
+  InProcessParent::sSingleton = parent.forget();
+  InProcessChild::sSingleton = child.forget();
+}
+
+
+/* static */ void
+InProcessParent::Shutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!sSingleton || sShutdown) {
+    return;
+  }
+
+  sShutdown = true;
+
+  RefPtr<InProcessParent> parent = sSingleton;
+  InProcessParent::sSingleton = nullptr;
+  InProcessChild::sSingleton = nullptr;
+
+  // Calling `Close` on the actor will cause the `Dealloc` methods to be called,
+  // freeing the remaining references.
+  parent->Close();
+}
+
+NS_IMETHODIMP
+InProcessParent::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+{
+  MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
+  InProcessParent::Shutdown();
+  return NS_OK;
+}
+
+void
+InProcessParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  InProcessParent::Shutdown();
+}
+
+void
+InProcessChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  InProcessParent::Shutdown();
+}
+
+void
+InProcessParent::DeallocPInProcessParent()
+{
+  MOZ_ASSERT(!InProcessParent::sSingleton);
+  Release(); // Release the reference taken in InProcessParent::Startup.
+}
+
+void
+InProcessChild::DeallocPInProcessChild()
+{
+  MOZ_ASSERT(!InProcessChild::sSingleton);
+  Release(); // Release the reference taken in InProcessParent::Startup.
+}
+
+////////////////////////////////
+// In-Process Actor Utilities //
+////////////////////////////////
+
+// Helper method for implementing ParentActorFor and ChildActorFor.
+static IProtocol*
+GetOtherInProcessActor(IProtocol* aActor)
+{
+  MOZ_ASSERT(aActor->GetSide() != UnknownSide, "bad unknown side");
+
+  // Discover the manager of aActor which is PInProcess.
+  IProtocol* current = aActor;
+  while (current) {
+    if (current->GetProtocolTypeId() == PInProcessMsgStart) {
+      break; // Found the correct actor.
+    }
+    current = current->Manager();
+  }
+  if (!current) {
+    return nullptr; // Not a PInProcess actor, return |nullptr|
+  }
+
+  MOZ_ASSERT(current->GetSide() == aActor->GetSide(), "side changed?");
+  MOZ_ASSERT_IF(aActor->GetSide() == ParentSide,
+                current == InProcessParent::Singleton());
+  MOZ_ASSERT_IF(aActor->GetSide() == ChildSide,
+                current == InProcessChild::Singleton());
+
+  // Check whether this is InProcessParent or InProcessChild, and get the other
+  // side's toplevel actor.
+  IProtocol* otherRoot = nullptr;
+  if (aActor->GetSide() == ParentSide) {
+    otherRoot = InProcessChild::Singleton();
+  } else {
+    otherRoot = InProcessParent::Singleton();
+  }
+  if (NS_WARN_IF(!otherRoot)) {
+    return nullptr;
+  }
+
+  // Look up the actor on the other side, and return it.
+  IProtocol* otherActor = otherRoot->Lookup(aActor->Id());
+  if (otherActor) {
+    MOZ_ASSERT(otherActor->GetSide() != UnknownSide, "bad unknown side");
+    MOZ_ASSERT(otherActor->GetSide() != aActor->GetSide(), "Wrong side!");
+    MOZ_ASSERT(otherActor->GetProtocolTypeId() == aActor->GetProtocolTypeId(),
+               "Wrong type of protocol!");
+  }
+
+  return otherActor;
+}
+
+/* static */ IProtocol*
+InProcessParent::ChildActorFor(IProtocol* aActor)
+{
+  MOZ_ASSERT(aActor && aActor->GetSide() == ParentSide);
+  return GetOtherInProcessActor(aActor);
+}
+
+/* static */ IProtocol*
+InProcessChild::ParentActorFor(IProtocol* aActor)
+{
+  MOZ_ASSERT(aActor && aActor->GetSide() == ChildSide);
+  return GetOtherInProcessActor(aActor);
+}
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/glue/InProcessParent.cpp
@@ -0,0 +1,15 @@
+/* -*- 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 "mozilla/ipc/InProcessParent.h"
+
+namespace mozilla {
+namespace ipc {
+
+NS_IMPL_ISUPPORTS(InProcessParent, nsIObserver)
+
+} // namespace ipc
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/ipc/glue/InProcessParent.h
@@ -0,0 +1,60 @@
+/* -*- 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/. */
+
+#ifndef mozilla_ipc_InProcessParent_h
+#define mozilla_ipc_InProcessParent_h
+
+#include "mozilla/ipc/PInProcessParent.h"
+#include "mozilla/StaticPtr.h"
+
+namespace mozilla {
+namespace ipc {
+
+class InProcessChild;
+
+/**
+ * The `InProcessParent` class represents the parent half of a main-thread to
+ * main-thread actor.
+ *
+ * The `PInProcess` actor should be used as an alternate manager to `PContent`
+ * for async actors which want to communicate uniformly between Content->Chrome
+ * and Chrome->Chrome situations.
+ */
+class InProcessParent : public nsIObserver
+                      , public PInProcessParent
+{
+public:
+  friend class InProcessChild;
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  // Get the singleton instance of this actor.
+  static InProcessParent* Singleton();
+
+  // Get the child side of the in-process child actor |aActor|. If |aActor| is
+  // not an in-process actor, or is not connected, this method will return
+  // |nullptr|.
+  static IProtocol* ChildActorFor(IProtocol* aActor);
+
+private:
+  // Lifecycle management is implemented in InProcessImpl.cpp
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+  virtual void DeallocPInProcessParent() override;
+  ~InProcessParent() = default;
+
+  static void Startup();
+  static void Shutdown();
+
+  static StaticRefPtr<InProcessParent> sSingleton;
+  static bool sShutdown;
+};
+
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // defined(mozilla_ipc_InProcessParent_h)
new file mode 100644
--- /dev/null
+++ b/ipc/glue/PInProcess.ipdl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+namespace mozilla {
+namespace ipc {
+
+/**
+ * PInProcess is intended for use as an alternative actor manager to PContent
+ * for async actors which want to be used uniformly in both Content->Chrome and
+ * Chrome->Chrome circumstances.
+ *
+ * `mozilla::ipc::InProcess{Parent, Child}::Singleton()` should be used to get
+ * an instance of this actor.
+ */
+async protocol PInProcess
+{
+};
+
+} // namespace ipc
+} // namespace mozilla
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -22,16 +22,18 @@ EXPORTS.mozilla.ipc += [
     'CrossProcessMutex.h',
     'CrossProcessSemaphore.h',
     'EnvironmentMap.h',
     'FileDescriptor.h',
     'FileDescriptorSetChild.h',
     'FileDescriptorSetParent.h',
     'FileDescriptorUtils.h',
     'GeckoChildProcessHost.h',
+    'InProcessChild.h',
+    'InProcessParent.h',
     'InputStreamUtils.h',
     'IOThreadChild.h',
     'IPCStreamAlloc.h',
     'IPCStreamDestination.h',
     'IPCStreamSource.h',
     'IPCStreamUtils.h',
     'IPDLParamTraits.h',
     'MessageChannel.h',
@@ -142,16 +144,19 @@ UNIFIED_SOURCES += [
     'BackgroundImpl.cpp',
     'BackgroundUtils.cpp',
     'BrowserProcessSubThread.cpp',
     'CrashReporterClient.cpp',
     'CrashReporterHost.cpp',
     'CrashReporterMetadataShmem.cpp',
     'FileDescriptor.cpp',
     'FileDescriptorUtils.cpp',
+    'InProcessChild.cpp',
+    'InProcessImpl.cpp',
+    'InProcessParent.cpp',
     'InputStreamUtils.cpp',
     'IPCMessageUtils.cpp',
     'IPCStreamChild.cpp',
     'IPCStreamDestination.cpp',
     'IPCStreamParent.cpp',
     'IPCStreamSource.cpp',
     'IPCStreamUtils.cpp',
     'MessageChannel.cpp',
@@ -203,16 +208,17 @@ LOCAL_INCLUDES += [
 IPDL_SOURCES = [
     'InputStreamParams.ipdlh',
     'IPCStream.ipdlh',
     'PBackground.ipdl',
     'PBackgroundSharedTypes.ipdlh',
     'PBackgroundTest.ipdl',
     'PChildToParentStream.ipdl',
     'PFileDescriptorSet.ipdl',
+    'PInProcess.ipdl',
     'PParentToChildStream.ipdl',
     'ProtocolTypes.ipdlh',
     'URIParams.ipdlh',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/ipc',
     '/toolkit/crashreporter',