Bug 1155547, Part 2: Create PNuwa protocol (managed by PBackground) for forking content processes. r=khuey

This allows us to send a sync fork request to the Nuwa process when we need one but there is no
spare process available. After an app is launched, the request to fork a spare process is still
handled asynchronously.

--HG--
extra : rebase_source : 9b692a647f4fc861285d95f0372d6a9913eadf64
This commit is contained in:
Cervantes Yu 2015-07-31 15:25:27 +08:00
Родитель bd05affa1b
Коммит aa6f91dd67
18 изменённых файлов: 809 добавлений и 252 удалений

Просмотреть файл

@ -153,9 +153,9 @@
#endif
#ifdef MOZ_NUWA_PROCESS
#include <setjmp.h>
#include "ipc/Nuwa.h"
#endif
#include "NuwaChild.h"
#ifdef MOZ_GAMEPAD
#include "mozilla/dom/GamepadService.h"
@ -220,19 +220,6 @@ using namespace mozilla::system;
#endif
using namespace mozilla::widget;
#ifdef MOZ_NUWA_PROCESS
static bool sNuwaForking = false;
// The size of the reserved stack (in unsigned ints). It's used to reserve space
// to push sigsetjmp() in NuwaCheckpointCurrentThread() to higher in the stack
// so that after it returns and do other work we don't garble the stack we want
// to preserve in NuwaCheckpointCurrentThread().
#define RESERVED_INT_STACK 128
// A sentinel value for checking whether RESERVED_INT_STACK is large enough.
#define STACK_SENTINEL_VALUE 0xdeadbeef
#endif
namespace mozilla {
namespace dom {
@ -539,7 +526,7 @@ ContentChild* ContentChild::sSingleton;
// Performs initialization that is not fork-safe, i.e. that must be done after
// forking from the Nuwa process.
static void
void
InitOnContentProcessCreated()
{
#ifdef MOZ_NUWA_PROCESS
@ -2206,8 +2193,25 @@ ContentChild::RecvCycleCollect()
#ifdef MOZ_NUWA_PROCESS
static void
OnFinishNuwaPreparation ()
OnFinishNuwaPreparation()
{
// We want to ensure that the PBackground actor gets cloned in the Nuwa
// process before we freeze. Also, we have to do this to avoid deadlock.
// Protocols that are "opened" (e.g. PBackground, PCompositor) block the
// main thread to wait for the IPC thread during the open operation.
// NuwaSpawnWait() blocks the IPC thread to wait for the main thread when
// the Nuwa process is forked. Unless we ensure that the two cannot happen
// at the same time then we risk deadlock. Spinning the event loop here
// guarantees the ordering is safe for PBackground.
while (!BackgroundChild::GetForCurrentThread()) {
if (NS_WARN_IF(!NS_ProcessNextEvent())) {
return;
}
}
// This will create the actor.
unused << mozilla::dom::NuwaChild::GetSingleton();
MakeNuwaProcess();
}
#endif
@ -2493,87 +2497,6 @@ ContentChild::DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* actor)
return true;
}
#ifdef MOZ_NUWA_PROCESS
class CallNuwaSpawn : public nsRunnable
{
public:
NS_IMETHOD Run()
{
NuwaSpawn();
if (IsNuwaProcess()) {
return NS_OK;
}
// In the new process.
ContentChild* child = ContentChild::GetSingleton();
child->SetProcessName(NS_LITERAL_STRING("(Preallocated app)"), false);
// Perform other after-fork initializations.
InitOnContentProcessCreated();
return NS_OK;
}
};
static void
DoNuwaFork()
{
NuwaSpawnPrepare(); // NuwaSpawn will be blocked.
{
nsCOMPtr<nsIRunnable> callSpawn(new CallNuwaSpawn());
NS_DispatchToMainThread(callSpawn);
}
// IOThread should be blocked here for waiting NuwaSpawn().
NuwaSpawnWait(); // Now! NuwaSpawn can go.
// Here, we can make sure the spawning was finished.
}
/**
* This function should keep IO thread in a stable state and freeze it
* until the spawning is finished.
*/
static void
RunNuwaFork()
{
if (NuwaCheckpointCurrentThread()) {
DoNuwaFork();
}
}
#endif
bool
ContentChild::RecvNuwaFork()
{
#ifdef MOZ_NUWA_PROCESS
if (sNuwaForking) { // No reentry.
return true;
}
sNuwaForking = true;
// We want to ensure that the PBackground actor gets cloned in the Nuwa
// process before we freeze. Also, we have to do this to avoid deadlock.
// Protocols that are "opened" (e.g. PBackground, PCompositor) block the
// main thread to wait for the IPC thread during the open operation.
// NuwaSpawnWait() blocks the IPC thread to wait for the main thread when
// the Nuwa process is forked. Unless we ensure that the two cannot happen
// at the same time then we risk deadlock. Spinning the event loop here
// guarantees the ordering is safe for PBackground.
while (!BackgroundChild::GetForCurrentThread()) {
if (NS_WARN_IF(!NS_ProcessNextEvent())) {
return false;
}
}
MessageLoop* ioloop = XRE_GetIOMessageLoop();
ioloop->PostTask(FROM_HERE, NewRunnableFunction(RunNuwaFork));
return true;
#else
return false; // Makes the underlying IPC channel abort.
#endif
}
bool
ContentChild::RecvOnAppThemeChanged()
{
@ -2918,109 +2841,3 @@ ContentChild::RecvTestGraphicsDeviceReset(const uint32_t& aResetReason)
} // namespace dom
} // namespace mozilla
extern "C" {
#if defined(MOZ_NUWA_PROCESS)
NS_EXPORT void
GetProtoFdInfos(NuwaProtoFdInfo* aInfoList,
size_t aInfoListSize,
size_t* aInfoSize)
{
size_t i = 0;
mozilla::dom::ContentChild* content =
mozilla::dom::ContentChild::GetSingleton();
aInfoList[i].protoId = content->GetProtocolId();
aInfoList[i].originFd =
content->GetTransport()->GetFileDescriptor();
i++;
IToplevelProtocol* actors[NUWA_TOPLEVEL_MAX];
size_t count = content->GetOpenedActorsUnsafe(actors, ArrayLength(actors));
for (size_t j = 0; j < count; j++) {
IToplevelProtocol* actor = actors[j];
if (i >= aInfoListSize) {
NS_RUNTIMEABORT("Too many top level protocols!");
}
aInfoList[i].protoId = actor->GetProtocolId();
aInfoList[i].originFd =
actor->GetTransport()->GetFileDescriptor();
i++;
}
if (i > NUWA_TOPLEVEL_MAX) {
NS_RUNTIMEABORT("Too many top level protocols!");
}
*aInfoSize = i;
}
class RunAddNewIPCProcess : public nsRunnable
{
public:
RunAddNewIPCProcess(pid_t aPid,
nsTArray<mozilla::ipc::ProtocolFdMapping>& aMaps)
: mPid(aPid)
{
mMaps.SwapElements(aMaps);
}
NS_IMETHOD Run()
{
mozilla::dom::ContentChild::GetSingleton()->
SendAddNewProcess(mPid, mMaps);
MOZ_ASSERT(sNuwaForking);
sNuwaForking = false;
return NS_OK;
}
private:
pid_t mPid;
nsTArray<mozilla::ipc::ProtocolFdMapping> mMaps;
};
/**
* AddNewIPCProcess() is called by Nuwa process to tell the parent
* process that a new process is created.
*
* In the newly created process, ResetContentChildTransport() is called to
* reset fd for the IPC Channel and the session.
*/
NS_EXPORT void
AddNewIPCProcess(pid_t aPid, NuwaProtoFdInfo* aInfoList, size_t aInfoListSize)
{
nsTArray<mozilla::ipc::ProtocolFdMapping> maps;
for (size_t i = 0; i < aInfoListSize; i++) {
int _fd = aInfoList[i].newFds[NUWA_NEWFD_PARENT];
mozilla::ipc::FileDescriptor fd(_fd);
mozilla::ipc::ProtocolFdMapping map(aInfoList[i].protoId, fd);
maps.AppendElement(map);
}
nsRefPtr<RunAddNewIPCProcess> runner = new RunAddNewIPCProcess(aPid, maps);
NS_DispatchToMainThread(runner);
}
NS_EXPORT void
OnNuwaProcessReady()
{
mozilla::dom::ContentChild* content =
mozilla::dom::ContentChild::GetSingleton();
content->SendNuwaReady();
}
NS_EXPORT void
AfterNuwaFork()
{
SetCurrentProcessPrivileges(base::PRIVILEGES_DEFAULT);
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
mozilla::SandboxEarlyInit(XRE_GetProcessType(), /* isNuwa: */ false);
#endif
}
#endif // MOZ_NUWA_PROCESS
}

Просмотреть файл

@ -363,8 +363,6 @@ public:
const bool& aIsHotSwappable) override;
virtual bool RecvVolumeRemoved(const nsString& aFsName) override;
virtual bool RecvNuwaFork() override;
virtual bool
RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority) override;
virtual bool RecvMinimizeMemoryUsage() override;
@ -517,6 +515,9 @@ private:
DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
};
void
InitOnContentProcessCreated();
uint64_t
NextWindowID();

Просмотреть файл

@ -42,6 +42,7 @@
#include "mozilla/dom/ExternalHelperAppParent.h"
#include "mozilla/dom/FileSystemRequestParent.h"
#include "mozilla/dom/GeolocationBinding.h"
#include "mozilla/dom/NuwaParent.h"
#include "mozilla/dom/PContentBridgeParent.h"
#include "mozilla/dom/PContentPermissionRequestParent.h"
#include "mozilla/dom/PCycleCollectWithLogsParent.h"
@ -75,6 +76,7 @@
#include "mozilla/layers/SharedBufferManagerParent.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/media/MediaParent.h"
#include "mozilla/Move.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/plugins/PluginBridge.h"
#include "mozilla/Preferences.h"
@ -2841,47 +2843,54 @@ ContentParent::RecvDataStoreGetStores(
return true;
}
bool
ContentParent::RecvNuwaReady()
void
ContentParent::ForkNewProcess(bool aBlocking)
{
#ifdef MOZ_NUWA_PROCESS
if (!IsNuwaProcess()) {
NS_ERROR(
nsPrintfCString(
"Terminating child process %d for unauthorized IPC message: NuwaReady",
Pid()).get());
uint32_t pid;
auto fds = MakeUnique<nsTArray<ProtocolFdMapping>>();
KillHard("NuwaReady");
return false;
}
sNuwaReady = true;
PreallocatedProcessManager::OnNuwaReady();
return true;
MOZ_ASSERT(IsNuwaProcess() && mNuwaParent);
if (mNuwaParent->ForkNewProcess(pid, mozilla::Move(fds), aBlocking)) {
OnNewProcessCreated(pid, mozilla::Move(fds));
}
#else
NS_ERROR("ContentParent::RecvNuwaReady() not implemented!");
return false;
NS_ERROR("ContentParent::ForkNewProcess() not implemented!");
#endif
}
bool
ContentParent::RecvAddNewProcess(const uint32_t& aPid,
InfallibleTArray<ProtocolFdMapping>&& aFds)
void
ContentParent::OnNuwaReady()
{
#ifdef MOZ_NUWA_PROCESS
if (!IsNuwaProcess()) {
NS_ERROR(
nsPrintfCString(
"Terminating child process %d for unauthorized IPC message: "
"AddNewProcess(%d)", Pid(), aPid).get());
// Protection from unauthorized IPC message is done in PNuwa protocol.
// Just assert that this actor is really for the Nuwa process.
MOZ_ASSERT(IsNuwaProcess());
sNuwaReady = true;
PreallocatedProcessManager::OnNuwaReady();
return;
#else
NS_ERROR("ContentParent::OnNuwaReady() not implemented!");
return;
#endif
}
void
ContentParent::OnNewProcessCreated(uint32_t aPid,
UniquePtr<nsTArray<ProtocolFdMapping>>&& aFds)
{
#ifdef MOZ_NUWA_PROCESS
// Protection from unauthorized IPC message is done in PNuwa protocol.
// Just assert that this actor is really for the Nuwa process.
MOZ_ASSERT(IsNuwaProcess());
KillHard("AddNewProcess");
return false;
}
nsRefPtr<ContentParent> content;
content = new ContentParent(this,
MAGIC_PREALLOCATED_APP_MANIFEST_URL,
aPid,
Move(aFds));
Move(*aFds.get()));
content->Init();
size_t numNuwaPrefUpdates = sNuwaPrefUpdates ?
@ -2909,10 +2918,10 @@ ContentParent::RecvAddNewProcess(const uint32_t& aPid,
"Unexpected values");
PreallocatedProcessManager::PublishSpareProcess(content);
return true;
return;
#else
NS_ERROR("ContentParent::RecvAddNewProcess() not implemented!");
return false;
NS_ERROR("ContentParent::OnNewProcessCreated() not implemented!");
return;
#endif
}

Просмотреть файл

@ -7,6 +7,7 @@
#ifndef mozilla_dom_ContentParent_h
#define mozilla_dom_ContentParent_h
#include "mozilla/dom/NuwaParent.h"
#include "mozilla/dom/PContentParent.h"
#include "mozilla/dom/nsIContentParent.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
@ -383,6 +384,9 @@ public:
bool HasGamepadListener() const { return mHasGamepadListener; }
void SetNuwaParent(NuwaParent* aNuwaParent) { mNuwaParent = aNuwaParent; }
void ForkNewProcess(bool aBlocking);
protected:
void OnChannelConnected(int32_t pid) override;
virtual void ActorDestroy(ActorDestroyReason why) override;
@ -775,10 +779,10 @@ private:
virtual bool RecvSystemMessageHandled() override;
virtual bool RecvNuwaReady() override;
virtual bool RecvAddNewProcess(const uint32_t& aPid,
InfallibleTArray<ProtocolFdMapping>&& aFds) override;
// Callbacks from NuwaParent.
void OnNuwaReady();
void OnNewProcessCreated(uint32_t aPid,
UniquePtr<nsTArray<ProtocolFdMapping>>&& aFds);
virtual bool RecvCreateFakeVolume(const nsString& fsName, const nsString& mountPoint) override;
@ -916,6 +920,9 @@ private:
friend class CrashReporterParent;
// Allows NuwaParent to access OnNuwaReady() and OnNewProcessCreated().
friend class NuwaParent;
nsRefPtr<nsConsoleService> mConsoleService;
nsConsoleService* GetConsoleService();
@ -933,6 +940,11 @@ private:
#endif
PProcessHangMonitorParent* mHangMonitorActor;
// NuwaParent and ContentParent hold strong references to each other. The
// cycle will be broken when either actor is destroyed.
nsRefPtr<NuwaParent> mNuwaParent;
#ifdef MOZ_ENABLE_PROFILER_SPS
nsRefPtr<mozilla::ProfileGatherer> mGatherer;
#endif

256
dom/ipc/NuwaChild.cpp Normal file
Просмотреть файл

@ -0,0 +1,256 @@
/* -*- 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 "ContentChild.h"
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#endif
#include "mozilla/dom/ContentChild.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/ProtocolUtils.h"
#if defined(MOZ_CONTENT_SANDBOX)
#if defined(XP_LINUX)
#include "mozilla/Sandbox.h"
#include "mozilla/SandboxInfo.h"
#elif defined(XP_MACOSX)
#include "mozilla/Sandbox.h"
#endif
#endif
#include "mozilla/unused.h"
#include "nsXULAppAPI.h"
#include "NuwaChild.h"
using namespace mozilla::ipc;
using namespace mozilla::dom;
namespace mozilla {
namespace dom {
#ifdef MOZ_NUWA_PROCESS
namespace {
class CallNuwaSpawn: public nsRunnable
{
public:
NS_IMETHOD Run()
{
NuwaSpawn();
if (IsNuwaProcess()) {
return NS_OK;
}
// In the new process.
ContentChild* child = ContentChild::GetSingleton();
child->InitProcessAttributes();
// Perform other after-fork initializations.
InitOnContentProcessCreated();
return NS_OK;
}
};
static void
DoNuwaFork()
{
NuwaSpawnPrepare(); // NuwaSpawn will be blocked.
{
nsCOMPtr<nsIRunnable> callSpawn(new CallNuwaSpawn());
NS_DispatchToMainThread(callSpawn);
}
// IOThread should be blocked here for waiting NuwaSpawn().
NuwaSpawnWait(); // Now! NuwaSpawn can go.
// Here, we can make sure the spawning was finished.
}
/**
* This function should keep IO thread in a stable state and freeze it
* until the spawning is finished.
*/
static void
RunNuwaFork()
{
if (NuwaCheckpointCurrentThread()) {
DoNuwaFork();
}
}
static bool sNuwaForking = false;
void
NuwaFork()
{
if (sNuwaForking) { // No reentry.
return;
}
sNuwaForking = true;
MessageLoop* ioloop = XRE_GetIOMessageLoop();
ioloop->PostTask(FROM_HERE, NewRunnableFunction(RunNuwaFork));
}
} // Anonymous namespace.
#endif
NuwaChild* NuwaChild::sSingleton;
NuwaChild*
NuwaChild::GetSingleton()
{
MOZ_ASSERT(NS_IsMainThread());
if (!sSingleton) {
PNuwaChild* nuwaChild =
BackgroundChild::GetForCurrentThread()->SendPNuwaConstructor();
MOZ_ASSERT(nuwaChild);
sSingleton = static_cast<NuwaChild*>(nuwaChild);
}
return sSingleton;
}
bool
NuwaChild::RecvFork()
{
#ifdef MOZ_NUWA_PROCESS
if (!IsNuwaProcess()) {
NS_ERROR(
nsPrintfCString(
"Terminating child process %d for unauthorized IPC message: "
"RecvFork(%d)", getpid()).get());
return false;
}
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableFunction(&NuwaFork);
MOZ_ASSERT(runnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
return true;
#else
NS_ERROR("NuwaChild::RecvFork() not implemented!");
return false;
#endif
}
} // namespace dom
} // namespace mozilla
extern "C" {
#if defined(MOZ_NUWA_PROCESS)
NS_EXPORT void
GetProtoFdInfos(NuwaProtoFdInfo* aInfoList,
size_t aInfoListSize,
size_t* aInfoSize)
{
size_t i = 0;
mozilla::dom::ContentChild* content =
mozilla::dom::ContentChild::GetSingleton();
aInfoList[i].protoId = content->GetProtocolId();
aInfoList[i].originFd =
content->GetTransport()->GetFileDescriptor();
i++;
IToplevelProtocol* actors[NUWA_TOPLEVEL_MAX];
size_t count = content->GetOpenedActorsUnsafe(actors, ArrayLength(actors));
for (size_t j = 0; j < count; j++) {
IToplevelProtocol* actor = actors[j];
if (i >= aInfoListSize) {
NS_RUNTIMEABORT("Too many top level protocols!");
}
aInfoList[i].protoId = actor->GetProtocolId();
aInfoList[i].originFd =
actor->GetTransport()->GetFileDescriptor();
i++;
}
if (i > NUWA_TOPLEVEL_MAX) {
NS_RUNTIMEABORT("Too many top level protocols!");
}
*aInfoSize = i;
}
class RunAddNewIPCProcess : public nsRunnable
{
public:
RunAddNewIPCProcess(pid_t aPid,
nsTArray<mozilla::ipc::ProtocolFdMapping>& aMaps)
: mPid(aPid)
{
mMaps.SwapElements(aMaps);
}
NS_IMETHOD Run()
{
NuwaChild::GetSingleton()->SendAddNewProcess(mPid, mMaps);
MOZ_ASSERT(sNuwaForking);
sNuwaForking = false;
return NS_OK;
}
private:
pid_t mPid;
nsTArray<mozilla::ipc::ProtocolFdMapping> mMaps;
};
/**
* AddNewIPCProcess() is called by Nuwa process to tell the parent
* process that a new process is created.
*
* In the newly created process, ResetContentChildTransport() is called to
* reset fd for the IPC Channel and the session.
*/
NS_EXPORT void
AddNewIPCProcess(pid_t aPid, NuwaProtoFdInfo* aInfoList, size_t aInfoListSize)
{
nsTArray<mozilla::ipc::ProtocolFdMapping> maps;
for (size_t i = 0; i < aInfoListSize; i++) {
int _fd = aInfoList[i].newFds[NUWA_NEWFD_PARENT];
mozilla::ipc::FileDescriptor fd(_fd);
mozilla::ipc::ProtocolFdMapping map(aInfoList[i].protoId, fd);
maps.AppendElement(map);
}
nsRefPtr<RunAddNewIPCProcess> runner = new RunAddNewIPCProcess(aPid, maps);
NS_DispatchToMainThread(runner);
}
NS_EXPORT void
OnNuwaProcessReady()
{
NuwaChild* nuwaChild = NuwaChild::GetSingleton();
MOZ_ASSERT(nuwaChild);
mozilla::unused << nuwaChild->SendNotifyReady();
}
NS_EXPORT void
AfterNuwaFork()
{
SetCurrentProcessPrivileges(base::PRIVILEGES_DEFAULT);
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
mozilla::SandboxEarlyInit(XRE_GetProcessType(), /* isNuwa: */ false);
#endif
}
#endif // MOZ_NUWA_PROCESS
}

34
dom/ipc/NuwaChild.h Normal file
Просмотреть файл

@ -0,0 +1,34 @@
/* -*- 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_dom_NuwaChild_h
#define mozilla_dom_NuwaChild_h
#include "mozilla/Assertions.h"
#include "mozilla/dom/PNuwaChild.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace dom {
class NuwaChild: public mozilla::dom::PNuwaChild
{
public:
virtual bool RecvFork() override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override
{ }
static NuwaChild* GetSingleton();
private:
static NuwaChild* sSingleton;
};
} // namespace dom
} // namespace mozilla
#endif

263
dom/ipc/NuwaParent.cpp Normal file
Просмотреть файл

@ -0,0 +1,263 @@
/* -*- 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/dom/ContentParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/unused.h"
#include "nsThreadUtils.h"
#include "NuwaParent.h"
using namespace mozilla::ipc;
using namespace mozilla::dom;
using namespace IPC;
namespace mozilla {
namespace dom {
/*static*/ NuwaParent*
NuwaParent::Alloc() {
nsRefPtr<NuwaParent> actor = new NuwaParent();
return actor.forget().take();
}
/*static*/ bool
NuwaParent::ActorConstructed(mozilla::dom::PNuwaParent *aActor)
{
NuwaParent* actor = static_cast<NuwaParent*>(aActor);
actor->ActorConstructed();
return true;
}
/*static*/ bool
NuwaParent::Dealloc(mozilla::dom::PNuwaParent *aActor)
{
nsRefPtr<NuwaParent> actor = dont_AddRef(static_cast<NuwaParent*>(aActor));
return true;
}
NuwaParent::NuwaParent()
: mBlocked(false)
, mMonitor("NuwaParent")
, mClonedActor(nullptr)
, mWorkerThread(do_GetCurrentThread())
, mNewProcessPid(0)
{
AssertIsOnBackgroundThread();
}
NuwaParent::~NuwaParent()
{
// Both the worker thread and the main thread (ContentParent) hold a ref to
// this. The instance may be destroyed on either thread.
MOZ_ASSERT(!mContentParent);
}
inline void
NuwaParent::AssertIsOnWorkerThread()
{
nsCOMPtr<nsIThread> currentThread = do_GetCurrentThread();
MOZ_ASSERT(currentThread == mWorkerThread);
}
bool
NuwaParent::ActorConstructed()
{
AssertIsOnWorkerThread();
MOZ_ASSERT(Manager());
MOZ_ASSERT(!mContentParent);
mContentParent = BackgroundParent::GetContentParent(Manager());
if (!mContentParent) {
return false;
}
// mContentParent is guaranteed to be alive. It's safe to set its backward ref
// to this.
mContentParent->SetNuwaParent(this);
return true;
}
mozilla::ipc::IProtocol*
NuwaParent::CloneProtocol(Channel* aChannel,
ProtocolCloneContext* aCtx)
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<NuwaParent> self = this;
MonitorAutoLock lock(mMonitor);
// Alloc NuwaParent on the worker thread.
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([self] () -> void
{
MonitorAutoLock lock(self->mMonitor);
// XXX Calling NuwaParent::Alloc() leads to a compilation error. Use
// self->Alloc() as a workaround.
self->mClonedActor = self->Alloc();
lock.Notify();
});
MOZ_ASSERT(runnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mWorkerThread->Dispatch(runnable,
NS_DISPATCH_NORMAL)));
while (!mClonedActor) {
lock.Wait();
}
nsRefPtr<NuwaParent> actor = mClonedActor;
mClonedActor = nullptr;
// mManager of the cloned actor is assigned after returning from this method.
// We can't call ActorConstructed() right after Alloc() in the above runnable.
// To be safe we dispatch a runnable to the current thread to do it.
runnable = NS_NewRunnableFunction([actor] () -> void
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIRunnable> nested = NS_NewRunnableFunction([actor] () -> void
{
AssertIsOnBackgroundThread();
// Call NuwaParent::ActorConstructed() on the worker thread.
actor->ActorConstructed();
// The actor can finally be deleted after fully constructed.
mozilla::unused << actor->Send__delete__(actor);
});
MOZ_ASSERT(nested);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
actor->mWorkerThread->Dispatch(nested, NS_DISPATCH_NORMAL)));
});
MOZ_ASSERT(runnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
return actor;
}
void
NuwaParent::ActorDestroy(ActorDestroyReason aWhy)
{
AssertIsOnWorkerThread();
nsRefPtr<NuwaParent> self = this;
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([self] () -> void
{
// These extra nsRefPtr serve as kungFuDeathGrip to keep both objects from
// deletion in breaking the ref cycle.
nsRefPtr<ContentParent> contentParent = self->mContentParent;
contentParent->SetNuwaParent(nullptr);
// Need to clear the ref to ContentParent on the main thread.
self->mContentParent = nullptr;
});
MOZ_ASSERT(runnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
}
bool
NuwaParent::RecvNotifyReady()
{
#ifdef MOZ_NUWA_PROCESS
if (!mContentParent || !mContentParent->IsNuwaProcess()) {
NS_ERROR("Received NotifyReady() message from a non-Nuwa process.");
return false;
}
// Creating a NonOwningRunnableMethod here is safe because refcount changes of
// mContentParent have to go the the main thread. The mContentParent will
// be alive when the runnable runs.
nsCOMPtr<nsIRunnable> runnable =
NS_NewNonOwningRunnableMethod(mContentParent.get(),
&ContentParent::OnNuwaReady);
MOZ_ASSERT(runnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
return true;
#else
NS_ERROR("NuwaParent::RecvNotifyReady() not implemented!");
return false;
#endif
}
bool
NuwaParent::RecvAddNewProcess(const uint32_t& aPid,
nsTArray<ProtocolFdMapping>&& aFds)
{
#ifdef MOZ_NUWA_PROCESS
if (!mContentParent || !mContentParent->IsNuwaProcess()) {
NS_ERROR("Received AddNewProcess() message from a non-Nuwa process.");
return false;
}
mNewProcessPid = aPid;
mNewProcessFds->SwapElements(aFds);
MonitorAutoLock lock(mMonitor);
if (mBlocked) {
// Unblock ForkNewProcess().
mMonitor.Notify();
mBlocked = false;
} else {
nsCOMPtr<nsIRunnable> runnable =
NS_NewNonOwningRunnableMethodWithArgs<
uint32_t,
UniquePtr<nsTArray<ProtocolFdMapping>>&& >(
mContentParent.get(),
&ContentParent::OnNewProcessCreated,
mNewProcessPid,
Move(mNewProcessFds));
MOZ_ASSERT(runnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
}
return true;
#else
NS_ERROR("NuwaParent::RecvAddNewProcess() not implemented!");
return false;
#endif
}
bool
NuwaParent::ForkNewProcess(uint32_t& aPid,
UniquePtr<nsTArray<ProtocolFdMapping>>&& aFds,
bool aBlocking)
{
MOZ_ASSERT(mWorkerThread);
MOZ_ASSERT(NS_IsMainThread());
mNewProcessFds = Move(aFds);
nsRefPtr<NuwaParent> self = this;
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([self] () -> void
{
mozilla::unused << self->SendFork();
});
MOZ_ASSERT(runnable);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mWorkerThread->Dispatch(runnable,
NS_DISPATCH_NORMAL)));
if (!aBlocking) {
return false;
}
MonitorAutoLock lock(mMonitor);
mBlocked = true;
while (mBlocked) {
// This will be notified in NuwaParent::RecvAddNewProcess().
lock.Wait();
}
if (!mNewProcessPid) {
return false;
}
aPid = mNewProcessPid;
aFds = Move(mNewProcessFds);
mNewProcessPid = 0;
return true;
}
} // namespace dom
} // namespace mozilla

73
dom/ipc/NuwaParent.h Normal file
Просмотреть файл

@ -0,0 +1,73 @@
/* -*- 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_dom_NuwaParent_h
#define mozilla_dom_NuwaParent_h
#include "base/message_loop.h"
#include "mozilla/dom/PNuwaParent.h"
#include "mozilla/Monitor.h"
#include "mozilla/nsRefPtr.h"
namespace mozilla {
namespace dom {
class ContentParent;
class NuwaParent : public mozilla::dom::PNuwaParent
{
public:
explicit NuwaParent();
// Called on the main thread.
bool ForkNewProcess(uint32_t& aPid,
UniquePtr<nsTArray<ProtocolFdMapping>>&& aFds,
bool aBlocking);
// Called on the background thread.
bool ActorConstructed();
// Both the worker thread and the main thread hold a ref to this.
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NuwaParent)
// Functions to be invoked by the manager of this actor to alloc/dealloc the
// actor.
static NuwaParent* Alloc();
static bool ActorConstructed(mozilla::dom::PNuwaParent *aActor);
static bool Dealloc(mozilla::dom::PNuwaParent *aActor);
protected:
virtual ~NuwaParent();
virtual bool RecvNotifyReady() override;
virtual bool RecvAddNewProcess(const uint32_t& aPid,
nsTArray<ProtocolFdMapping>&& aFds) override;
virtual mozilla::ipc::IProtocol*
CloneProtocol(Channel* aChannel,
ProtocolCloneContext* aCtx) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
private:
void AssertIsOnWorkerThread();
bool mBlocked;
mozilla::Monitor mMonitor;
NuwaParent* mClonedActor;
nsCOMPtr<nsIThread> mWorkerThread;
uint32_t mNewProcessPid;
UniquePtr<nsTArray<ProtocolFdMapping>> mNewProcessFds;
// The mutual reference will be broken on the main thread.
nsRefPtr<ContentParent> mContentParent;
};
} // namespace dom
} // namespace mozilla
#endif

Просмотреть файл

@ -579,9 +579,6 @@ child:
// Notify volume is removed.
VolumeRemoved(nsString fsName);
// Ask the Nuwa process to create a new child process.
NuwaFork();
NotifyProcessPriorityChanged(ProcessPriority priority);
MinimizeMemoryUsage();
@ -888,10 +885,6 @@ parent:
// Notify the parent that the child has finished handling a system message.
async SystemMessageHandled();
NuwaReady();
sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds);
// called by the child (test code only) to propagate volume changes to the parent
async CreateFakeVolume(nsString fsName, nsString mountPoint);
async SetFakeVolumeState(nsString fsName, int32_t fsState);

31
dom/ipc/PNuwa.ipdl Normal file
Просмотреть файл

@ -0,0 +1,31 @@
/* -*- 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 protocol PBackground;
include ProtocolTypes;
namespace mozilla {
namespace dom {
sync protocol PNuwa
{
manager PBackground;
child:
// Ask the Nuwa process to create a new child process.
async Fork();
// This message will be sent to non-Nuwa process, or to Nuwa process during
// test.
async __delete__();
parent:
async NotifyReady();
sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds);
};
} // namespace layout
} // namespace mozilla

Просмотреть файл

@ -278,10 +278,15 @@ PreallocatedProcessManagerImpl::GetSpareProcess()
{
MOZ_ASSERT(NS_IsMainThread());
if (mSpareProcesses.IsEmpty()) {
if (!mIsNuwaReady) {
return nullptr;
}
if (mSpareProcesses.IsEmpty()) {
// After this call, there should be a spare process.
mPreallocatedAppProcess->ForkNewProcess(true);
}
nsRefPtr<ContentParent> process = mSpareProcesses.LastElement();
mSpareProcesses.RemoveElementAt(mSpareProcesses.Length() - 1);
@ -369,7 +374,7 @@ PreallocatedProcessManagerImpl::PreallocatedProcessReady()
void
PreallocatedProcessManagerImpl::NuwaFork()
{
mozilla::unused << mPreallocatedAppProcess->SendNuwaFork();
mPreallocatedAppProcess->ForkNewProcess(false);
}
#endif

Просмотреть файл

@ -34,6 +34,8 @@ EXPORTS.mozilla.dom += [
'FilePickerParent.h',
'nsIContentChild.h',
'nsIContentParent.h',
'NuwaChild.h',
'NuwaParent.h',
'PermissionMessageUtils.h',
'StructuredCloneUtils.h',
'TabChild.h',
@ -62,6 +64,8 @@ UNIFIED_SOURCES += [
'FilePickerParent.cpp',
'nsIContentChild.cpp',
'nsIContentParent.cpp',
'NuwaChild.cpp',
'NuwaParent.cpp',
'PermissionMessageUtils.cpp',
'PreallocatedProcessManager.cpp',
'ProcessPriorityManager.cpp',
@ -101,6 +105,7 @@ IPDL_SOURCES += [
'PDocumentRenderer.ipdl',
'PFilePicker.ipdl',
'PMemoryReportRequest.ipdl',
'PNuwa.ipdl',
'PPluginWidget.ipdl',
'PProcessHangMonitor.ipdl',
'PScreenManager.ipdl',

Просмотреть файл

@ -14,6 +14,7 @@
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/MessagePortChild.h"
#include "mozilla/dom/NuwaChild.h"
#include "mozilla/ipc/PBackgroundTestChild.h"
#include "mozilla/layout/VsyncChild.h"
#include "mozilla/net/PUDPSocketChild.h"
@ -57,6 +58,7 @@ using mozilla::net::PUDPSocketChild;
using mozilla::dom::cache::PCacheChild;
using mozilla::dom::cache::PCacheStorageChild;
using mozilla::dom::cache::PCacheStreamControlChild;
using mozilla::dom::PNuwaChild;
// -----------------------------------------------------------------------------
// BackgroundChildImpl::ThreadLocal
@ -350,6 +352,21 @@ BackgroundChildImpl::DeallocPMessagePortChild(PMessagePortChild* aActor)
return true;
}
PNuwaChild*
BackgroundChildImpl::AllocPNuwaChild()
{
return new mozilla::dom::NuwaChild();
}
bool
BackgroundChildImpl::DeallocPNuwaChild(PNuwaChild* aActor)
{
MOZ_ASSERT(aActor);
delete aActor;
return true;
}
} // namespace ipc
} // namespace mozilla

Просмотреть файл

@ -122,6 +122,12 @@ protected:
virtual bool
DeallocPMessagePortChild(PMessagePortChild* aActor) override;
virtual PNuwaChild*
AllocPNuwaChild() override;
virtual bool
DeallocPNuwaChild(PNuwaChild* aActor) override;
};
class BackgroundChildImpl::ThreadLocal final

Просмотреть файл

@ -9,6 +9,7 @@
#include "mozilla/AppProcessChecker.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/NuwaParent.h"
#include "mozilla/dom/PBlobParent.h"
#include "mozilla/dom/MessagePortParent.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
@ -42,6 +43,8 @@ using mozilla::dom::cache::PCacheStorageParent;
using mozilla::dom::cache::PCacheStreamControlParent;
using mozilla::dom::MessagePortParent;
using mozilla::dom::PMessagePortParent;
using mozilla::dom::PNuwaParent;
using mozilla::dom::NuwaParent;
using mozilla::dom::UDPSocketParent;
namespace {
@ -233,6 +236,24 @@ BackgroundParentImpl::DeallocPFileDescriptorSetParent(
return true;
}
PNuwaParent*
BackgroundParentImpl::AllocPNuwaParent()
{
return mozilla::dom::NuwaParent::Alloc();
}
bool
BackgroundParentImpl::RecvPNuwaConstructor(PNuwaParent* aActor)
{
return mozilla::dom::NuwaParent::ActorConstructed(aActor);
}
bool
BackgroundParentImpl::DeallocPNuwaParent(PNuwaParent *aActor)
{
return mozilla::dom::NuwaParent::Dealloc(aActor);
}
BackgroundParentImpl::PVsyncParent*
BackgroundParentImpl::AllocPVsyncParent()
{

Просмотреть файл

@ -86,6 +86,15 @@ protected:
virtual bool
DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
virtual PNuwaParent*
AllocPNuwaParent() override;
virtual bool
RecvPNuwaConstructor(PNuwaParent* aActor) override;
virtual bool
DeallocPNuwaParent(PNuwaParent* aActor) override;
virtual PServiceWorkerManagerParent*
AllocPServiceWorkerManagerParent() override;

Просмотреть файл

@ -13,8 +13,9 @@
#ifdef MOZ_NUWA_PROCESS
#include "ipc/Nuwa.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/hal_sandbox/PHalParent.h"
#include "mozilla/dom/PContent.h"
#include "mozilla/dom/PNuwa.h"
#include "mozilla/hal_sandbox/PHal.h"
#endif
#include "mozilla/Assertions.h"
@ -176,8 +177,8 @@ ProcessLink::SendMessage(Message *msg)
#ifdef MOZ_NUWA_PROCESS
if (mIsToNuwaProcess && mozilla::dom::ContentParent::IsNuwaReady()) {
switch (msg->type()) {
case mozilla::dom::PContent::Msg_NuwaFork__ID:
case mozilla::dom::PContent::Reply_AddNewProcess__ID:
case mozilla::dom::PNuwa::Msg_Fork__ID:
case mozilla::dom::PNuwa::Reply_AddNewProcess__ID:
case mozilla::dom::PContent::Msg_NotifyPhoneStateChange__ID:
case mozilla::hal_sandbox::PHal::Msg_NotifyNetworkChange__ID:
case GOODBYE_MESSAGE_TYPE:

Просмотреть файл

@ -11,6 +11,7 @@ include protocol PCacheStorage;
include protocol PCacheStreamControl;
include protocol PFileDescriptorSet;
include protocol PMessagePort;
include protocol PNuwa;
include protocol PServiceWorkerManager;
include protocol PUDPSocket;
include protocol PVsync;
@ -36,6 +37,7 @@ sync protocol PBackground
manages PCacheStreamControl;
manages PFileDescriptorSet;
manages PMessagePort;
manages PNuwa;
manages PServiceWorkerManager;
manages PUDPSocket;
manages PVsync;
@ -60,6 +62,8 @@ parent:
PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
PNuwa();
MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
child: