Force child processes to close and wait for them on shutdown

This commit is contained in:
Ben Turner 2009-09-02 17:18:27 -07:00
parent 6e6fa8eaec
commit 0f1d5c36a0
11 changed files with 156 additions and 13 deletions

View File

@ -21,6 +21,8 @@ child:
TestShell();
~TestShell();
Quit();
};
}

View File

@ -8,6 +8,9 @@
#include "nsXULAppAPI.h"
#include "base/message_loop.h"
#include "base/task.h"
using namespace mozilla::ipc;
namespace mozilla {
@ -16,6 +19,7 @@ namespace dom {
ContentProcessChild* ContentProcessChild::sSingleton;
ContentProcessChild::ContentProcessChild()
: mQuit(PR_FALSE)
{
}
@ -73,9 +77,29 @@ ContentProcessChild::TestShellDestructor(TestShellProtocolChild* shell)
void
ContentProcessChild::Quit()
{
NS_ASSERTION(mQuit, "Exiting uncleanly!");
mIFrames.Clear();
mTestShells.Clear();
}
static void
QuitIOLoop()
{
MessageLoop::current()->Quit();
}
nsresult
ContentProcessChild::RecvQuit()
{
mQuit = PR_TRUE;
Quit();
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
NewRunnableFunction(&QuitIOLoop));
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -33,6 +33,7 @@ public:
virtual nsresult TestShellDestructor(TestShellProtocolChild*);
void Quit();
virtual nsresult RecvQuit();
private:
static ContentProcessChild* sSingleton;
@ -40,6 +41,8 @@ private:
nsTArray<nsAutoPtr<IFrameEmbeddingProtocolChild> > mIFrames;
nsTArray<nsAutoPtr<TestShellProtocolChild> > mTestShells;
PRBool mQuit;
DISALLOW_EVIL_CONSTRUCTORS(ContentProcessChild);
};

View File

@ -5,7 +5,19 @@
#include "TabParent.h"
#include "mozilla/ipc/TestShellParent.h"
#include "nsIObserverService.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
using namespace mozilla::ipc;
using mozilla::MonitorAutoEnter;
namespace {
PRBool gSingletonDied = PR_FALSE;
}
namespace mozilla {
namespace dom {
@ -15,10 +27,20 @@ ContentProcessParent* ContentProcessParent::gSingleton;
ContentProcessParent*
ContentProcessParent::GetSingleton()
{
if (!gSingleton)
gSingleton = new ContentProcessParent();
return gSingleton;
if (!gSingleton && !gSingletonDied) {
nsRefPtr<ContentProcessParent> parent = new ContentProcessParent();
if (parent) {
nsCOMPtr<nsIObserverService> obs =
do_GetService("@mozilla.org/observer-service;1");
if (obs) {
if (NS_SUCCEEDED(obs->AddObserver(parent, "xpcom-shutdown",
PR_FALSE))) {
gSingleton = parent;
}
}
}
}
return gSingleton;
}
TabParent*
@ -34,15 +56,50 @@ ContentProcessParent::CreateTestShell()
}
ContentProcessParent::ContentProcessParent()
: mSubprocess(GeckoProcessType_Content)
: mMonitor("ContentProcessParent::mMonitor")
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// TODO: async launching!
mSubprocess.SyncLaunch();
Open(mSubprocess.GetChannel());
mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, this);
mSubprocess->SyncLaunch();
Open(mSubprocess->GetChannel());
}
ContentProcessParent::~ContentProcessParent()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(gSingleton == this, "More than one singleton?!");
gSingletonDied = PR_TRUE;
gSingleton = nsnull;
}
NS_IMPL_ISUPPORTS1(ContentProcessParent, nsIObserver)
NS_IMETHODIMP
ContentProcessParent::Observe(nsISupports* aSubject,
const char* aTopic,
const PRUnichar* aData)
{
if (!strcmp(aTopic, "xpcom-shutdown") && mSubprocess) {
SendQuit();
#ifdef OS_WIN
MonitorAutoEnter mon(mMonitor);
while (mSubprocess) {
mon.Wait();
}
#endif
}
return NS_OK;
}
void
ContentProcessParent::OnWaitableEventSignaled(base::WaitableEvent *event)
{
// The child process has died! Sadly we're on the wrong thread to do much.
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
MonitorAutoEnter mon(mMonitor);
mSubprocess = nsnull;
mon.Notify();
}
IFrameEmbeddingProtocolParent*

View File

@ -4,9 +4,14 @@
#ifndef mozilla_dom_ContentProcessParent_h
#define mozilla_dom_ContentProcessParent_h
#include "base/waitable_event_watcher.h"
#include "mozilla/dom/ContentProcessProtocolParent.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "nsIObserver.h"
#include "mozilla/Monitor.h"
namespace mozilla {
namespace ipc {
class TestShellParent;
@ -17,7 +22,9 @@ namespace dom {
class TabParent;
class ContentProcessParent
: private ContentProcessProtocolParent
: private ContentProcessProtocolParent,
public base::WaitableEventWatcher::Delegate,
public nsIObserver
{
private:
typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
@ -30,6 +37,11 @@ public:
static ContentProcessParent* FreeSingleton();
#endif
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
virtual void OnWaitableEventSignaled(base::WaitableEvent *event);
TabParent* CreateTab(const MagicWindowHandle& hwnd);
mozilla::ipc::TestShellParent* CreateTestShell();
@ -50,7 +62,9 @@ private:
virtual TestShellProtocolParent* TestShellConstructor();
virtual nsresult TestShellDestructor(TestShellProtocolParent* shell);
GeckoChildProcessHost mSubprocess;
mozilla::Monitor mMonitor;
GeckoChildProcessHost* mSubprocess;
};
} // namespace dom

View File

@ -51,6 +51,7 @@ PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath) :
GeckoChildProcessHost(GeckoProcessType_Plugin),
mPluginFilePath(aPluginFilePath)
{
// XXXbent Need to catch crashing plugins by watching the process event!
}
PluginProcessParent::~PluginProcessParent()

View File

@ -97,8 +97,14 @@ class ChildProcessHost :
// Sends the given notification to the notification service on the UI thread.
void Notify(NotificationType type);
#ifdef CHROMIUM_MOZILLA_BUILD
protected:
#endif
// WaitableEventWatcher::Delegate implementation:
virtual void OnWaitableEventSignaled(base::WaitableEvent *event);
#ifdef CHROMIUM_MOZILLA_BUILD
private:
#endif
// By using an internal class as the IPC::Channel::Listener, we can intercept
// OnMessageReceived/OnChannelConnected and do our own processing before

View File

@ -53,11 +53,13 @@ struct RunnableMethodTraits<GeckoChildProcessHost>
static void ReleaseCallee(GeckoChildProcessHost* obj) { }
};
GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType)
GeckoChildProcessHost::GeckoChildProcessHost(GeckoProcessType aProcessType,
base::WaitableEventWatcher::Delegate* aDelegate)
: ChildProcessHost(RENDER_PROCESS), // FIXME/cjones: we should own this enum
mProcessType(aProcessType),
mMonitor("mozilla.ipc.GeckChildProcessHost.mMonitor"),
mLaunched(false)
mLaunched(false),
mDelegate(aDelegate)
{
}
@ -155,3 +157,12 @@ GeckoChildProcessHost::OnChannelError()
{
// XXXbent Notify that the child process is gone?
}
void
GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event)
{
if (mDelegate) {
mDelegate->OnWaitableEventSignaled(event);
}
ChildProcessHost::OnWaitableEventSignaled(event);
}

View File

@ -56,7 +56,8 @@ protected:
typedef mozilla::Monitor Monitor;
public:
GeckoChildProcessHost(GeckoProcessType aProcessType=GeckoProcessType_Default);
GeckoChildProcessHost(GeckoProcessType aProcessType=GeckoProcessType_Default,
base::WaitableEventWatcher::Delegate* aDelegate=nsnull);
bool SyncLaunch(std::vector<std::wstring> aExtraOpts=std::vector<std::wstring>());
bool AsyncLaunch(std::vector<std::wstring> aExtraOpts=std::vector<std::wstring>());
@ -67,6 +68,8 @@ public:
virtual bool CanShutdown() { return true; }
virtual void OnWaitableEventSignaled(base::WaitableEvent *event);
IPC::Channel* GetChannel() {
return channelp();
}
@ -85,6 +88,8 @@ protected:
base::file_handle_mapping_vector mFileMap;
#endif
base::WaitableEventWatcher::Delegate* mDelegate;
private:
DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost);
};

View File

@ -85,6 +85,7 @@
using mozilla::ipc::GeckoChildProcessHost;
using mozilla::ipc::GeckoThread;
using mozilla::ipc::BrowserProcessSubThread;
using mozilla::ipc::ScopedXREEmbed;
using mozilla::plugins::PluginThreadChild;
@ -238,6 +239,8 @@ XRE_StringToChildProcessType(const char* aProcessTypeString)
#ifdef MOZ_IPC
static GeckoProcessType sChildProcessType = GeckoProcessType_Default;
static MessageLoop* sIOMessageLoop;
nsresult
XRE_InitChildProcess(int aArgc,
char* aArgv[],
@ -288,7 +291,11 @@ XRE_InitChildProcess(int aArgc,
ChildProcess process(mainThread);
// Do IPC event loop
MessageLoop::current()->Run();
sIOMessageLoop = MessageLoop::current();
sIOMessageLoop->Run();
sIOMessageLoop = nsnull;
}
return NS_OK;
@ -300,6 +307,16 @@ XRE_GetProcessType()
return sChildProcessType;
}
MessageLoop*
XRE_GetIOMessageLoop()
{
if (sChildProcessType == GeckoProcessType_Default) {
NS_ASSERTION(!sIOMessageLoop, "Shouldn't be set on parent process!");
return BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO);
}
return sIOMessageLoop;
}
namespace {
class MainFunctionRunnable : public nsRunnable

View File

@ -477,6 +477,9 @@ class MessageLoop;
XRE_API(void,
XRE_ShutdownChildProcess, (MessageLoop* aUILoop))
XRE_API(MessageLoop*,
XRE_GetIOMessageLoop, ())
struct JSContext;
struct JSString;