gecko-dev/netwerk/ipc/SocketProcessBridgeChild.cpp
Kershaw Chang 1c6a55798d Bug 1242221 - Close IPC channel before MessageLoop gets destroyed r=mayhemer
From the log, I found that the intermittent crash happened because SocketProcessBridgeParent's message loop is destroyed before the IPC channel is closed. Before this patch, the only way to close the IPC channel between SocketProcessBridgeParent and SocketProcessBridgeChild is relying on "content-child-shutdown" notification in child process. If socket process gets terminated before "content-child-shutdown", the IPC channel is never closed.
So, this patch tries to close the IPC channel at SocketProcessChild::CleanUp if the channel is not closed.

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

--HG--
extra : moz-landing-system : lando
2019-07-25 15:24:02 +00:00

181 lines
5.5 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "SocketProcessBridgeChild.h"
#include "SocketProcessLogging.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/net/NeckoChild.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h"
#include "mozilla/Preferences.h"
namespace mozilla {
using dom::ContentChild;
namespace net {
StaticRefPtr<SocketProcessBridgeChild>
SocketProcessBridgeChild::sSocketProcessBridgeChild;
NS_IMPL_ISUPPORTS(SocketProcessBridgeChild, nsIObserver)
// static
bool SocketProcessBridgeChild::Create(
Endpoint<PSocketProcessBridgeChild>&& aEndpoint) {
MOZ_ASSERT(NS_IsMainThread());
sSocketProcessBridgeChild =
new SocketProcessBridgeChild(std::move(aEndpoint));
if (sSocketProcessBridgeChild->Inited()) {
return true;
}
sSocketProcessBridgeChild = nullptr;
return false;
}
// static
already_AddRefed<SocketProcessBridgeChild>
SocketProcessBridgeChild::GetSingleton() {
RefPtr<SocketProcessBridgeChild> child = sSocketProcessBridgeChild;
return child.forget();
}
static bool SocketProcessEnabled() {
static bool sInited = false;
static bool sSocketProcessEnabled = false;
if (!sInited) {
sSocketProcessEnabled = Preferences::GetBool("network.process.enabled") &&
XRE_IsContentProcess();
sInited = true;
}
return sSocketProcessEnabled;
}
// static
RefPtr<SocketProcessBridgeChild::GetPromise>
SocketProcessBridgeChild::GetSocketProcessBridge() {
MOZ_ASSERT(NS_IsMainThread());
if (!SocketProcessEnabled()) {
return GetPromise::CreateAndReject(nsCString("Socket process disabled!"),
__func__);
}
if (!gNeckoChild) {
return GetPromise::CreateAndReject(nsCString("No NeckoChild!"), __func__);
}
// ContentChild is shutting down, we should not try to create
// SocketProcessBridgeChild.
ContentChild* content = ContentChild::GetSingleton();
if (!content || content->IsShuttingDown()) {
return GetPromise::CreateAndReject(
nsCString("ContentChild is shutting down."), __func__);
}
if (sSocketProcessBridgeChild) {
return GetPromise::CreateAndResolve(sSocketProcessBridgeChild, __func__);
}
return gNeckoChild->SendInitSocketProcessBridge()->Then(
GetMainThreadSerialEventTarget(), __func__,
[](NeckoChild::InitSocketProcessBridgePromise::ResolveOrRejectValue&&
aResult) {
ContentChild* content = ContentChild::GetSingleton();
if (!content || content->IsShuttingDown()) {
return GetPromise::CreateAndReject(
nsCString("ContentChild is shutting down."), __func__);
}
if (!sSocketProcessBridgeChild) {
if (aResult.IsReject()) {
return GetPromise::CreateAndReject(
nsCString("SendInitSocketProcessBridge failed"), __func__);
}
if (!aResult.ResolveValue().IsValid()) {
return GetPromise::CreateAndReject(
nsCString(
"SendInitSocketProcessBridge resolved with an invalid "
"endpoint!"),
__func__);
}
if (!SocketProcessBridgeChild::Create(
std::move(aResult.ResolveValue()))) {
return GetPromise::CreateAndReject(
nsCString("SendInitSocketProcessBridge resolved with a valid "
"endpoint, "
"but SocketProcessBridgeChild::Create failed!"),
__func__);
}
}
return GetPromise::CreateAndResolve(sSocketProcessBridgeChild,
__func__);
});
}
SocketProcessBridgeChild::SocketProcessBridgeChild(
Endpoint<PSocketProcessBridgeChild>&& aEndpoint)
: mShuttingDown(false) {
LOG(("CONSTRUCT SocketProcessBridgeChild::SocketProcessBridgeChild\n"));
mInited = aEndpoint.Bind(this);
if (!mInited) {
MOZ_ASSERT(false, "Bind failed!");
return;
}
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->AddObserver(this, "content-child-shutdown", false);
}
mSocketProcessPid = aEndpoint.OtherPid();
}
SocketProcessBridgeChild::~SocketProcessBridgeChild() {
LOG(("DESTRUCT SocketProcessBridgeChild::SocketProcessBridgeChild\n"));
}
mozilla::ipc::IPCResult SocketProcessBridgeChild::RecvTest() {
LOG(("SocketProcessBridgeChild::RecvTest\n"));
return IPC_OK();
}
void SocketProcessBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
LOG(("SocketProcessBridgeChild::ActorDestroy\n"));
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
os->RemoveObserver(this, "content-child-shutdown");
}
MessageLoop::current()->PostTask(
NewRunnableMethod("net::SocketProcessBridgeChild::DeferredDestroy", this,
&SocketProcessBridgeChild::DeferredDestroy));
mShuttingDown = true;
}
NS_IMETHODIMP
SocketProcessBridgeChild::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
if (!strcmp(aTopic, "content-child-shutdown")) {
PSocketProcessBridgeChild::Close();
}
return NS_OK;
}
void SocketProcessBridgeChild::DeferredDestroy() {
MOZ_ASSERT(NS_IsMainThread());
sSocketProcessBridgeChild = nullptr;
}
} // namespace net
} // namespace mozilla