summaryrefslogtreecommitdiffstats
path: root/base/process_linux.cc
blob: dfdc20e103f93325c786a09c60406e57a4e46c3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// 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