mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-27 04:38:02 +00:00
Bug 1272614 - Avoid blocking where possible while waiting for the updater to stage; r=spohl
MozReview-Commit-ID: 1aAU2wxQvMm --HG-- extra : rebase_source : c965bd526d99560203da5b082a396c1102fd22dd
This commit is contained in:
parent
2c2d766102
commit
b29fee8676
@ -44,34 +44,11 @@
|
||||
# define getpid() GetCurrentProcessId()
|
||||
#elif defined(XP_UNIX)
|
||||
# include <unistd.h>
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
//
|
||||
// We use execv to spawn the updater process on all UNIX systems except Mac OSX
|
||||
// since it is known to cause problems on the Mac. Windows has execv, but it
|
||||
// is a faked implementation that doesn't really replace the current process.
|
||||
// Instead it spawns a new process, so we gain nothing from using execv on
|
||||
// Windows.
|
||||
//
|
||||
// On platforms where we are not calling execv, we may need to make the
|
||||
// updater executable wait for the calling process to exit. Otherwise, the
|
||||
// updater may have trouble modifying our executable image (because it might
|
||||
// still be in use). This is accomplished by passing our PID to the updater so
|
||||
// that it can wait for us to exit. This is not perfect as there is a race
|
||||
// condition that could bite us. It's possible that the calling process could
|
||||
// exit before the updater waits on the specified PID, and in the meantime a
|
||||
// new process with the same PID could be created. This situation is unlikely,
|
||||
// however, given the way most operating systems recycle PIDs. We'll take our
|
||||
// chances ;-)
|
||||
//
|
||||
// A similar #define lives in updater.cpp and should be kept in sync with this.
|
||||
//
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
#define USE_EXECV
|
||||
#endif
|
||||
|
||||
static PRLogModuleInfo *
|
||||
GetUpdateLog()
|
||||
{
|
||||
@ -204,7 +181,7 @@ static bool
|
||||
GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr<nsIFile> &result)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = dir->Clone(getter_AddRefs(file));
|
||||
if (NS_FAILED(rv))
|
||||
@ -401,7 +378,7 @@ CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
|
||||
return false;
|
||||
#endif
|
||||
rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN));
|
||||
return NS_SUCCEEDED(rv);
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -593,9 +570,10 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir,
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
// Construct the PID argument for this process. If we are using execv, then
|
||||
// we pass "0" which is then ignored by the updater.
|
||||
#if defined(USE_EXECV)
|
||||
// Construct the PID argument for this process. We start the updater using
|
||||
// execv on all Unix platforms except Mac, so on those platforms we pass 0
|
||||
// instead of a good PID to signal the updater not to try and wait for us.
|
||||
#if defined(XP_UNIX) & !defined(XP_MACOSX)
|
||||
nsAutoCString pid("0");
|
||||
#else
|
||||
nsAutoCString pid;
|
||||
@ -642,14 +620,14 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir,
|
||||
|
||||
LOG(("spawning updater process for replacing [%s]\n", updaterPath.get()));
|
||||
|
||||
#if defined(USE_EXECV)
|
||||
#if defined(XP_UNIX) & !defined(XP_MACOSX)
|
||||
# if defined(MOZ_WIDGET_GONK)
|
||||
// In Gonk, we preload libmozglue, which the updater process doesn't need.
|
||||
// Since the updater will move and delete libmozglue.so, this can actually
|
||||
// stop the /system mount from correctly being remounted as read-only.
|
||||
unsetenv("LD_PRELOAD");
|
||||
# endif
|
||||
execv(updaterPath.get(), argv);
|
||||
exit(execv(updaterPath.get(), argv));
|
||||
#elif defined(XP_WIN)
|
||||
// Switch the application using updater.exe
|
||||
if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
|
||||
@ -789,7 +767,7 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
|
||||
rv = appFile->GetNativePath(appFilePath);
|
||||
if (NS_FAILED(rv))
|
||||
return;
|
||||
|
||||
|
||||
nsAutoCString updaterPath;
|
||||
rv = updater->GetNativePath(updaterPath);
|
||||
if (NS_FAILED(rv))
|
||||
@ -868,6 +846,16 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
|
||||
// in the Windows case. This change was made for all platforms so
|
||||
// that it stays consistent across all OS.
|
||||
|
||||
// On platforms where we are not calling execv, we may need to make the
|
||||
// updater executable wait for the calling process to exit. Otherwise, the
|
||||
// updater may have trouble modifying our executable image (because it might
|
||||
// still be in use). This is accomplished by passing our PID to the updater so
|
||||
// that it can wait for us to exit. This is not perfect as there is a race
|
||||
// condition that could bite us. It's possible that the calling process could
|
||||
// exit before the updater waits on the specified PID, and in the meantime a
|
||||
// new process with the same PID could be created. This situation is unlikely,
|
||||
// however, given the way most operating systems recycle PIDs. We'll take our
|
||||
// chances ;-)
|
||||
// Construct the PID argument for this process. If we are using execv, then
|
||||
// we pass "0" which is then ignored by the updater.
|
||||
nsAutoCString pid;
|
||||
@ -875,7 +863,7 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
|
||||
// Signal the updater application that it should stage the update.
|
||||
pid.AssignASCII("-1");
|
||||
} else {
|
||||
#if defined(USE_EXECV)
|
||||
#if defined(XP_UNIX) & !defined(XP_MACOSX)
|
||||
pid.AssignASCII("0");
|
||||
#else
|
||||
pid.AppendInt((int32_t) getpid());
|
||||
@ -942,12 +930,20 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
|
||||
|
||||
LOG(("spawning updater process [%s]\n", updaterPath.get()));
|
||||
|
||||
#if defined(USE_EXECV)
|
||||
// Don't use execv when staging updates.
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
// We use execv to spawn the updater process on all UNIX systems except Mac OSX
|
||||
// since it is known to cause problems on the Mac. Windows has execv, but it
|
||||
// is a faked implementation that doesn't really replace the current process.
|
||||
// Instead it spawns a new process, so we gain nothing from using execv on
|
||||
// Windows.
|
||||
if (restart) {
|
||||
execv(updaterPath.get(), argv);
|
||||
} else {
|
||||
*outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
|
||||
exit(execv(updaterPath.get(), argv));
|
||||
}
|
||||
*outpid = fork();
|
||||
if (*outpid == -1) {
|
||||
return;
|
||||
} else if (*outpid == 0) {
|
||||
exit(execv(updaterPath.get(), argv));
|
||||
}
|
||||
#elif defined(XP_WIN)
|
||||
// Launch the update using updater.exe
|
||||
@ -985,22 +981,35 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a process until it terminates. This call is blocking.
|
||||
* Wait briefly to see if a process terminates, then return true if it has.
|
||||
*/
|
||||
static void
|
||||
WaitForProcess(ProcessType pt)
|
||||
static bool
|
||||
ProcessHasTerminated(ProcessType pt)
|
||||
{
|
||||
#if defined(XP_WIN)
|
||||
WaitForSingleObject(pt, INFINITE);
|
||||
if (WaitForSingleObject(pt, 1000)) {
|
||||
return false;
|
||||
}
|
||||
CloseHandle(pt);
|
||||
#elif defined(XP_MACOSX)
|
||||
waitpid(pt, 0, 0);
|
||||
return true;
|
||||
#elif defined(XP_UNIX)
|
||||
int exitStatus;
|
||||
bool exited = waitpid(pt, &exitStatus, WNOHANG) > 0;
|
||||
if (!exited) {
|
||||
sleep(1);
|
||||
} else if (WIFEXITED(exitStatus) && (WEXITSTATUS(exitStatus) != 0)) {
|
||||
LOG(("Error while running the updater process, check update.log"));
|
||||
}
|
||||
return exited;
|
||||
#else
|
||||
// No way to have a non-blocking implementation on these platforms,
|
||||
// because we're using NSPR and it only provides a blocking wait.
|
||||
int32_t exitCode;
|
||||
PR_WaitProcess(pt, &exitCode);
|
||||
if (exitCode != 0) {
|
||||
LOG(("Error while running the updater process, check update.log"));
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1024,7 +1033,7 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
|
||||
rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
||||
nsCOMPtr<nsIFile> statusFile;
|
||||
UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
|
||||
switch (status) {
|
||||
@ -1270,8 +1279,11 @@ void
|
||||
nsUpdateProcessor::WaitForProcess()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(), "main thread");
|
||||
::WaitForProcess(mUpdaterPID);
|
||||
NS_DispatchToMainThread(NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone));
|
||||
if (ProcessHasTerminated(mUpdaterPID)) {
|
||||
NS_DispatchToMainThread(NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone));
|
||||
} else {
|
||||
NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1287,4 +1299,3 @@ nsUpdateProcessor::UpdateDone()
|
||||
|
||||
ShutdownWatcherThread();
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ class nsIFile;
|
||||
#if defined(XP_WIN)
|
||||
#include <windows.h>
|
||||
typedef HANDLE ProcessType;
|
||||
#elif defined(XP_MACOSX)
|
||||
#elif defined(XP_UNIX)
|
||||
typedef pid_t ProcessType;
|
||||
#else
|
||||
#include "prproces.h"
|
||||
@ -33,7 +33,7 @@ class nsIFile;
|
||||
* Two directories are passed to this function: greDir (where the actual
|
||||
* binary resides) and appDir (which contains application.ini for XULRunner
|
||||
* apps). If this is not a XULRunner app then appDir is identical to greDir.
|
||||
*
|
||||
*
|
||||
* The argc and argv passed to this function should be what is needed to
|
||||
* relaunch the current process.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user