Skip to content

Commit

Permalink
Bug 1487249 - Part 2: Add a new PInProcess actor to manage intra-thre…
Browse files Browse the repository at this point in the history
…ad 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
  • Loading branch information
mystor committed Dec 5, 2018
1 parent c4ffb4a commit 5070302
Show file tree
Hide file tree
Showing 7 changed files with 381 additions and 0 deletions.
13 changes: 13 additions & 0 deletions ipc/glue/InProcessChild.cpp
Original file line number Diff line number Diff line change
@@ -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
54 changes: 54 additions & 0 deletions ipc/glue/InProcessChild.h
Original file line number Diff line number Diff line change
@@ -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)
210 changes: 210 additions & 0 deletions ipc/glue/InProcessImpl.cpp
Original file line number Diff line number Diff line change
@@ -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
15 changes: 15 additions & 0 deletions ipc/glue/InProcessParent.cpp
Original file line number Diff line number Diff line change
@@ -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
60 changes: 60 additions & 0 deletions ipc/glue/InProcessParent.h
Original file line number Diff line number Diff line change
@@ -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)
23 changes: 23 additions & 0 deletions ipc/glue/PInProcess.ipdl
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 5070302

Please sign in to comment.