Avi Drissman | e4622aa | 2022-09-08 20:36:06 | [diff] [blame] | 1 | // Copyright 2011 The Chromium Authors |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 4 | |
[email protected] | 76bea67 | 2013-07-19 16:48:56 | [diff] [blame] | 5 | #include "base/process/process.h" |
[email protected] | dd4b5126 | 2013-07-25 21:38:23 | [diff] [blame] | 6 | |
Sebastien Marchand | bd02bc29e | 2020-03-11 15:53:36 | [diff] [blame] | 7 | #include "base/clang_profiling_buildflags.h" |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 8 | #include "base/logging.h" |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 9 | #include "base/numerics/safe_conversions.h" |
rvargas | 126fd582 | 2014-12-12 00:25:14 | [diff] [blame] | 10 | #include "base/process/kill.h" |
Francois Doray | a678fc1 | 2017-10-30 22:18:06 | [diff] [blame] | 11 | #include "base/threading/thread_restrictions.h" |
Gabriel Charette | 6836c0d5 | 2021-01-11 17:40:26 | [diff] [blame] | 12 | #include "base/trace_event/base_tracing.h" |
Zheda Chen | 2808be7 | 2022-12-19 06:26:55 | [diff] [blame] | 13 | #include "base/win/windows_version.h" |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 14 | |
Bruce Dawson | bfdc3fd | 2018-01-03 20:32:36 | [diff] [blame] | 15 | #include <windows.h> |
| 16 | |
Sebastien Marchand | bd02bc29e | 2020-03-11 15:53:36 | [diff] [blame] | 17 | #if BUILDFLAG(CLANG_PROFILING) |
| 18 | #include "base/test/clang_profiling.h" |
Yannic Bonenberger | 6801e6c | 2019-06-07 10:42:53 | [diff] [blame] | 19 | #endif |
| 20 | |
rvargas | 747ff24 | 2015-01-17 02:46:47 | [diff] [blame] | 21 | namespace { |
| 22 | |
| 23 | DWORD kBasicProcessAccess = |
| 24 | PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE; |
| 25 | |
| 26 | } // namespace |
| 27 | |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 28 | namespace base { |
| 29 | |
Zheda Chen | 2808be7 | 2022-12-19 06:26:55 | [diff] [blame] | 30 | // Sets Eco QoS (Quality of Service) level for background process which would |
| 31 | // select efficient CPU frequency and schedule the process to efficient cores |
| 32 | // (available on hybrid CPUs). |
| 33 | // QoS is a scheduling Win API which indicates the desired performance and power |
| 34 | // efficiency of a process/thread. EcoQoS is introduced since Windows 11. |
| 35 | BASE_FEATURE(kUseEcoQoSForBackgroundProcess, |
| 36 | "UseEcoQoSForBackgroundProcess", |
François Doray | 5769436 | 2023-05-09 14:31:53 | [diff] [blame] | 37 | FEATURE_ENABLED_BY_DEFAULT); |
Zheda Chen | 2808be7 | 2022-12-19 06:26:55 | [diff] [blame] | 38 | |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 39 | Process::Process(ProcessHandle handle) |
scottmg | 297cc93 | 2017-05-24 03:45:58 | [diff] [blame] | 40 | : process_(handle), is_current_process_(false) { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 41 | CHECK_NE(handle, ::GetCurrentProcess()); |
| 42 | } |
| 43 | |
dcheng | e1b0277c | 2015-12-01 12:09:52 | [diff] [blame] | 44 | Process::Process(Process&& other) |
Lei Zhang | 04c0bcb | 2022-02-04 04:13:45 | [diff] [blame] | 45 | : process_(other.process_.release()), |
scottmg | 297cc93 | 2017-05-24 03:45:58 | [diff] [blame] | 46 | is_current_process_(other.is_current_process_) { |
dcheng | e1b0277c | 2015-12-01 12:09:52 | [diff] [blame] | 47 | other.Close(); |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 48 | } |
| 49 | |
thakis | 3096dac | 2015-04-20 16:44:48 | [diff] [blame] | 50 | Process::~Process() { |
| 51 | } |
| 52 | |
dcheng | e1b0277c | 2015-12-01 12:09:52 | [diff] [blame] | 53 | Process& Process::operator=(Process&& other) { |
| 54 | DCHECK_NE(this, &other); |
Lei Zhang | 04c0bcb | 2022-02-04 04:13:45 | [diff] [blame] | 55 | process_.Set(other.process_.release()); |
dcheng | e1b0277c | 2015-12-01 12:09:52 | [diff] [blame] | 56 | is_current_process_ = other.is_current_process_; |
| 57 | other.Close(); |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 58 | return *this; |
| 59 | } |
| 60 | |
| 61 | // static |
| 62 | Process Process::Current() { |
| 63 | Process process; |
| 64 | process.is_current_process_ = true; |
dcheng | 70c4942 | 2016-03-02 23:20:34 | [diff] [blame] | 65 | return process; |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 66 | } |
| 67 | |
| 68 | // static |
rvargas | 6b039c37 | 2015-02-04 21:11:29 | [diff] [blame] | 69 | Process Process::Open(ProcessId pid) { |
| 70 | return Process(::OpenProcess(kBasicProcessAccess, FALSE, pid)); |
| 71 | } |
| 72 | |
| 73 | // static |
rvargas | 1c376a8 | 2015-03-16 23:03:52 | [diff] [blame] | 74 | Process Process::OpenWithExtraPrivileges(ProcessId pid) { |
rvargas | 747ff24 | 2015-01-17 02:46:47 | [diff] [blame] | 75 | DWORD access = kBasicProcessAccess | PROCESS_DUP_HANDLE | PROCESS_VM_READ; |
rvargas | 6b039c37 | 2015-02-04 21:11:29 | [diff] [blame] | 76 | return Process(::OpenProcess(access, FALSE, pid)); |
rvargas | 747ff24 | 2015-01-17 02:46:47 | [diff] [blame] | 77 | } |
| 78 | |
| 79 | // static |
rvargas | 17a407d | 2015-01-23 20:36:44 | [diff] [blame] | 80 | Process Process::OpenWithAccess(ProcessId pid, DWORD desired_access) { |
| 81 | return Process(::OpenProcess(desired_access, FALSE, pid)); |
| 82 | } |
| 83 | |
| 84 | // static |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 85 | bool Process::CanBackgroundProcesses() { |
| 86 | return true; |
| 87 | } |
| 88 | |
haraken | 940efb9 | 2017-02-08 05:58:15 | [diff] [blame] | 89 | // static |
| 90 | void Process::TerminateCurrentProcessImmediately(int exit_code) { |
Sebastien Marchand | bd02bc29e | 2020-03-11 15:53:36 | [diff] [blame] | 91 | #if BUILDFLAG(CLANG_PROFILING) |
| 92 | WriteClangProfilingProfile(); |
Wez | d2031d30 | 2018-08-16 23:34:25 | [diff] [blame] | 93 | #endif |
Peter Kasting | 4d3664b | 2022-06-16 19:27:54 | [diff] [blame] | 94 | ::TerminateProcess(GetCurrentProcess(), static_cast<UINT>(exit_code)); |
Bruce Dawson | 33ce2ff | 2017-11-28 20:47:51 | [diff] [blame] | 95 | // There is some ambiguity over whether the call above can return. Rather than |
| 96 | // hitting confusing crashes later on we should crash right here. |
Peter Boström | 25c6ec7 | 2022-11-02 23:25:19 | [diff] [blame] | 97 | ImmediateCrash(); |
haraken | 940efb9 | 2017-02-08 05:58:15 | [diff] [blame] | 98 | } |
| 99 | |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 100 | bool Process::IsValid() const { |
Lei Zhang | 04c0bcb | 2022-02-04 04:13:45 | [diff] [blame] | 101 | return process_.is_valid() || is_current(); |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 102 | } |
| 103 | |
| 104 | ProcessHandle Process::Handle() const { |
Lei Zhang | 04c0bcb | 2022-02-04 04:13:45 | [diff] [blame] | 105 | return is_current_process_ ? GetCurrentProcess() : process_.get(); |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 106 | } |
| 107 | |
| 108 | Process Process::Duplicate() const { |
| 109 | if (is_current()) |
| 110 | return Current(); |
| 111 | |
| 112 | ProcessHandle out_handle; |
| 113 | if (!IsValid() || !::DuplicateHandle(GetCurrentProcess(), |
| 114 | Handle(), |
| 115 | GetCurrentProcess(), |
| 116 | &out_handle, |
| 117 | 0, |
| 118 | FALSE, |
| 119 | DUPLICATE_SAME_ACCESS)) { |
| 120 | return Process(); |
| 121 | } |
| 122 | return Process(out_handle); |
| 123 | } |
| 124 | |
Robert Sesek | 0e7f165a | 2020-11-20 21:31:45 | [diff] [blame] | 125 | ProcessHandle Process::Release() { |
| 126 | if (is_current()) |
| 127 | return ::GetCurrentProcess(); |
Lei Zhang | 04c0bcb | 2022-02-04 04:13:45 | [diff] [blame] | 128 | return process_.release(); |
Robert Sesek | 0e7f165a | 2020-11-20 21:31:45 | [diff] [blame] | 129 | } |
| 130 | |
rvargas | 960db88 | 2015-01-24 00:27:25 | [diff] [blame] | 131 | ProcessId Process::Pid() const { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 132 | DCHECK(IsValid()); |
| 133 | return GetProcId(Handle()); |
| 134 | } |
| 135 | |
Francois Doray | 7d131534 | 2018-10-21 03:54:47 | [diff] [blame] | 136 | Time Process::CreationTime() const { |
| 137 | FILETIME creation_time = {}; |
| 138 | FILETIME ignore1 = {}; |
| 139 | FILETIME ignore2 = {}; |
| 140 | FILETIME ignore3 = {}; |
| 141 | if (!::GetProcessTimes(Handle(), &creation_time, &ignore1, &ignore2, |
| 142 | &ignore3)) { |
| 143 | return Time(); |
| 144 | } |
| 145 | return Time::FromFileTime(creation_time); |
| 146 | } |
| 147 | |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 148 | bool Process::is_current() const { |
| 149 | return is_current_process_; |
| 150 | } |
| 151 | |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 152 | void Process::Close() { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 153 | is_current_process_ = false; |
Lei Zhang | 04c0bcb | 2022-02-04 04:13:45 | [diff] [blame] | 154 | if (!process_.is_valid()) |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 155 | return; |
[email protected] | b987e90e | 2011-08-15 19:22:44 | [diff] [blame] | 156 | |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 157 | process_.Close(); |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 158 | } |
| 159 | |
rvargas | 02ad783 | 2015-04-02 01:36:06 | [diff] [blame] | 160 | bool Process::Terminate(int exit_code, bool wait) const { |
Brian White | 6c8f3b9c | 2017-12-01 18:55:31 | [diff] [blame] | 161 | constexpr DWORD kWaitMs = 60 * 1000; |
| 162 | |
rvargas | eedb763e | 2015-03-09 23:53:45 | [diff] [blame] | 163 | DCHECK(IsValid()); |
Peter Kasting | 4d3664b | 2022-06-16 19:27:54 | [diff] [blame] | 164 | bool result = |
| 165 | ::TerminateProcess(Handle(), static_cast<UINT>(exit_code)) != FALSE; |
Brian White | ae2a8b9a | 2017-11-02 19:10:36 | [diff] [blame] | 166 | if (result) { |
rvargas | 02ad783 | 2015-04-02 01:36:06 | [diff] [blame] | 167 | // The process may not end immediately due to pending I/O |
Brian White | 6c8f3b9c | 2017-12-01 18:55:31 | [diff] [blame] | 168 | if (wait && ::WaitForSingleObject(Handle(), kWaitMs) != WAIT_OBJECT_0) |
rvargas | 02ad783 | 2015-04-02 01:36:06 | [diff] [blame] | 169 | DPLOG(ERROR) << "Error waiting for process exit"; |
Brian White | ae2a8b9a | 2017-11-02 19:10:36 | [diff] [blame] | 170 | Exited(exit_code); |
Brian White | ccb2c6e | 2017-11-14 19:09:44 | [diff] [blame] | 171 | } else { |
Bruce Dawson | d9111de | 2019-06-18 23:29:38 | [diff] [blame] | 172 | // The process can't be terminated, perhaps because it has already exited or |
| 173 | // is in the process of exiting. An error code of ERROR_ACCESS_DENIED is the |
| 174 | // undocumented-but-expected result if the process has already exited or |
| 175 | // started exiting when TerminateProcess is called, so don't print an error |
| 176 | // message in that case. |
| 177 | if (GetLastError() != ERROR_ACCESS_DENIED) |
| 178 | DPLOG(ERROR) << "Unable to terminate process"; |
| 179 | // A non-zero timeout is necessary here for the same reasons as above. |
Brian White | 6c8f3b9c | 2017-12-01 18:55:31 | [diff] [blame] | 180 | if (::WaitForSingleObject(Handle(), kWaitMs) == WAIT_OBJECT_0) { |
Brian White | ccb2c6e | 2017-11-14 19:09:44 | [diff] [blame] | 181 | DWORD actual_exit; |
Peter Kasting | 4d3664b | 2022-06-16 19:27:54 | [diff] [blame] | 182 | Exited(::GetExitCodeProcess(Handle(), &actual_exit) |
| 183 | ? static_cast<int>(actual_exit) |
| 184 | : exit_code); |
Brian White | ccb2c6e | 2017-11-14 19:09:44 | [diff] [blame] | 185 | result = true; |
| 186 | } |
rvargas | 02ad783 | 2015-04-02 01:36:06 | [diff] [blame] | 187 | } |
| 188 | return result; |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 189 | } |
| 190 | |
Mike Rorke | f36971b | 2020-03-06 17:56:29 | [diff] [blame] | 191 | Process::WaitExitStatus Process::WaitForExitOrEvent( |
| 192 | const base::win::ScopedHandle& stop_event_handle, |
| 193 | int* exit_code) const { |
Lei Zhang | 04c0bcb | 2022-02-04 04:13:45 | [diff] [blame] | 194 | HANDLE events[] = {Handle(), stop_event_handle.get()}; |
Mike Rorke | f36971b | 2020-03-06 17:56:29 | [diff] [blame] | 195 | DWORD wait_result = |
Daniel Cheng | f45f4760 | 2022-02-28 22:38:32 | [diff] [blame] | 196 | ::WaitForMultipleObjects(std::size(events), events, FALSE, INFINITE); |
Mike Rorke | f36971b | 2020-03-06 17:56:29 | [diff] [blame] | 197 | |
| 198 | if (wait_result == WAIT_OBJECT_0) { |
| 199 | DWORD temp_code; // Don't clobber out-parameters in case of failure. |
| 200 | if (!::GetExitCodeProcess(Handle(), &temp_code)) |
| 201 | return Process::WaitExitStatus::FAILED; |
| 202 | |
| 203 | if (exit_code) |
Peter Kasting | 4d3664b | 2022-06-16 19:27:54 | [diff] [blame] | 204 | *exit_code = static_cast<int>(temp_code); |
Mike Rorke | f36971b | 2020-03-06 17:56:29 | [diff] [blame] | 205 | |
Peter Kasting | 4d3664b | 2022-06-16 19:27:54 | [diff] [blame] | 206 | Exited(static_cast<int>(temp_code)); |
Mike Rorke | f36971b | 2020-03-06 17:56:29 | [diff] [blame] | 207 | return Process::WaitExitStatus::PROCESS_EXITED; |
| 208 | } |
| 209 | |
| 210 | if (wait_result == WAIT_OBJECT_0 + 1) { |
| 211 | return Process::WaitExitStatus::STOP_EVENT_SIGNALED; |
| 212 | } |
| 213 | |
| 214 | return Process::WaitExitStatus::FAILED; |
| 215 | } |
| 216 | |
jcivelli | f4462a35 | 2017-01-10 04:45:59 | [diff] [blame] | 217 | bool Process::WaitForExit(int* exit_code) const { |
Bruce Dawson | d3e5d6e | 2021-07-16 21:17:26 | [diff] [blame] | 218 | return WaitForExitWithTimeout(TimeDelta::Max(), exit_code); |
rvargas | 126fd582 | 2014-12-12 00:25:14 | [diff] [blame] | 219 | } |
| 220 | |
jcivelli | f4462a35 | 2017-01-10 04:45:59 | [diff] [blame] | 221 | bool Process::WaitForExitWithTimeout(TimeDelta timeout, int* exit_code) const { |
Gabriel Charette | 6836c0d5 | 2021-01-11 17:40:26 | [diff] [blame] | 222 | TRACE_EVENT0("base", "Process::WaitForExitWithTimeout"); |
Francois Doray | a678fc1 | 2017-10-30 22:18:06 | [diff] [blame] | 223 | |
Gabriel Charette | 6836c0d5 | 2021-01-11 17:40:26 | [diff] [blame] | 224 | if (!timeout.is_zero()) { |
Gabriel Charette | 6836c0d5 | 2021-01-11 17:40:26 | [diff] [blame] | 225 | // Assert that this thread is allowed to wait below. This intentionally |
| 226 | // doesn't use ScopedBlockingCallWithBaseSyncPrimitives because the process |
| 227 | // being waited upon tends to itself be using the CPU and considering this |
| 228 | // thread non-busy causes more issue than it fixes: http://crbug.com/905788 |
| 229 | internal::AssertBaseSyncPrimitivesAllowed(); |
| 230 | } |
bcwhite | d970596 | 2016-08-10 03:10:03 | [diff] [blame] | 231 | |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 232 | // Limit timeout to INFINITE. |
| 233 | DWORD timeout_ms = saturated_cast<DWORD>(timeout.InMilliseconds()); |
| 234 | if (::WaitForSingleObject(Handle(), timeout_ms) != WAIT_OBJECT_0) |
| 235 | return false; |
| 236 | |
| 237 | DWORD temp_code; // Don't clobber out-parameters in case of failure. |
| 238 | if (!::GetExitCodeProcess(Handle(), &temp_code)) |
| 239 | return false; |
| 240 | |
g.mehndiratt | fd19e23 | 2015-05-29 08:17:14 | [diff] [blame] | 241 | if (exit_code) |
Peter Kasting | 4d3664b | 2022-06-16 19:27:54 | [diff] [blame] | 242 | *exit_code = static_cast<int>(temp_code); |
bcwhite | 815054b5 | 2017-03-16 18:41:12 | [diff] [blame] | 243 | |
Peter Kasting | 4d3664b | 2022-06-16 19:27:54 | [diff] [blame] | 244 | Exited(static_cast<int>(temp_code)); |
rvargas | 8572897 | 2015-03-03 20:46:19 | [diff] [blame] | 245 | return true; |
rvargas | 126fd582 | 2014-12-12 00:25:14 | [diff] [blame] | 246 | } |
| 247 | |
Joe Mason | a0ba58b05 | 2023-02-22 20:46:29 | [diff] [blame] | 248 | void Process::Exited(int exit_code) const {} |
Brian White | ae2a8b9a | 2017-11-02 19:10:36 | [diff] [blame] | 249 | |
[email protected] | 2f15de4 | 2008-11-11 22:35:19 | [diff] [blame] | 250 | bool Process::IsProcessBackgrounded() const { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 251 | DCHECK(IsValid()); |
Patrick Monette | 740b81b | 2023-07-21 21:09:09 | [diff] [blame^] | 252 | int priority = GetOSPriority(); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 253 | if (priority == 0) |
| 254 | return false; // Failure case. |
[email protected] | 568bfb0 | 2011-04-28 23:51:53 | [diff] [blame] | 255 | return ((priority == BELOW_NORMAL_PRIORITY_CLASS) || |
| 256 | (priority == IDLE_PRIORITY_CLASS)); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 257 | } |
| 258 | |
| 259 | bool Process::SetProcessBackgrounded(bool value) { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 260 | DCHECK(IsValid()); |
Bruce Dawson | b7e516e | 2022-12-13 18:41:43 | [diff] [blame] | 261 | // Having a process remove itself from background mode is a potential |
| 262 | // priority inversion, and having a process put itself in background mode is |
| 263 | // broken in Windows 11 22H2. So, it is no longer supported. See |
| 264 | // https://crbug.com/1396155 for details. |
| 265 | DCHECK(!is_current()); |
| 266 | const DWORD priority = value ? IDLE_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS; |
[email protected] | 568bfb0 | 2011-04-28 23:51:53 | [diff] [blame] | 267 | |
Zheda Chen | 2808be7 | 2022-12-19 06:26:55 | [diff] [blame] | 268 | if (base::win::OSInfo::GetInstance()->version() >= |
| 269 | base::win::Version::WIN11 && |
| 270 | FeatureList::IsEnabled(kUseEcoQoSForBackgroundProcess)) { |
| 271 | PROCESS_POWER_THROTTLING_STATE power_throttling; |
| 272 | RtlZeroMemory(&power_throttling, sizeof(power_throttling)); |
| 273 | power_throttling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION; |
| 274 | |
| 275 | if (value) { |
| 276 | // Sets Eco QoS level. |
| 277 | power_throttling.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED; |
| 278 | power_throttling.StateMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED; |
| 279 | } else { |
| 280 | // Uses system default. |
| 281 | power_throttling.ControlMask = 0; |
| 282 | power_throttling.StateMask = 0; |
| 283 | } |
| 284 | bool ret = |
| 285 | ::SetProcessInformation(Handle(), ProcessPowerThrottling, |
| 286 | &power_throttling, sizeof(power_throttling)); |
| 287 | if (ret == 0) { |
| 288 | DPLOG(ERROR) << "Setting process QoS policy fails"; |
| 289 | } |
| 290 | } |
| 291 | |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 292 | return (::SetPriorityClass(Handle(), priority) != 0); |
[email protected] | 8a42080 | 2011-12-02 16:14:46 | [diff] [blame] | 293 | } |
| 294 | |
Patrick Monette | 740b81b | 2023-07-21 21:09:09 | [diff] [blame^] | 295 | int Process::GetOSPriority() const { |
rvargas | 079d184 | 2014-10-17 22:32:16 | [diff] [blame] | 296 | DCHECK(IsValid()); |
Peter Kasting | 4d3664b | 2022-06-16 19:27:54 | [diff] [blame] | 297 | return static_cast<int>(::GetPriorityClass(Handle())); |
[email protected] | 276aa6a | 2009-10-29 17:43:44 | [diff] [blame] | 298 | } |
| 299 | |
[email protected] | 176aa48 | 2008-11-14 03:25:15 | [diff] [blame] | 300 | } // namespace base |