1 // Copyright 2018 The Chromium Authors
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/threading/platform_thread_win.h"
6
7 #include <windows.h>
8
9 #include <array>
10
11 #include "base/process/process.h"
12 #include "base/test/scoped_feature_list.h"
13 #include "base/threading/platform_thread_win.h"
14 #include "base/threading/simple_thread.h"
15 #include "base/threading/threading_features.h"
16 #include "base/win/windows_version.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 namespace base {
21
22 // It has been observed that calling
23 // :SetThreadPriority(THREAD_MODE_BACKGROUND_BEGIN) in an IDLE_PRIORITY_CLASS
24 // process never affects the return value of ::GetThreadPriority() or
25 // the base priority reported in Process Explorer. It does however
26 // set the memory and I/O priorities to very low. This test confirms that
27 // behavior which we suspect is a Windows kernel bug. If this test starts
28 // failing, the mitigation for https://crbug.com/901483 in
29 // PlatformThread::SetCurrentThreadType() should be revisited.
TEST(PlatformThreadWinTest,SetBackgroundThreadModeFailsInIdlePriorityProcess)30 TEST(PlatformThreadWinTest, SetBackgroundThreadModeFailsInIdlePriorityProcess) {
31 PlatformThreadHandle::Handle thread_handle =
32 PlatformThread::CurrentHandle().platform_handle();
33
34 // ::GetThreadPriority() is NORMAL. Memory priority is NORMAL.
35 // Note: There is no practical way to verify the I/O priority.
36 EXPECT_EQ(::GetThreadPriority(thread_handle), THREAD_PRIORITY_NORMAL);
37 internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
38
39 // Set the process priority to IDLE.
40 // Note: Do not use Process::SetPriority() because it uses
41 // PROCESS_MODE_BACKGROUND_BEGIN instead of IDLE_PRIORITY_CLASS when
42 // the target is the current process.
43 EXPECT_EQ(::GetPriorityClass(Process::Current().Handle()),
44 static_cast<DWORD>(NORMAL_PRIORITY_CLASS));
45 ::SetPriorityClass(Process::Current().Handle(), IDLE_PRIORITY_CLASS);
46 EXPECT_EQ(Process::Current().GetOSPriority(),
47 static_cast<int>(IDLE_PRIORITY_CLASS));
48
49 // GetThreadPriority() stays NORMAL. Memory priority stays NORMAL.
50 EXPECT_EQ(::GetThreadPriority(thread_handle), THREAD_PRIORITY_NORMAL);
51 internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
52
53 // Begin thread mode background.
54 EXPECT_TRUE(::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_BEGIN));
55
56 // On Win10+, GetThreadPriority() stays NORMAL and memory priority becomes
57 // VERY_LOW.
58 //
59 // Note: this documents the aforementioned kernel bug. Ideally this would
60 // *not* be the case.
61 const int priority_after_thread_mode_background_begin =
62 ::GetThreadPriority(thread_handle);
63 EXPECT_EQ(priority_after_thread_mode_background_begin,
64 THREAD_PRIORITY_NORMAL);
65 internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
66
67 PlatformThread::Sleep(base::Seconds(1));
68
69 // After 1 second, GetThreadPriority() and memory priority don't change (this
70 // refutes the hypothesis that it simply takes time before GetThreadPriority()
71 // is updated after entering thread mode background).
72 EXPECT_EQ(::GetThreadPriority(thread_handle),
73 priority_after_thread_mode_background_begin);
74 internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
75
76 // Set the process priority to NORMAL.
77 ::SetPriorityClass(Process::Current().Handle(), NORMAL_PRIORITY_CLASS);
78
79 // GetThreadPriority() and memory priority don't change when the process
80 // priority changes.
81 EXPECT_EQ(::GetThreadPriority(thread_handle),
82 priority_after_thread_mode_background_begin);
83 internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_VERY_LOW);
84
85 // End thread mode background.
86 //
87 // Note: at least "ending" the semi-enforced background mode works...
88 EXPECT_TRUE(::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_END));
89
90 // GetThreadPriority() stays/becomes NORMAL. Memory priority becomes NORMAL.
91 EXPECT_EQ(::GetThreadPriority(thread_handle), THREAD_PRIORITY_NORMAL);
92 internal::AssertMemoryPriority(thread_handle, MEMORY_PRIORITY_NORMAL);
93 }
94
95 namespace {
96 class MemoryPriorityAssertingThreadDelegate
97 : public base::PlatformThread::Delegate {
98 public:
MemoryPriorityAssertingThreadDelegate(LONG memory_priority)99 explicit MemoryPriorityAssertingThreadDelegate(LONG memory_priority)
100 : memory_priority_(memory_priority) {}
101
ThreadMain()102 void ThreadMain() override {
103 PlatformThreadHandle::Handle thread_handle =
104 PlatformThread::CurrentHandle().platform_handle();
105 internal::AssertMemoryPriority(thread_handle, memory_priority_);
106 }
107
108 LONG memory_priority_;
109 };
110 } // namespace
111
112 } // namespace base
113