gecko-dev/ipc/ipdl/test/cxx/TestEndpointBridgeMain.cpp
Jed Davis bcff2dd108 Bug 1487287 - Synchronize GeckoChildProcessHost destruction with launching. r=mccr8
In order to enable asynchronous launch, destruction of
GeckoChildProcessHost (and its subclasses) has to be delayed until after
launching (or anything else that might be made asynchronous in the
future) has completed, to prevent use-after-free.  However, there are
other dependencies on process hosts always being destroyed on the I/O
thread, so refcounting would be difficult to use.

Instead, GeckoChildProcessHost now may not be destroyed directly, but
must go through a method that handles the scheduling.

There are also some minor cleanups to the affected headers (removed
duplicate access modifiers, and made PluginProcessParent final).

Depends on D18010

Differential Revision: https://phabricator.services.mozilla.com/D18011

--HG--
extra : moz-landing-system : lando
2019-02-05 00:15:20 +00:00

227 lines
6.1 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
#include "TestEndpointBridgeMain.h"
#include "base/task.h"
#include "IPDLUnitTests.h" // fail etc.
#include "IPDLUnitTestSubprocess.h"
using namespace std;
namespace mozilla {
namespace _ipdltest {
//-----------------------------------------------------------------------------
// main process
void TestEndpointBridgeMainParent::Main() {
if (!SendStart()) {
fail("sending Start");
}
}
mozilla::ipc::IPCResult TestEndpointBridgeMainParent::RecvBridged(
Endpoint<PTestEndpointBridgeMainSubParent>&& endpoint) {
TestEndpointBridgeMainSubParent* a = new TestEndpointBridgeMainSubParent();
if (!endpoint.Bind(a)) {
fail("Bind failed");
}
return IPC_OK();
}
void TestEndpointBridgeMainParent::ActorDestroy(ActorDestroyReason why) {
if (NormalShutdown != why) {
fail("unexpected destruction!");
}
passed("ok");
QuitParent();
}
mozilla::ipc::IPCResult TestEndpointBridgeMainSubParent::RecvHello() {
if (!SendHi()) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
mozilla::ipc::IPCResult TestEndpointBridgeMainSubParent::RecvHelloSync() {
return IPC_OK();
}
mozilla::ipc::IPCResult TestEndpointBridgeMainSubParent::AnswerHelloRpc() {
if (!CallHiRpc()) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
void TestEndpointBridgeMainSubParent::ActorDestroy(ActorDestroyReason why) {
if (NormalShutdown != why) {
fail("unexpected destruction!");
}
// ActorDestroy() is just a callback from IPDL-generated code,
// which needs the top-level actor (this) to stay alive a little
// longer so other things can be cleaned up.
MessageLoop::current()->PostTask(
do_AddRef(new DeleteTask<TestEndpointBridgeMainSubParent>(this)));
}
//-----------------------------------------------------------------------------
// sub process --- child of main
TestEndpointBridgeMainChild* gEndpointBridgeMainChild;
TestEndpointBridgeMainChild::TestEndpointBridgeMainChild()
: mSubprocess(nullptr) {
gEndpointBridgeMainChild = this;
}
mozilla::ipc::IPCResult TestEndpointBridgeMainChild::RecvStart() {
vector<string> subsubArgs;
subsubArgs.push_back("TestEndpointBridgeSub");
mSubprocess = new IPDLUnitTestSubprocess();
if (!mSubprocess->SyncLaunch(subsubArgs)) {
fail("problem launching subprocess");
}
IPC::Channel* transport = mSubprocess->GetChannel();
if (!transport) {
fail("no transport");
}
TestEndpointBridgeSubParent* bsp = new TestEndpointBridgeSubParent();
bsp->Open(transport, base::GetProcId(mSubprocess->GetChildProcessHandle()));
bsp->Main();
return IPC_OK();
}
void TestEndpointBridgeMainChild::ActorDestroy(ActorDestroyReason why) {
if (NormalShutdown != why) {
fail("unexpected destruction!");
}
// NB: this is kosher because QuitChild() joins with the IO thread
mSubprocess->Destroy();
mSubprocess = nullptr;
QuitChild();
}
void TestEndpointBridgeSubParent::Main() {
if (!SendPing()) {
fail("sending Ping");
}
}
mozilla::ipc::IPCResult TestEndpointBridgeSubParent::RecvBridgeEm() {
Endpoint<PTestEndpointBridgeMainSubParent> parent;
Endpoint<PTestEndpointBridgeMainSubChild> child;
nsresult rv;
rv = PTestEndpointBridgeMainSub::CreateEndpoints(
gEndpointBridgeMainChild->OtherPid(), OtherPid(), &parent, &child);
if (NS_FAILED(rv)) {
fail("opening PTestEndpointOpensOpened");
}
if (!gEndpointBridgeMainChild->SendBridged(std::move(parent))) {
fail("SendBridge failed for parent");
}
if (!SendBridged(std::move(child))) {
fail("SendBridge failed for child");
}
return IPC_OK();
}
void TestEndpointBridgeSubParent::ActorDestroy(ActorDestroyReason why) {
if (NormalShutdown != why) {
fail("unexpected destruction!");
}
gEndpointBridgeMainChild->Close();
// ActorDestroy() is just a callback from IPDL-generated code,
// which needs the top-level actor (this) to stay alive a little
// longer so other things can be cleaned up.
MessageLoop::current()->PostTask(
do_AddRef(new DeleteTask<TestEndpointBridgeSubParent>(this)));
}
//-----------------------------------------------------------------------------
// subsub process --- child of sub
static TestEndpointBridgeSubChild* gBridgeSubChild;
TestEndpointBridgeSubChild::TestEndpointBridgeSubChild() {
gBridgeSubChild = this;
}
mozilla::ipc::IPCResult TestEndpointBridgeSubChild::RecvPing() {
if (!SendBridgeEm()) {
fail("sending BridgeEm");
}
return IPC_OK();
}
mozilla::ipc::IPCResult TestEndpointBridgeSubChild::RecvBridged(
Endpoint<PTestEndpointBridgeMainSubChild>&& endpoint) {
TestEndpointBridgeMainSubChild* a = new TestEndpointBridgeMainSubChild();
if (!endpoint.Bind(a)) {
fail("failed to Bind");
}
if (!a->SendHello()) {
fail("sending Hello");
}
return IPC_OK();
}
void TestEndpointBridgeSubChild::ActorDestroy(ActorDestroyReason why) {
if (NormalShutdown != why) {
fail("unexpected destruction!");
}
QuitChild();
}
mozilla::ipc::IPCResult TestEndpointBridgeMainSubChild::RecvHi() {
if (!SendHelloSync()) {
fail("sending HelloSync");
}
if (!CallHelloRpc()) {
fail("calling HelloRpc");
}
if (!mGotHi) {
fail("didn't answer HiRpc");
}
// Need to close the channel without message-processing frames on
// the C++ stack
MessageLoop::current()->PostTask(
NewNonOwningRunnableMethod("ipc::IToplevelProtocol::Close", this,
&TestEndpointBridgeMainSubChild::Close));
return IPC_OK();
}
mozilla::ipc::IPCResult TestEndpointBridgeMainSubChild::AnswerHiRpc() {
mGotHi = true; // d00d
return IPC_OK();
}
void TestEndpointBridgeMainSubChild::ActorDestroy(ActorDestroyReason why) {
if (NormalShutdown != why) {
fail("unexpected destruction!");
}
gBridgeSubChild->Close();
// ActorDestroy() is just a callback from IPDL-generated code,
// which needs the top-level actor (this) to stay alive a little
// longer so other things can be cleaned up.
MessageLoop::current()->PostTask(
do_AddRef(new DeleteTask<TestEndpointBridgeMainSubChild>(this)));
}
} // namespace _ipdltest
} // namespace mozilla