92 lines
3.9 KiB
C++
92 lines
3.9 KiB
C++
|
|
// Copyright 2018 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "base/threading/platform_thread_win.h"
|
||
|
|
|
||
|
|
#include <windows.h>
|
||
|
|
|
||
|
|
#include <array>
|
||
|
|
|
||
|
|
#include "base/process/process.h"
|
||
|
|
#include "base/win/windows_version.h"
|
||
|
|
#include "testing/gmock/include/gmock/gmock.h"
|
||
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
||
|
|
|
||
|
|
namespace base {
|
||
|
|
|
||
|
|
// It has been observed that calling
|
||
|
|
// :SetThreadPriority(THREAD_MODE_BACKGROUND_BEGIN) in an IDLE_PRIORITY_CLASS
|
||
|
|
// process never affects the return value of ::GetThreadPriority() or
|
||
|
|
// the base priority reported in Process Explorer. It does however
|
||
|
|
// set the memory and I/O priorities to very low. This test confirms that
|
||
|
|
// behavior which we suspect is a Windows kernel bug. If this test starts
|
||
|
|
// failing, the mitigation for https://crbug.com/901483 in
|
||
|
|
// PlatformThread::SetCurrentThreadType() should be revisited.
|
||
|
|
TEST(PlatformThreadWinTest, SetBackgroundThreadModeFailsInIdlePriorityProcess) {
|
||
|
|
PlatformThreadHandle::Handle thread_handle =
|
||
|
|
PlatformThread::CurrentHandle().platform_handle();
|
||
|
|
|
||
|
|
// ::GetThreadPriority() is NORMAL. Memory priority is NORMAL.
|
||
|
|
// Note: There is no practical way to verify the I/O priority.
|
||
|
|
EXPECT_EQ(::GetThreadPriority(thread_handle), THREAD_PRIORITY_NORMAL);
|
||
|
|
internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
|
||
|
|
|
||
|
|
// Set the process priority to IDLE.
|
||
|
|
// Note: Do not use Process::SetProcessBackgrounded() because it uses
|
||
|
|
// PROCESS_MODE_BACKGROUND_BEGIN instead of IDLE_PRIORITY_CLASS when
|
||
|
|
// the target is the current process.
|
||
|
|
EXPECT_EQ(::GetPriorityClass(Process::Current().Handle()),
|
||
|
|
static_cast<DWORD>(NORMAL_PRIORITY_CLASS));
|
||
|
|
::SetPriorityClass(Process::Current().Handle(), IDLE_PRIORITY_CLASS);
|
||
|
|
EXPECT_EQ(Process::Current().GetPriority(),
|
||
|
|
static_cast<int>(IDLE_PRIORITY_CLASS));
|
||
|
|
|
||
|
|
// GetThreadPriority() stays NORMAL. Memory priority stays NORMAL.
|
||
|
|
EXPECT_EQ(::GetThreadPriority(thread_handle), THREAD_PRIORITY_NORMAL);
|
||
|
|
internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
|
||
|
|
|
||
|
|
// Begin thread mode background.
|
||
|
|
EXPECT_TRUE(::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_BEGIN));
|
||
|
|
|
||
|
|
// On Win10+, GetThreadPriority() stays NORMAL and memory priority becomes
|
||
|
|
// VERY_LOW.
|
||
|
|
//
|
||
|
|
// Note: this documents the aforementioned kernel bug. Ideally this would
|
||
|
|
// *not* be the case.
|
||
|
|
const int priority_after_thread_mode_background_begin =
|
||
|
|
::GetThreadPriority(thread_handle);
|
||
|
|
EXPECT_EQ(priority_after_thread_mode_background_begin,
|
||
|
|
THREAD_PRIORITY_NORMAL);
|
||
|
|
internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
|
||
|
|
|
||
|
|
PlatformThread::Sleep(base::Seconds(1));
|
||
|
|
|
||
|
|
// After 1 second, GetThreadPriority() and memory priority don't change (this
|
||
|
|
// refutes the hypothesis that it simply takes time before GetThreadPriority()
|
||
|
|
// is updated after entering thread mode background).
|
||
|
|
EXPECT_EQ(::GetThreadPriority(thread_handle),
|
||
|
|
priority_after_thread_mode_background_begin);
|
||
|
|
internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
|
||
|
|
|
||
|
|
// Set the process priority to NORMAL.
|
||
|
|
::SetPriorityClass(Process::Current().Handle(), NORMAL_PRIORITY_CLASS);
|
||
|
|
|
||
|
|
// GetThreadPriority() and memory priority don't change when the process
|
||
|
|
// priority changes.
|
||
|
|
EXPECT_EQ(::GetThreadPriority(thread_handle),
|
||
|
|
priority_after_thread_mode_background_begin);
|
||
|
|
internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
|
||
|
|
|
||
|
|
// End thread mode background.
|
||
|
|
//
|
||
|
|
// Note: at least "ending" the semi-enforced background mode works...
|
||
|
|
EXPECT_TRUE(::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_END));
|
||
|
|
|
||
|
|
// GetThreadPriority() stays/becomes NORMAL. Memory priority becomes NORMAL.
|
||
|
|
EXPECT_EQ(::GetThreadPriority(thread_handle), THREAD_PRIORITY_NORMAL);
|
||
|
|
internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace base
|