// Copyright (c) 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. #include "base/process.h" #include <errno.h> #include <sys/resource.h> #include "base/file_util.h" #include "base/logging.h" #include "base/stringprintf.h" #if defined(OS_CHROMEOS) static bool use_cgroups = false; static bool cgroups_inited = false; static const char kForegroundTasks[] = "/tmp/cgroup/cpu/chrome_renderers/foreground/tasks"; static const char kBackgroundTasks[] = "/tmp/cgroup/cpu/chrome_renderers/background/tasks"; static FilePath foreground_tasks; static FilePath background_tasks; #endif namespace base { #if defined(OS_CHROMEOS) // We are more aggressive in our lowering of background process priority // for chromeos as we have much more control over other processes running // on the machine. const int kPriorityAdjustment = 19; #else const int kPriorityAdjustment = 5; #endif bool Process::IsProcessBackgrounded() const { DCHECK(process_); return saved_priority_ == kUnsetProcessPriority; } bool Process::SetProcessBackgrounded(bool background) { DCHECK(process_); #if defined(OS_CHROMEOS) // Check for cgroups files. ChromeOS supports these by default. It creates // a cgroup mount in /tmp/cgroup and then configures two cpu task groups, // one contains at most a single foreground renderer and the other contains // all background renderers. This allows us to limit the impact of background // renderers on foreground ones to a greater level than simple renicing. if (!cgroups_inited) { cgroups_inited = true; foreground_tasks = FilePath(kForegroundTasks); background_tasks = FilePath(kBackgroundTasks); file_util::FileSystemType foreground_type; file_util::FileSystemType background_type; use_cgroups = file_util::GetFileSystemType(foreground_tasks, &foreground_type) && file_util::GetFileSystemType(background_tasks, &background_type) && foreground_type == file_util::FILE_SYSTEM_CGROUP && background_type == file_util::FILE_SYSTEM_CGROUP; } if (use_cgroups) { if (background) { std::string pid = StringPrintf("%d", process_); if (file_util::WriteFile(background_tasks, pid.c_str(), pid.size()) > 0) { // With cgroups there's no real notion of priority as an int, but this // will ensure we only move renderers back to the foreground group // if we've ever put them in the background one. saved_priority_ = 0; return true; } else { return false; } } else { if (saved_priority_ == kUnsetProcessPriority) { // Can't restore if we were never backgrounded. return false; } std::string pid = StringPrintf("%d", process_); if (file_util::WriteFile(foreground_tasks, pid.c_str(), pid.size()) > 0) { saved_priority_ = kUnsetProcessPriority; return true; } else { return false; } } } #endif // OS_CHROMEOS if (background) { // We won't be able to raise the priority if we don't have the right rlimit. // The limit may be adjusted in /etc/security/limits.conf for PAM systems. struct rlimit rlim; if (getrlimit(RLIMIT_NICE, &rlim) != 0) { // Call to getrlimit failed, don't background. return false; } errno = 0; int current_priority = GetPriority(); if (errno) { // Couldn't get priority. return false; } // {set,get}priority values are in the range -20 to 19, where -1 is higher // priority than 0. But rlimit's are in the range from 0 to 39 where // 1 is higher than 0. if ((20 - current_priority) > static_cast<int>(rlim.rlim_cur)) { // User is not allowed to raise the priority back to where it is now. return false; } int result = setpriority( PRIO_PROCESS, process_, current_priority + kPriorityAdjustment); if (result == -1) { LOG(ERROR) << "Failed to lower priority, errno: " << errno; return false; } saved_priority_ = current_priority; return true; } else { if (saved_priority_ == kUnsetProcessPriority) { // Can't restore if we were never backgrounded. return false; } int result = setpriority(PRIO_PROCESS, process_, saved_priority_); // If we can't restore something has gone terribly wrong. DPCHECK(result == 0); saved_priority_ = kUnsetProcessPriority; return true; } } } // namespace base