Rollup of bug 821192: Ensure that content processes don't see an inconsistent app dir. r=bent,dhylands

Bug 821192, part 1: Fix the watchdog timeout code. r=dhylands
Bug 821192, part 2: Add an interface to join all live content processes. r=bent
Bug 821192, part 3: Join all subprocesses before restarting the main process, when we're e.g. about to apply an update. r=dhylands
This commit is contained in:
Chris Jones 2012-12-28 01:45:16 -08:00
parent 2abd0a40fb
commit fb825fe86d
6 changed files with 105 additions and 1 deletions

View File

@ -163,6 +163,10 @@ nsDataHashtable<nsStringHashKey, ContentParent*>* ContentParent::gAppContentPare
nsTArray<ContentParent*>* ContentParent::gNonAppContentParents;
nsTArray<ContentParent*>* ContentParent::gPrivateContent;
// This is true when subprocess launching is enabled. This is the
// case between StartUp() and ShutDown() or JoinAllSubprocesses().
static bool sCanLaunchSubprocesses;
// The first content child has ID 1, so the chrome process can have ID 0.
static uint64_t gContentChildID = 1;
@ -256,6 +260,8 @@ ContentParent::StartUp()
// the main process goes idle before we preallocate a process
MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableFunction(FirstIdle));
}
sCanLaunchSubprocesses = true;
}
/*static*/ void
@ -263,6 +269,55 @@ ContentParent::ShutDown()
{
// No-op for now. We rely on normal process shutdown and
// ClearOnShutdown() to clean up our state.
sCanLaunchSubprocesses = false;
}
/*static*/ void
ContentParent::JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
Monitor* aMonitor, bool* aDone)
{
const nsTArray<ContentParent*>& processes = *aProcesses;
for (uint32_t i = 0; i < processes.Length(); ++i) {
if (GeckoChildProcessHost* process = processes[i]->mSubprocess) {
process->Join();
}
}
{
MonitorAutoLock lock(*aMonitor);
*aDone = true;
lock.Notify();
}
// Don't touch any arguments to this function from now on.
}
/*static*/ void
ContentParent::JoinAllSubprocesses()
{
MOZ_ASSERT(NS_IsMainThread());
nsAutoTArray<ContentParent*, 8> processes;
GetAll(processes);
if (processes.IsEmpty()) {
printf_stderr("There are no live subprocesses.");
return;
}
printf_stderr("Subprocesses are still alive. Doing emergency join.\n");
bool done = false;
Monitor monitor("mozilla.dom.ContentParent.JoinAllSubprocesses");
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
NewRunnableFunction(
&ContentParent::JoinProcessesIOThread,
&processes, &monitor, &done));
{
MonitorAutoLock lock(monitor);
while (!done) {
lock.Wait();
}
}
sCanLaunchSubprocesses = false;
}
/*static*/ ContentParent*
@ -324,6 +379,10 @@ PrivilegesForApp(mozIApplication* aApp)
/*static*/ TabParent*
ContentParent::CreateBrowserOrApp(const TabContext& aContext)
{
if (!sCanLaunchSubprocesses) {
return nullptr;
}
if (aContext.IsBrowserElement() || !aContext.HasOwnApp()) {
if (ContentParent* cp = GetNewOrUsed(aContext.IsBrowserElement())) {
nsRefPtr<TabParent> tp(new TabParent(aContext));

View File

@ -71,6 +71,13 @@ public:
static void StartUp();
/** Shut down the content-process machinery. */
static void ShutDown();
/**
* Ensure that all subprocesses are terminated and their OS
* resources have been reaped. This is synchronous and can be
* very expensive in general. It also bypasses the normal
* shutdown process.
*/
static void JoinAllSubprocesses();
static ContentParent* GetNewOrUsed(bool aForBrowserElement = false);
@ -136,6 +143,9 @@ private:
static nsTArray<ContentParent*>* gNonAppContentParents;
static nsTArray<ContentParent*>* gPrivateContent;
static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
Monitor* aMonitor, bool* aDone);
static void PreallocateAppProcess();
static void DelayedPreallocateAppProcess();
static void ScheduleDelayedPreallocateAppProcess();

View File

@ -3,6 +3,7 @@
* 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/Hal.h"
#include "mozilla/HalWakeLock.h"
#include "mozilla/ClearOnShutdown.h"
@ -139,6 +140,12 @@ PowerManagerService::Restart()
// because it relies on the Gonk to initialize the Gecko processes to
// restart B2G. It's better to do it here to have a real "restart".
StartForceQuitWatchdog(eHalShutdownMode_Restart, mWatchdogTimeoutSecs);
// Ensure all content processes are dead before we continue
// restarting. This code is used to restart to apply updates, and
// if we don't join all the subprocesses, race conditions can cause
// them to see an inconsistent view of the application directory.
ContentParent::JoinAllSubprocesses();
// To synchronize any unsaved user data before restarting.
SyncProfile();
#ifdef XP_UNIX

View File

@ -71,7 +71,16 @@ ForceQuitWatchdog(void* aParamPtr)
if (paramPtr->timeoutSecs > 0 && paramPtr->timeoutSecs <= 30) {
// If we shut down normally before the timeout, this thread will
// be harmlessly reaped by the OS.
sleep(paramPtr->timeoutSecs);
TimeStamp deadline =
(TimeStamp::Now() + TimeDuration::FromSeconds(paramPtr->timeoutSecs));
while (true) {
TimeDuration remaining = (deadline - TimeStamp::Now());
int sleepSeconds = int(remaining.ToSeconds());
if (sleepSeconds <= 0) {
break;
}
sleep(sleepSeconds);
}
}
hal::ShutdownMode mode = paramPtr->mode;
delete paramPtr;

View File

@ -364,6 +364,20 @@ GeckoChildProcessHost::InitializeChannel()
lock.Notify();
}
void
GeckoChildProcessHost::Join()
{
AssertIOThread();
if (!mChildProcessHandle) {
return;
}
// If this fails, there's nothing we can do.
base::KillProcess(mChildProcessHandle, 0, /*wait*/true);
mChildProcessHandle = 0;
}
int32_t GeckoChildProcessHost::mChildCounter = 0;
//

View File

@ -96,6 +96,11 @@ public:
}
#endif
/**
* Must run on the IO thread. Cause the OS process to exit and
* ensure its OS resources are cleaned up.
*/
void Join();
protected:
GeckoProcessType mProcessType;