OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "base/process/process.h" | |
6 | |
7 #include "base/logging.h" | |
8 #include "base/memory/scoped_ptr.h" | |
9 #include "base/metrics/field_trial.h" | |
10 #include "base/numerics/safe_conversions.h" | |
11 #include "base/process/kill.h" | |
12 #include "base/win/windows_version.h" | |
13 | |
14 namespace { | |
15 | |
16 DWORD kBasicProcessAccess = | |
17 PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE; | |
18 | |
19 } // namespace | |
20 | |
21 namespace base { | |
22 | |
23 Process::Process(ProcessHandle handle) | |
24 : is_current_process_(false), | |
25 process_(handle) { | |
26 CHECK_NE(handle, ::GetCurrentProcess()); | |
27 } | |
28 | |
29 Process::Process(RValue other) | |
30 : is_current_process_(other.object->is_current_process_), | |
31 process_(other.object->process_.Take()) { | |
32 other.object->Close(); | |
33 } | |
34 | |
35 Process::~Process() { | |
36 } | |
37 | |
38 Process& Process::operator=(RValue other) { | |
39 if (this != other.object) { | |
40 process_.Set(other.object->process_.Take()); | |
41 is_current_process_ = other.object->is_current_process_; | |
42 other.object->Close(); | |
43 } | |
44 return *this; | |
45 } | |
46 | |
47 // static | |
48 Process Process::Current() { | |
49 Process process; | |
50 process.is_current_process_ = true; | |
51 return process.Pass(); | |
52 } | |
53 | |
54 // static | |
55 Process Process::Open(ProcessId pid) { | |
56 return Process(::OpenProcess(kBasicProcessAccess, FALSE, pid)); | |
57 } | |
58 | |
59 // static | |
60 Process Process::OpenWithExtraPrivileges(ProcessId pid) { | |
61 DWORD access = kBasicProcessAccess | PROCESS_DUP_HANDLE | PROCESS_VM_READ; | |
62 return Process(::OpenProcess(access, FALSE, pid)); | |
63 } | |
64 | |
65 // static | |
66 Process Process::OpenWithAccess(ProcessId pid, DWORD desired_access) { | |
67 return Process(::OpenProcess(desired_access, FALSE, pid)); | |
68 } | |
69 | |
70 // static | |
71 Process Process::DeprecatedGetProcessFromHandle(ProcessHandle handle) { | |
72 DCHECK_NE(handle, ::GetCurrentProcess()); | |
73 ProcessHandle out_handle; | |
74 if (!::DuplicateHandle(GetCurrentProcess(), handle, | |
75 GetCurrentProcess(), &out_handle, | |
76 0, FALSE, DUPLICATE_SAME_ACCESS)) { | |
77 return Process(); | |
78 } | |
79 return Process(out_handle); | |
80 } | |
81 | |
82 // static | |
83 bool Process::CanBackgroundProcesses() { | |
84 return true; | |
85 } | |
86 | |
87 bool Process::IsValid() const { | |
88 return process_.IsValid() || is_current(); | |
89 } | |
90 | |
91 ProcessHandle Process::Handle() const { | |
92 return is_current_process_ ? GetCurrentProcess() : process_.Get(); | |
93 } | |
94 | |
95 Process Process::Duplicate() const { | |
96 if (is_current()) | |
97 return Current(); | |
98 | |
99 ProcessHandle out_handle; | |
100 if (!IsValid() || !::DuplicateHandle(GetCurrentProcess(), | |
101 Handle(), | |
102 GetCurrentProcess(), | |
103 &out_handle, | |
104 0, | |
105 FALSE, | |
106 DUPLICATE_SAME_ACCESS)) { | |
107 return Process(); | |
108 } | |
109 return Process(out_handle); | |
110 } | |
111 | |
112 ProcessId Process::Pid() const { | |
113 DCHECK(IsValid()); | |
114 return GetProcId(Handle()); | |
115 } | |
116 | |
117 bool Process::is_current() const { | |
118 return is_current_process_; | |
119 } | |
120 | |
121 void Process::Close() { | |
122 is_current_process_ = false; | |
123 if (!process_.IsValid()) | |
124 return; | |
125 | |
126 process_.Close(); | |
127 } | |
128 | |
129 bool Process::Terminate(int exit_code, bool wait) const { | |
130 DCHECK(IsValid()); | |
131 bool result = (::TerminateProcess(Handle(), exit_code) != FALSE); | |
132 if (result && wait) { | |
133 // The process may not end immediately due to pending I/O | |
134 if (::WaitForSingleObject(Handle(), 60 * 1000) != WAIT_OBJECT_0) | |
135 DPLOG(ERROR) << "Error waiting for process exit"; | |
136 } else if (!result) { | |
137 DPLOG(ERROR) << "Unable to terminate process"; | |
138 } | |
139 return result; | |
140 } | |
141 | |
142 bool Process::WaitForExit(int* exit_code) { | |
143 return WaitForExitWithTimeout(TimeDelta::FromMilliseconds(INFINITE), | |
144 exit_code); | |
145 } | |
146 | |
147 bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) { | |
148 // Limit timeout to INFINITE. | |
149 DWORD timeout_ms = saturated_cast<DWORD>(timeout.InMilliseconds()); | |
150 if (::WaitForSingleObject(Handle(), timeout_ms) != WAIT_OBJECT_0) | |
151 return false; | |
152 | |
153 DWORD temp_code; // Don't clobber out-parameters in case of failure. | |
154 if (!::GetExitCodeProcess(Handle(), &temp_code)) | |
155 return false; | |
156 | |
157 if (exit_code) | |
158 *exit_code = temp_code; | |
159 return true; | |
160 } | |
161 | |
162 bool Process::IsProcessBackgrounded() const { | |
163 DCHECK(IsValid()); | |
164 DWORD priority = GetPriority(); | |
165 if (priority == 0) | |
166 return false; // Failure case. | |
167 return ((priority == BELOW_NORMAL_PRIORITY_CLASS) || | |
168 (priority == IDLE_PRIORITY_CLASS)); | |
169 } | |
170 | |
171 bool Process::SetProcessBackgrounded(bool value) { | |
172 DCHECK(IsValid()); | |
173 // Vista and above introduce a real background mode, which not only | |
174 // sets the priority class on the threads but also on the IO generated | |
175 // by it. Unfortunately it can only be set for the calling process. | |
176 DWORD priority; | |
177 if ((base::win::GetVersion() >= base::win::VERSION_VISTA) && (is_current())) { | |
178 priority = value ? PROCESS_MODE_BACKGROUND_BEGIN : | |
179 PROCESS_MODE_BACKGROUND_END; | |
180 } else { | |
181 // Experiment (http://crbug.com/458594) with using IDLE_PRIORITY_CLASS as a | |
182 // background priority for background renderers (this code path is | |
183 // technically for more than just the renderers but they're the only use | |
184 // case in practice and experimenting here direclty is thus easier -- plus | |
185 // it doesn't really hurt as above we already state our intent of using | |
186 // PROCESS_MODE_BACKGROUND_BEGIN if available which is essentially | |
187 // IDLE_PRIORITY_CLASS plus lowered IO priority). Enabled by default in the | |
188 // asbence of field trials to get coverage on the perf waterfall. | |
189 DWORD background_priority = IDLE_PRIORITY_CLASS; | |
190 base::FieldTrial* trial = | |
191 base::FieldTrialList::Find("BackgroundRendererProcesses"); | |
192 if (trial && trial->group_name() == "AllowBelowNormalFromBrowser") | |
193 background_priority = BELOW_NORMAL_PRIORITY_CLASS; | |
194 | |
195 priority = value ? background_priority : NORMAL_PRIORITY_CLASS; | |
196 } | |
197 | |
198 return (::SetPriorityClass(Handle(), priority) != 0); | |
199 } | |
200 | |
201 int Process::GetPriority() const { | |
202 DCHECK(IsValid()); | |
203 return ::GetPriorityClass(Handle()); | |
204 } | |
205 | |
206 } // namespace base | |
OLD | NEW |