From 46b695e56bc2854e7f7432cf09fd0bc37e0ac9e9 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Tue, 2 Aug 2011 15:35:42 -0400 Subject: [PATCH] Bug 666748 - Optionally support a pool of content processes instead of a single one. r=jdm The followup patch for 669640 must land together with this one. --- content/base/src/nsFrameLoader.cpp | 2 +- content/base/src/nsFrameMessageManager.cpp | 4 ++ dom/ipc/ContentParent.cpp | 59 ++++++++++++++++------ dom/ipc/ContentParent.h | 16 +++--- extensions/cookie/nsPermissionManager.cpp | 34 ++++--------- extensions/cookie/nsPermissionManager.h | 11 ---- modules/libpref/src/init/all.js | 2 + toolkit/components/places/History.cpp | 11 ++-- toolkit/xre/nsAppRunner.cpp | 7 +-- toolkit/xre/nsEmbedFunctions.cpp | 5 +- widget/src/android/nsWindow.cpp | 11 ++-- 11 files changed, 89 insertions(+), 73 deletions(-) diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 2f4bd5a0544..89721ddd7e8 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -1791,7 +1791,7 @@ nsFrameLoader::TryRemoteBrowser() return false; } - ContentParent* parent = ContentParent::GetSingleton(); + ContentParent* parent = ContentParent::GetNewOrUsed(); NS_ASSERTION(parent->IsAlive(), "Process parent should be alive; something is very wrong!"); mRemoteBrowser = parent->CreateTab(chromeFlags); if (mRemoteBrowser) { diff --git a/content/base/src/nsFrameMessageManager.cpp b/content/base/src/nsFrameMessageManager.cpp index f906120b31a..a572899bffb 100644 --- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -822,6 +822,7 @@ bool SendAsyncMessageToChildProcess(void* aCallbackData, const nsAString& aMessage, const nsAString& aJSON) { +#if 0 // Need code for multiple content processes mozilla::dom::ContentParent* cp = mozilla::dom::ContentParent::GetSingleton(PR_FALSE); NS_WARN_IF_FALSE(cp, "No child process!"); @@ -829,6 +830,9 @@ bool SendAsyncMessageToChildProcess(void* aCallbackData, return cp->SendAsyncMessage(nsString(aMessage), nsString(aJSON)); } return true; +#else + return false; +#endif } bool SendSyncMessageToParentProcess(void* aCallbackData, diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 86675d4caa3..3fb43c3b216 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -44,6 +44,7 @@ #include "History.h" #include "mozilla/ipc/TestShellParent.h" #include "mozilla/net/NeckoParent.h" +#include "mozilla/Preferences.h" #include "nsHashPropertyBag.h" #include "nsIFilePicker.h" #include "nsIWindowWatcher.h" @@ -111,6 +112,7 @@ static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static const char* sClipboardTextFlavors[] = { kUnicodeMime }; +using mozilla::Preferences; using namespace mozilla::ipc; using namespace mozilla::net; using namespace mozilla::places; @@ -154,21 +156,39 @@ MemoryReportRequestParent::~MemoryReportRequestParent() MOZ_COUNT_DTOR(MemoryReportRequestParent); } -ContentParent* ContentParent::gSingleton; +nsTArray* ContentParent::gContentParents; ContentParent* -ContentParent::GetSingleton(PRBool aForceNew) +ContentParent::GetNewOrUsed() { - if (gSingleton && !gSingleton->IsAlive()) - gSingleton = nsnull; - - if (!gSingleton && aForceNew) { - nsRefPtr parent = new ContentParent(); - gSingleton = parent; - parent->Init(); + if (!gContentParents) + gContentParents = new nsTArray(); + + PRInt32 maxContentProcesses = Preferences::GetInt("dom.ipc.processCount", 1); + if (maxContentProcesses < 1) + maxContentProcesses = 1; + + if (gContentParents->Length() >= PRUint32(maxContentProcesses)) { + ContentParent* p = (*gContentParents)[rand() % gContentParents->Length()]; + NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in gContentParents?"); + return p; + } + + nsRefPtr p = new ContentParent(); + p->Init(); + gContentParents->AppendElement(p); + return p; +} + +void +ContentParent::GetAll(nsTArray& aArray) +{ + if (!gContentParents) { + aArray.Clear(); + return; } - return gSingleton; + aArray = *gContentParents; } void @@ -195,7 +215,7 @@ ContentParent::Init() threadInt->SetObserver(this); } if (obs) { - obs->NotifyObservers(nsnull, "ipc:content-created", nsnull); + obs->NotifyObservers(static_cast(this), "ipc:content-created", nsnull); } #ifdef ACCESSIBILITY @@ -295,6 +315,14 @@ ContentParent::ActorDestroy(ActorDestroyReason why) if (mRunToCompletionDepth) mRunToCompletionDepth = 0; + if (gContentParents) { + gContentParents->RemoveElement(this); + if (!gContentParents->Length()) { + delete gContentParents; + gContentParents = NULL; + } + } + mIsAlive = false; if (obs) { @@ -362,6 +390,7 @@ ContentParent::ContentParent() , mShouldCallUnblockChild(false) , mIsAlive(true) , mProcessStartTime(time(NULL)) + , mSendPermissionUpdates(false) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content); @@ -380,10 +409,8 @@ ContentParent::~ContentParent() base::CloseProcessHandle(OtherProcess()); NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - //If the previous content process has died, a new one could have - //been started since. - if (gSingleton == this) - gSingleton = nsnull; + NS_ASSERTION(!gContentParents || !gContentParents->Contains(this), + "Should have been removed in ActorDestroy"); } bool @@ -459,7 +486,7 @@ ContentParent::RecvReadPermissions(InfallibleTArray* aPermissio } // Ask for future changes - permissionManager->ChildRequestPermissions(); + mSendPermissionUpdates = true; #endif return true; diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index f7f9c4798a7..4c9717bb1d1 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -77,12 +77,8 @@ private: typedef mozilla::ipc::TestShellParent TestShellParent; public: - static ContentParent* GetSingleton(PRBool aForceNew = PR_TRUE); - -#if 0 - // TODO: implement this somewhere! - static ContentParent* FreeSingleton(); -#endif + static ContentParent* GetNewOrUsed(); + static void GetAll(nsTArray& aArray); NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER @@ -102,12 +98,16 @@ public: void SetChildMemoryReporters(const InfallibleTArray& report); + bool NeedsPermissionsUpdate() { + return mSendPermissionUpdates; + } + protected: void OnChannelConnected(int32 pid); virtual void ActorDestroy(ActorDestroyReason why); private: - static ContentParent* gSingleton; + static nsTArray* gContentParents; // Hide the raw constructor methods since we don't want client code // using them. @@ -229,6 +229,8 @@ private: bool mIsAlive; nsCOMPtr mPrefService; time_t mProcessStartTime; + + bool mSendPermissionUpdates; }; } // namespace dom diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp index f8522181117..486327dac96 100644 --- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -89,23 +89,6 @@ ChildProcess() } -/** - * @returns The parent process object, or if we are not in the parent - * process, nsnull. - */ -static ContentParent* -ParentProcess() -{ - if (!IsChildProcess()) { - ContentParent* cpc = ContentParent::GetSingleton(); - if (!cpc) - NS_RUNTIMEABORT("Content Process is NULL!"); - return cpc; - } - - return nsnull; -} - #define ENSURE_NOT_CHILD_PROCESS_(onError) \ PR_BEGIN_MACRO \ if (IsChildProcess()) { \ @@ -170,7 +153,6 @@ NS_IMPL_ISUPPORTS3(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISu nsPermissionManager::nsPermissionManager() : mLargestID(0) - , mUpdateChildProcess(PR_FALSE) { } @@ -467,12 +449,16 @@ nsPermissionManager::AddInternal(const nsAFlatCString &aHost, DBOperationType aDBOperation) { if (!IsChildProcess()) { - // In the parent, send the update now, if the child is ready - if (mUpdateChildProcess) { - IPC::Permission permission((aHost), - (aType), - aPermission, aExpireType, aExpireTime); - unused << ParentProcess()->SendAddPermission(permission); + IPC::Permission permission((aHost), + (aType), + aPermission, aExpireType, aExpireTime); + + nsTArray cplist; + ContentParent::GetAll(cplist); + for (PRUint32 i = 0; i < cplist.Length(); ++i) { + ContentParent* cp = cplist[i]; + if (cp->NeedsPermissionsUpdate()) + unused << cp->SendAddPermission(permission); } } diff --git a/extensions/cookie/nsPermissionManager.h b/extensions/cookie/nsPermissionManager.h index 78786a32943..79f9b3f0bbb 100644 --- a/extensions/cookie/nsPermissionManager.h +++ b/extensions/cookie/nsPermissionManager.h @@ -250,17 +250,6 @@ private: // An array to store the strings identifying the different types. nsTArray mTypeArray; - - // Whether we should update the child process with every change to a - // permission. This is set to true once the child is ready to receive - // such updates. - PRBool mUpdateChildProcess; - -public: - void ChildRequestPermissions() - { - mUpdateChildProcess = PR_TRUE; - } }; // {4F6B5E00-0C36-11d5-A535-0010A401EB10} diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 8339b99ad39..a8a1574faf5 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1493,6 +1493,8 @@ pref("dom.ipc.plugins.enabled.602plugin.so", false); #endif #endif +pref("dom.ipc.processCount", 1); + pref("svg.smil.enabled", true); pref("font.minimum-size.ar", 0); diff --git a/toolkit/components/places/History.cpp b/toolkit/components/places/History.cpp index 126d1f9dc45..9bc8b6114ba 100644 --- a/toolkit/components/places/History.cpp +++ b/toolkit/components/places/History.cpp @@ -55,6 +55,7 @@ #include "nsThreadUtils.h" #include "nsNetUtil.h" #include "nsIXPConnect.h" +#include "mozilla/unused.h" #include "mozilla/Util.h" #include "nsContentUtils.h" @@ -65,6 +66,7 @@ #define TOPIC_UPDATEPLACES_COMPLETE "places-updatePlaces-complete" using namespace mozilla::dom; +using mozilla::unused; namespace mozilla { namespace places { @@ -1282,10 +1284,11 @@ History::NotifyVisited(nsIURI* aURI) nsAutoScriptBlocker scriptBlocker; if (XRE_GetProcessType() == GeckoProcessType_Default) { - mozilla::dom::ContentParent* cpp = - mozilla::dom::ContentParent::GetSingleton(PR_FALSE); - if (cpp) - (void)cpp->SendNotifyVisited(aURI); + nsTArray cplist; + ContentParent::GetAll(cplist); + for (PRUint32 i = 0; i < cplist.Length(); ++i) { + unused << cplist[i]->SendNotifyVisited(aURI); + } } // If the hash table has not been initialized, then we have nothing to notify diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 4a7f9306949..83009218403 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -121,6 +121,9 @@ #include "nsAppShellCID.h" #include "mozilla/FunctionTimer.h" +#include "mozilla/unused.h" + +using mozilla::unused; #ifdef XP_WIN #include "nsIWinAppHelper.h" @@ -755,9 +758,7 @@ nsXULAppInfo::EnsureContentProcess() if (XRE_GetProcessType() != GeckoProcessType_Default) return NS_ERROR_NOT_AVAILABLE; - ContentParent* c = ContentParent::GetSingleton(); - if (!c) - return NS_ERROR_NOT_AVAILABLE; + unused << ContentParent::GetNewOrUsed(); return NS_OK; } diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp index 912e0c99663..47ef8875076 100644 --- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -712,7 +712,7 @@ TestShellParent* gTestShellParent = nsnull; TestShellParent* GetOrCreateTestShellParent() { if (!gTestShellParent) { - ContentParent* parent = ContentParent::GetSingleton(); + ContentParent* parent = ContentParent::GetNewOrUsed(); NS_ENSURE_TRUE(parent, nsnull); gTestShellParent = parent->CreateTestShell(); NS_ENSURE_TRUE(gTestShellParent, nsnull); @@ -758,7 +758,8 @@ XRE_ShutdownTestShell() { if (!gTestShellParent) return true; - return ContentParent::GetSingleton()->DestroyTestShell(gTestShellParent); + return static_cast(gTestShellParent->Manager())-> + DestroyTestShell(gTestShellParent); } #ifdef MOZ_X11 diff --git a/widget/src/android/nsWindow.cpp b/widget/src/android/nsWindow.cpp index 73fe9f66be2..635b6530255 100644 --- a/widget/src/android/nsWindow.cpp +++ b/widget/src/android/nsWindow.cpp @@ -95,8 +95,8 @@ class ContentCreationNotifier : public nsIObserver const PRUnichar* aData) { if (!strcmp(aTopic, "ipc:content-created")) { - ContentParent *cp = ContentParent::GetSingleton(PR_FALSE); - NS_ABORT_IF_FALSE(cp, "Must have content process if notified of its creation"); + nsCOMPtr cpo = do_QueryInterface(aSubject); + ContentParent* cp = static_cast(cpo.get()); unused << cp->SendScreenSizeChanged(gAndroidScreenBounds); } else if (!strcmp(aTopic, "xpcom-shutdown")) { nsCOMPtr @@ -771,9 +771,10 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) break; // Tell the content process the new screen size. - ContentParent *cp = ContentParent::GetSingleton(PR_FALSE); - if (cp) - unused << cp->SendScreenSizeChanged(gAndroidScreenBounds); + nsTArray cplist; + ContentParent::GetAll(cplist); + for (PRUint32 i = 0; i < cplist.Length(); ++i) + unused << cplist[i]->SendScreenSizeChanged(gAndroidScreenBounds); if (gContentCreationNotifier) break;