diff options
author | shrike <shrike@chromium.org> | 2015-06-02 12:53:57 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-02 19:54:25 +0000 |
commit | 8fbe9d3eb4ab56b0be87c3c4e04bb544ede982c7 (patch) | |
tree | 1543cdc456d3f41e6ad786bcc4301ff21ee0d674 | |
parent | 1460c4b34f2dbfc37f5934380f362e2193f64360 (diff) | |
download | chromium_src-8fbe9d3eb4ab56b0be87c3c4e04bb544ede982c7.zip chromium_src-8fbe9d3eb4ab56b0be87c3c4e04bb544ede982c7.tar.gz chromium_src-8fbe9d3eb4ab56b0be87c3c4e04bb544ede982c7.tar.bz2 |
Add support for backgrounding processes on the Mac
Added process_mac.cc with implementations of IsProcessBackgrounded() and
SetProcessBackgrounded().
BUG=460102
Originally Committed: https://crrev.com/e3bb10f7860a1d553c85293bd7d7615c0e7f0fd9
Reverted: https://crrev.com/ce6226a7ffe2c1cb7ac5f6cf34b56b8d217686b9
Second commit: https://crrev.com/0160d130f8a4462fa7bfb8a9924e476d31ba9a48
Second revert: https://crrev.com/93ef7cd278d450b06f4a95fad6577d05b67624aa
Review URL: https://codereview.chromium.org/989703002
Cr-Commit-Position: refs/heads/master@{#332454}
-rw-r--r-- | base/base.gypi | 1 | ||||
-rw-r--r-- | base/process/BUILD.gn | 1 | ||||
-rw-r--r-- | base/process/process.h | 22 | ||||
-rw-r--r-- | base/process/process_mac.cc | 128 | ||||
-rw-r--r-- | base/process/process_posix.cc | 18 | ||||
-rw-r--r-- | base/process/process_unittest.cc | 26 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_apitest.cc | 6 | ||||
-rw-r--r-- | content/browser/child_process_launcher.cc | 10 | ||||
-rw-r--r-- | content/browser/renderer_host/render_process_host_impl.cc | 18 | ||||
-rw-r--r-- | content/public/common/content_switches.cc | 3 | ||||
-rw-r--r-- | content/public/common/content_switches.h | 1 |
11 files changed, 214 insertions, 20 deletions
diff --git a/base/base.gypi b/base/base.gypi index e65b6ad..6e2944a 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -489,6 +489,7 @@ 'process/process_iterator_openbsd.cc', 'process/process_iterator_win.cc', 'process/process_linux.cc', + 'process/process_mac.cc', 'process/process_metrics.cc', 'process/process_metrics.h', 'process/process_metrics_freebsd.cc', diff --git a/base/process/BUILD.gn b/base/process/BUILD.gn index e570647..814459b 100644 --- a/base/process/BUILD.gn +++ b/base/process/BUILD.gn @@ -41,6 +41,7 @@ source_set("process") { "process_iterator_openbsd.cc", "process_iterator_win.cc", "process_linux.cc", + "process_mac.cc", "process_metrics.cc", "process_metrics.h", "process_metrics_freebsd.cc", diff --git a/base/process/process.h b/base/process/process.h index 50dfef1..1559554 100644 --- a/base/process/process.h +++ b/base/process/process.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2011 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. @@ -111,6 +111,24 @@ class BASE_EXPORT Process { // is not required. bool WaitForExitWithTimeout(TimeDelta timeout, int* exit_code); +#if defined(OS_MACOSX) + // The Mac needs a Mach port in order to manipulate a process's priority, + // and there's no good way to get that from base given the pid. These Mac + // variants of the IsProcessBackgrounded and SetProcessBackgrounded API take + // the Mach port for this reason. See crbug.com/460102 + // + // A process is backgrounded when its priority is lower than normal. + // Return true if the process with mach port |task_port| is backgrounded, + // false otherwise. + bool IsProcessBackgrounded(mach_port_t task_port) const; + + // Set the process with the specified mach port as backgrounded. If value is + // true, the priority of the process will be lowered. If value is false, the + // priority of the process will be made "normal" - equivalent to default + // process priority. Returns true if the priority was changed, false + // otherwise. + bool SetProcessBackgrounded(mach_port_t task_port, bool value); +#else // A process is backgrounded when it's priority is lower than normal. // Return true if this process is backgrounded, false otherwise. bool IsProcessBackgrounded() const; @@ -120,7 +138,7 @@ class BASE_EXPORT Process { // will be made "normal" - equivalent to default process priority. // Returns true if the priority was changed, false otherwise. bool SetProcessBackgrounded(bool value); - +#endif // defined(OS_MACOSX) // Returns an integer representing the priority of a process. The meaning // of this value is OS dependent. int GetPriority() const; diff --git a/base/process/process_mac.cc b/base/process/process_mac.cc new file mode 100644 index 0000000..1913cc3 --- /dev/null +++ b/base/process/process_mac.cc @@ -0,0 +1,128 @@ +// Copyright 2015 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. + +#include "base/process/process.h" + +#include "base/mac/mac_util.h" +#include "base/mac/mach_logging.h" + +#include <mach/mach.h> + +// The following was added to <mach/task_policy.h> after 10.8. +// TODO(shrike): Remove the TASK_OVERRIDE_QOS_POLICY ifndef once builders +// reach 10.9 or higher. +#ifndef TASK_OVERRIDE_QOS_POLICY + +#define TASK_OVERRIDE_QOS_POLICY 9 + +typedef struct task_category_policy task_category_policy_data_t; +typedef struct task_category_policy* task_category_policy_t; + +enum task_latency_qos { + LATENCY_QOS_TIER_UNSPECIFIED = 0x0, + LATENCY_QOS_TIER_0 = ((0xFF << 16) | 1), + LATENCY_QOS_TIER_1 = ((0xFF << 16) | 2), + LATENCY_QOS_TIER_2 = ((0xFF << 16) | 3), + LATENCY_QOS_TIER_3 = ((0xFF << 16) | 4), + LATENCY_QOS_TIER_4 = ((0xFF << 16) | 5), + LATENCY_QOS_TIER_5 = ((0xFF << 16) | 6) +}; +typedef integer_t task_latency_qos_t; +enum task_throughput_qos { + THROUGHPUT_QOS_TIER_UNSPECIFIED = 0x0, + THROUGHPUT_QOS_TIER_0 = ((0xFE << 16) | 1), + THROUGHPUT_QOS_TIER_1 = ((0xFE << 16) | 2), + THROUGHPUT_QOS_TIER_2 = ((0xFE << 16) | 3), + THROUGHPUT_QOS_TIER_3 = ((0xFE << 16) | 4), + THROUGHPUT_QOS_TIER_4 = ((0xFE << 16) | 5), + THROUGHPUT_QOS_TIER_5 = ((0xFE << 16) | 6), +}; + +#define LATENCY_QOS_LAUNCH_DEFAULT_TIER LATENCY_QOS_TIER_3 +#define THROUGHPUT_QOS_LAUNCH_DEFAULT_TIER THROUGHPUT_QOS_TIER_3 + +typedef integer_t task_throughput_qos_t; + +struct task_qos_policy { + task_latency_qos_t task_latency_qos_tier; + task_throughput_qos_t task_throughput_qos_tier; +}; + +typedef struct task_qos_policy* task_qos_policy_t; +#define TASK_QOS_POLICY_COUNT \ + ((mach_msg_type_number_t)(sizeof(struct task_qos_policy) / sizeof(integer_t))) + +#endif // TASK_OVERRIDE_QOS_POLICY + +namespace base { + +bool Process::CanBackgroundProcesses() { + return true; +} + +bool Process::IsProcessBackgrounded(mach_port_t task_port) const { + // See SetProcessBackgrounded(). + DCHECK(IsValid()); + DCHECK_NE(task_port, TASK_NULL); + + task_category_policy_data_t category_policy; + mach_msg_type_number_t task_info_count = TASK_CATEGORY_POLICY_COUNT; + boolean_t get_default = FALSE; + + kern_return_t result = + task_policy_get(task_port, TASK_CATEGORY_POLICY, + reinterpret_cast<task_policy_t>(&category_policy), + &task_info_count, &get_default); + MACH_LOG_IF(ERROR, result != KERN_SUCCESS, result) << + "task_policy_get TASK_CATEGORY_POLICY"; + + if (result == KERN_SUCCESS && get_default == FALSE) { + return category_policy.role == TASK_BACKGROUND_APPLICATION; + } + return false; +} + +bool Process::SetProcessBackgrounded(mach_port_t task_port, bool background) { + DCHECK(IsValid()); + DCHECK_NE(task_port, TASK_NULL); + + if (!CanBackgroundProcesses()) { + return false; + } else if (IsProcessBackgrounded(task_port) == background) { + return true; + } + + task_category_policy category_policy; + category_policy.role = + background ? TASK_BACKGROUND_APPLICATION : TASK_FOREGROUND_APPLICATION; + kern_return_t result = + task_policy_set(task_port, TASK_CATEGORY_POLICY, + reinterpret_cast<task_policy_t>(&category_policy), + TASK_CATEGORY_POLICY_COUNT); + + if (result != KERN_SUCCESS) { + MACH_LOG(ERROR, result) << "task_policy_set TASK_CATEGORY_POLICY"; + return false; + } else if (!mac::IsOSMavericksOrLater()) { + return true; + } + + // Latency QoS regulates timer throttling/accuracy. Select default tier + // on foreground because precise timer firing isn't needed. + struct task_qos_policy qos_policy = { + background ? LATENCY_QOS_TIER_5 : LATENCY_QOS_TIER_UNSPECIFIED, + background ? THROUGHPUT_QOS_TIER_5 : THROUGHPUT_QOS_TIER_UNSPECIFIED + }; + result = task_policy_set(task_port, TASK_OVERRIDE_QOS_POLICY, + reinterpret_cast<task_policy_t>(&qos_policy), + TASK_QOS_POLICY_COUNT); + if (result != KERN_SUCCESS) { + MACH_LOG(ERROR, result) << "task_policy_set TASK_OVERRIDE_QOS_POLICY"; + return false; + } + + return true; +} + +} // namespace base diff --git a/base/process/process_posix.cc b/base/process/process_posix.cc index bb987d0..47b0d5b 100644 --- a/base/process/process_posix.cc +++ b/base/process/process_posix.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright 2011 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. @@ -255,12 +255,12 @@ Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) { return Process(handle); } -#if !defined(OS_LINUX) +#if !defined(OS_LINUX) && !defined(OS_MACOSX) // static bool Process::CanBackgroundProcesses() { return false; } -#endif // !defined(OS_LINUX) +#endif // !defined(OS_LINUX) && !defined(OS_MACOSX) bool Process::IsValid() const { return process_ != kNullProcessHandle; @@ -355,7 +355,7 @@ bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) { return WaitForExitWithTimeoutImpl(Handle(), exit_code, timeout); } -#if !defined(OS_LINUX) +#if !defined(OS_LINUX) && !defined(OS_MACOSX) bool Process::IsProcessBackgrounded() const { // See SetProcessBackgrounded(). DCHECK(IsValid()); @@ -363,13 +363,13 @@ bool Process::IsProcessBackgrounded() const { } bool Process::SetProcessBackgrounded(bool value) { - // POSIX only allows lowering the priority of a process, so if we - // were to lower it we wouldn't be able to raise it back to its initial - // priority. - DCHECK(IsValid()); + // Not implemented for POSIX systems other than Mac and Linux. With POSIX, if + // we were to lower the process priority we wouldn't be able to raise it back + // to its initial priority. + NOTIMPLEMENTED(); return false; } -#endif // !defined(OS_LINUX) +#endif // !defined(OS_LINUX) && !defined(OS_MACOSX) int Process::GetPriority() const { DCHECK(IsValid()); diff --git a/base/process/process_unittest.cc b/base/process/process_unittest.cc index ba8e4e6..e094c03 100644 --- a/base/process/process_unittest.cc +++ b/base/process/process_unittest.cc @@ -11,6 +11,10 @@ #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" +#if defined(OS_MACOSX) +#include <mach/mach.h> +#endif // OS_MACOSX + namespace { #if defined(OS_WIN) @@ -170,7 +174,19 @@ TEST_F(ProcessTest, WaitForExitWithTimeout) { TEST_F(ProcessTest, SetProcessBackgrounded) { Process process(SpawnChild("SimpleChildProcess")); int old_priority = process.GetPriority(); -#if defined(OS_WIN) +#if defined(OS_MACOSX) + // On the Mac, backgrounding a process requires a port to that process. + // In the browser it's available through the MachBroker class, which is not + // part of base. Additionally, there is an indefinite amount of time between + // spawning a process and receiving its port. Because this test just checks + // the ability to background/foreground a process, we can use the current + // process's port instead. + mach_port_t process_port = mach_task_self(); + EXPECT_TRUE(process.SetProcessBackgrounded(process_port, true)); + EXPECT_TRUE(process.IsProcessBackgrounded(process_port)); + EXPECT_TRUE(process.SetProcessBackgrounded(process_port, false)); + EXPECT_FALSE(process.IsProcessBackgrounded(process_port)); +#elif defined(OS_WIN) EXPECT_TRUE(process.SetProcessBackgrounded(true)); EXPECT_TRUE(process.IsProcessBackgrounded()); EXPECT_TRUE(process.SetProcessBackgrounded(false)); @@ -188,7 +204,13 @@ TEST_F(ProcessTest, SetProcessBackgrounded) { TEST_F(ProcessTest, SetProcessBackgroundedSelf) { Process process = Process::Current(); int old_priority = process.GetPriority(); -#if defined(OS_WIN) +#if defined(OS_MACOSX) + mach_port_t process_port = mach_task_self(); + EXPECT_TRUE(process.SetProcessBackgrounded(process_port, true)); + EXPECT_TRUE(process.IsProcessBackgrounded(process_port)); + EXPECT_TRUE(process.SetProcessBackgrounded(process_port, false)); + EXPECT_FALSE(process.IsProcessBackgrounded(process_port)); +#elif defined(OS_WIN) EXPECT_TRUE(process.SetProcessBackgrounded(true)); EXPECT_TRUE(process.IsProcessBackgrounded()); EXPECT_TRUE(process.SetProcessBackgrounded(false)); diff --git a/chrome/browser/extensions/extension_apitest.cc b/chrome/browser/extensions/extension_apitest.cc index c2e3139..c370197 100644 --- a/chrome/browser/extensions/extension_apitest.cc +++ b/chrome/browser/extensions/extension_apitest.cc @@ -4,6 +4,7 @@ #include "chrome/browser/extensions/extension_apitest.h" +#include "base/base_switches.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -14,6 +15,7 @@ #include "chrome/browser/ui/extensions/app_launch_params.h" #include "chrome/browser/ui/extensions/application_launch.h" #include "chrome/test/base/ui_test_utils.h" +#include "content/public/common/content_switches.h" #include "extensions/browser/api/test/test_api.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" @@ -421,4 +423,8 @@ bool ExtensionApiTest::StartSpawnedTestServer() { void ExtensionApiTest::SetUpCommandLine(base::CommandLine* command_line) { ExtensionBrowserTest::SetUpCommandLine(command_line); test_data_dir_ = test_data_dir_.AppendASCII("api_test"); + // Backgrounded renderer processes run at a lower priority, causing the + // tests to take more time to complete. Disable backgrounding so that the + // tests don't time out. + command_line->AppendSwitch(switches::kDisableRendererBackgrounding); } diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc index 28be72b..23cf832 100644 --- a/content/browser/child_process_launcher.cc +++ b/content/browser/child_process_launcher.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 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. @@ -269,7 +269,15 @@ void TerminateOnLauncherThread(bool zygote, base::Process process) { void SetProcessBackgroundedOnLauncherThread(base::Process process, bool background) { DCHECK_CURRENTLY_ON(BrowserThread::PROCESS_LAUNCHER); +#if defined(OS_MACOSX) + MachBroker* broker = MachBroker::GetInstance(); + mach_port_t task_port = broker->TaskForPid(process.Pid()); + if (task_port != TASK_NULL) { + process.SetProcessBackgrounded(task_port, background); + } +#else process.SetProcessBackgrounded(background); +#endif // defined(OS_MACOSX) #if defined(OS_ANDROID) SetChildProcessInForeground(process.Handle(), !background); #endif diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 8f90764..11812f8 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2012 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. @@ -2218,6 +2218,11 @@ void RenderProcessHostImpl::SetBackgrounded(bool backgrounded) { if (backgrounded_ && audio_renderer_host_->HasActiveAudio()) return; + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kDisableRendererBackgrounding)) + return; + #if defined(OS_WIN) // The cbstext.dll loads as a global GetMessage hook in the browser process // and intercepts/unintercepts the kernel32 API SetPriorityClass in a @@ -2229,14 +2234,15 @@ void RenderProcessHostImpl::SetBackgrounded(bool backgrounded) { return; #endif // OS_WIN -#if defined(OS_WIN) - // Same as below, but bound to an experiment (http://crbug.com/458594) - // initially on Windows. Enabled by default in the asbence of field trials to - // get coverage on the perf waterfall. +#if defined(OS_WIN) || defined(OS_MACOSX) + // Same as below, but bound to an experiment (http://crbug.com/458594 on + // Windows, http://crbug.com/398103 on the Mac). Enabled by default in the + // absence of field trials to get coverage on the perf waterfall. base::FieldTrial* trial = base::FieldTrialList::Find("BackgroundRendererProcesses"); - if (!trial || trial->group_name() != "Disallow") + if (!trial || !StartsWithASCII(trial->group_name(), "Disallow", true)) { child_process_launcher_->SetProcessBackgrounded(backgrounded); + } #else // Control the background state from the browser process, otherwise the task // telling the renderer to "unbackground" itself may be preempted by other diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index 04fd6e8b..f7c94dd 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -226,6 +226,9 @@ const char kDisableRemoteFonts[] = "disable-remote-fonts"; // Turns off the accessibility in the renderer. const char kDisableRendererAccessibility[] = "disable-renderer-accessibility"; +// Prevent renderer process backgrounding when set. +const char kDisableRendererBackgrounding[] = "disable-renderer-backgrounding"; + // Disable the seccomp filter sandbox (seccomp-bpf) (Linux only). const char kDisableSeccompFilterSandbox[] = "disable-seccomp-filter-sandbox"; diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index c036949..ce84514 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -76,6 +76,7 @@ CONTENT_EXPORT extern const char kDisablePluginsDiscovery[]; CONTENT_EXPORT extern const char kDisableReadingFromCanvas[]; extern const char kDisableRemoteFonts[]; extern const char kDisableRendererAccessibility[]; +CONTENT_EXPORT extern const char kDisableRendererBackgrounding[]; CONTENT_EXPORT extern const char kDisableSeccompFilterSandbox[]; CONTENT_EXPORT extern const char kDisableSetuidSandbox[]; CONTENT_EXPORT extern const char kDisableSharedWorkers[]; |