Commit c2e19612 authored by grt@chromium.org's avatar grt@chromium.org

Lower the I/O priority of the installer when resonable.

On operating systems that support it (Vista+), begin background
processing mode before doing major disk I/O in the installer if it was
launched below the normal process priority class. One hopes that this
avoids making users' machines unresponsive while an update is being
applied in the background.

BUG=167622
TEST=mostly covered by "setup_unittests.exe
--gtest_filter=SetupUtilTest.Adjust*", but it will also be interesting
to look at verbose installer log files for an install and for a Google
Update-driven update. Only the latter should contain "Entered background
processing mode."

Review URL: https://chromiumcodereview.appspot.com/16023027

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@203982 0039d316-1c4b-4281-b951-d872f2087c98
parent 4dfec746
...@@ -433,6 +433,7 @@ ...@@ -433,6 +433,7 @@
'installer/setup/setup_unittests_resource.h', 'installer/setup/setup_unittests_resource.h',
'installer/setup/setup_util.cc', 'installer/setup/setup_util.cc',
'installer/setup/setup_util_unittest.cc', 'installer/setup/setup_util_unittest.cc',
'installer/setup/setup_util_unittest.h',
], ],
'rules': [ 'rules': [
{ {
......
...@@ -2,12 +2,19 @@ ...@@ -2,12 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "base/command_line.h"
#include "base/test/test_suite.h" #include "base/test/test_suite.h"
#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths.h"
#include "chrome/installer/setup/setup_util_unittest.h"
int main(int argc, char** argv) { int main(int argc, char** argv) {
TestSuite test_suite(argc, argv); TestSuite test_suite(argc, argv);
// Handle the --adjust-process-priority switch, which is used to test the
// installer::AdjustProcessPriority() function in a subprocess.
if (CommandLine::ForCurrentProcess()->HasSwitch(kAdjustProcessPriority))
return DoProcessPriorityAdjustment();
// Register Chrome Path provider so that we can get test data dir. // Register Chrome Path provider so that we can get test data dir.
chrome::RegisterPathProvider(); chrome::RegisterPathProvider();
......
...@@ -639,6 +639,11 @@ installer::InstallStatus InstallProductsHelper( ...@@ -639,6 +639,11 @@ installer::InstallStatus InstallProductsHelper(
const bool system_install = installer_state.system_install(); const bool system_install = installer_state.system_install();
installer::InstallStatus install_status = installer::UNKNOWN_STATUS; installer::InstallStatus install_status = installer::UNKNOWN_STATUS;
// Drop to background processing mode if the process was started below the
// normal process priority class.
bool entered_background_mode = installer::AdjustProcessPriority();
VLOG_IF(1, entered_background_mode) << "Entered background processing mode.";
// For install the default location for chrome.packed.7z is in current // For install the default location for chrome.packed.7z is in current
// folder, so get that value first. // folder, so get that value first.
base::FilePath archive(cmd_line.GetProgram().DirName().Append( base::FilePath archive(cmd_line.GetProgram().DirName().Append(
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/process_util.h" #include "base/process_util.h"
#include "base/string_util.h" #include "base/string_util.h"
#include "base/version.h" #include "base/version.h"
#include "base/win/windows_version.h"
#include "chrome/installer/util/copy_tree_work_item.h" #include "chrome/installer/util/copy_tree_work_item.h"
#include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/installation_state.h"
#include "chrome/installer/util/installer_state.h" #include "chrome/installer/util/installer_state.h"
...@@ -313,6 +314,22 @@ bool WillProductBePresentAfterSetup( ...@@ -313,6 +314,22 @@ bool WillProductBePresentAfterSetup(
return is_affected ? !is_uninstall : is_present; return is_affected ? !is_uninstall : is_present;
} }
bool AdjustProcessPriority() {
if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
DWORD priority_class = ::GetPriorityClass(::GetCurrentProcess());
if (priority_class == 0) {
PLOG(WARNING) << "Failed to get the process's priority class.";
} else if (priority_class == BELOW_NORMAL_PRIORITY_CLASS ||
priority_class == IDLE_PRIORITY_CLASS) {
BOOL result = ::SetPriorityClass(::GetCurrentProcess(),
PROCESS_MODE_BACKGROUND_BEGIN);
PLOG_IF(WARNING, !result) << "Failed to enter background mode.";
return !!result;
}
}
return false;
}
ScopedTokenPrivilege::ScopedTokenPrivilege(const wchar_t* privilege_name) ScopedTokenPrivilege::ScopedTokenPrivilege(const wchar_t* privilege_name)
: is_enabled_(false) { : is_enabled_(false) {
if (!::OpenProcessToken(::GetCurrentProcess(), if (!::OpenProcessToken(::GetCurrentProcess(),
......
...@@ -81,6 +81,11 @@ bool WillProductBePresentAfterSetup( ...@@ -81,6 +81,11 @@ bool WillProductBePresentAfterSetup(
const installer::InstallationState& machine_state, const installer::InstallationState& machine_state,
BrowserDistribution::Type type); BrowserDistribution::Type type);
// Drops the process down to background processing mode on supported OSes if it
// was launched below the normal process priority. Returns true when background
// procesing mode is entered.
bool AdjustProcessPriority();
// This class will enable the privilege defined by |privilege_name| on the // This class will enable the privilege defined by |privilege_name| on the
// current process' token. The privilege will be disabled upon the // current process' token. The privilege will be disabled upon the
// ScopedTokenPrivilege's destruction (unless it was already enabled when the // ScopedTokenPrivilege's destruction (unless it was already enabled when the
......
...@@ -2,17 +2,22 @@ ...@@ -2,17 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/installer/setup/setup_util_unittest.h"
#include <windows.h> #include <windows.h>
#include <string> #include <string>
#include "base/command_line.h"
#include "base/file_util.h" #include "base/file_util.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/process_util.h"
#include "base/threading/platform_thread.h" #include "base/threading/platform_thread.h"
#include "base/time.h" #include "base/time.h"
#include "base/win/scoped_handle.h" #include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths.h"
#include "chrome/installer/setup/setup_util.h" #include "chrome/installer/setup/setup_util.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -189,3 +194,86 @@ TEST(SetupUtilTest, ScopedTokenPrivilegeAlreadyEnabled) { ...@@ -189,3 +194,86 @@ TEST(SetupUtilTest, ScopedTokenPrivilegeAlreadyEnabled) {
ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege)); ASSERT_FALSE(CurrentProcessHasPrivilege(kTestedPrivilege));
} }
const char kAdjustProcessPriority[] = "adjust-process-priority";
PriorityClassChangeResult DoProcessPriorityAdjustment() {
return installer::AdjustProcessPriority() ? PCCR_CHANGED : PCCR_UNCHANGED;
}
namespace {
// A scoper that sets/resets the current process's priority class.
class ScopedPriorityClass {
public:
// Applies |priority_class|, returning an instance if a change was made.
// Otherwise, returns an empty scoped_ptr.
static scoped_ptr<ScopedPriorityClass> Create(DWORD priority_class);
~ScopedPriorityClass();
private:
explicit ScopedPriorityClass(DWORD original_priority_class);
DWORD original_priority_class_;
DISALLOW_COPY_AND_ASSIGN(ScopedPriorityClass);
};
scoped_ptr<ScopedPriorityClass> ScopedPriorityClass::Create(
DWORD priority_class) {
HANDLE this_process = ::GetCurrentProcess();
DWORD original_priority_class = ::GetPriorityClass(this_process);
EXPECT_NE(0U, original_priority_class);
if (original_priority_class && original_priority_class != priority_class) {
BOOL result = ::SetPriorityClass(this_process, priority_class);
EXPECT_NE(FALSE, result);
if (result) {
return scoped_ptr<ScopedPriorityClass>(
new ScopedPriorityClass(original_priority_class));
}
}
return scoped_ptr<ScopedPriorityClass>();
}
ScopedPriorityClass::ScopedPriorityClass(DWORD original_priority_class)
: original_priority_class_(original_priority_class) {}
ScopedPriorityClass::~ScopedPriorityClass() {
BOOL result = ::SetPriorityClass(::GetCurrentProcess(),
original_priority_class_);
EXPECT_NE(FALSE, result);
}
PriorityClassChangeResult RelaunchAndDoProcessPriorityAdjustment() {
CommandLine cmd_line(*CommandLine::ForCurrentProcess());
cmd_line.AppendSwitch(kAdjustProcessPriority);
base::ProcessHandle process_handle = NULL;
int exit_code = 0;
if (!base::LaunchProcess(cmd_line, base::LaunchOptions(),
&process_handle)) {
ADD_FAILURE() << " to launch subprocess.";
} else if (!base::WaitForExitCode(process_handle, &exit_code)) {
ADD_FAILURE() << " to wait for subprocess to exit.";
} else {
return static_cast<PriorityClassChangeResult>(exit_code);
}
return PCCR_UNKNOWN;
}
} // namespace
// Launching a subprocess at normal priority class is a noop.
TEST(SetupUtilTest, AdjustFromNormalPriority) {
ASSERT_EQ(NORMAL_PRIORITY_CLASS, ::GetPriorityClass(::GetCurrentProcess()));
EXPECT_EQ(PCCR_UNCHANGED, RelaunchAndDoProcessPriorityAdjustment());
}
// Launching a subprocess below normal priority class drops it to bg mode for
// sufficiently recent operating systems.
TEST(SetupUtilTest, AdjustFromBelowNormalPriority) {
scoped_ptr<ScopedPriorityClass> below_normal =
ScopedPriorityClass::Create(BELOW_NORMAL_PRIORITY_CLASS);
ASSERT_TRUE(below_normal);
if (base::win::GetVersion() > base::win::VERSION_SERVER_2003)
EXPECT_EQ(PCCR_CHANGED, RelaunchAndDoProcessPriorityAdjustment());
else
EXPECT_EQ(PCCR_UNCHANGED, RelaunchAndDoProcessPriorityAdjustment());
}
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_INSTALLER_SETUP_SETUP_UTIL_UNITTEST_H_
#define CHROME_INSTALLER_SETUP_SETUP_UTIL_UNITTEST_H_
// A command line switch that causes the test harness to exit with the result of
// DoProcessPriorityAdjustment rather than executing all tests.
extern const char kAdjustProcessPriority[];
// Process exit codes when the test harness is run with the
// kAdjustProcessPriority switch.
enum PriorityClassChangeResult {
PCCR_UNKNOWN,
PCCR_UNCHANGED,
PCCR_CHANGED,
};
// Calls AdjustProcessPriority() and returns PCCR_CHANGED or PCCR_UNCHANGED
// based on its true or false result (respectively).
PriorityClassChangeResult DoProcessPriorityAdjustment();
#endif // CHROME_INSTALLER_SETUP_SETUP_UTIL_UNITTEST_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment