1// Copyright (c) 2012 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 <stddef.h>
6
7#include "base/compiler_specific.h"
8#include "base/macros.h"
9#include "base/synchronization/waitable_event.h"
10#include "base/threading/platform_thread.h"
11#include "build/build_config.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14#if defined(OS_POSIX)
15#include <sys/types.h>
16#include <unistd.h>
17#elif defined(OS_WIN)
18#include <windows.h>
19#endif
20
21namespace base {
22
23// Trivial tests that thread runs and doesn't crash on create and join ---------
24
25namespace {
26
27class TrivialThread : public PlatformThread::Delegate {
28 public:
29  TrivialThread() : did_run_(false) {}
30
31  void ThreadMain() override { did_run_ = true; }
32
33  bool did_run() const { return did_run_; }
34
35 private:
36  bool did_run_;
37
38  DISALLOW_COPY_AND_ASSIGN(TrivialThread);
39};
40
41}  // namespace
42
43TEST(PlatformThreadTest, Trivial) {
44  TrivialThread thread;
45  PlatformThreadHandle handle;
46
47  ASSERT_FALSE(thread.did_run());
48  ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
49  PlatformThread::Join(handle);
50  ASSERT_TRUE(thread.did_run());
51}
52
53TEST(PlatformThreadTest, TrivialTimesTen) {
54  TrivialThread thread[10];
55  PlatformThreadHandle handle[arraysize(thread)];
56
57  for (size_t n = 0; n < arraysize(thread); n++)
58    ASSERT_FALSE(thread[n].did_run());
59  for (size_t n = 0; n < arraysize(thread); n++)
60    ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
61  for (size_t n = 0; n < arraysize(thread); n++)
62    PlatformThread::Join(handle[n]);
63  for (size_t n = 0; n < arraysize(thread); n++)
64    ASSERT_TRUE(thread[n].did_run());
65}
66
67// Tests of basic thread functions ---------------------------------------------
68
69namespace {
70
71class FunctionTestThread : public PlatformThread::Delegate {
72 public:
73  FunctionTestThread()
74      : thread_id_(kInvalidThreadId),
75        termination_ready_(true, false),
76        terminate_thread_(true, false),
77        done_(false) {}
78  ~FunctionTestThread() override {
79    EXPECT_TRUE(terminate_thread_.IsSignaled())
80        << "Need to mark thread for termination and join the underlying thread "
81        << "before destroying a FunctionTestThread as it owns the "
82        << "WaitableEvent blocking the underlying thread's main.";
83  }
84
85  // Grabs |thread_id_|, runs an optional test on that thread, signals
86  // |termination_ready_|, and then waits for |terminate_thread_| to be
87  // signaled before exiting.
88  void ThreadMain() override {
89    thread_id_ = PlatformThread::CurrentId();
90    EXPECT_NE(thread_id_, kInvalidThreadId);
91
92    // Make sure that the thread ID is the same across calls.
93    EXPECT_EQ(thread_id_, PlatformThread::CurrentId());
94
95    // Run extra tests.
96    RunTest();
97
98    termination_ready_.Signal();
99    terminate_thread_.Wait();
100
101    done_ = true;
102  }
103
104  PlatformThreadId thread_id() const {
105    EXPECT_TRUE(termination_ready_.IsSignaled()) << "Thread ID still unknown";
106    return thread_id_;
107  }
108
109  bool IsRunning() const {
110    return termination_ready_.IsSignaled() && !done_;
111  }
112
113  // Blocks until this thread is started and ready to be terminated.
114  void WaitForTerminationReady() { termination_ready_.Wait(); }
115
116  // Marks this thread for termination (callers must then join this thread to be
117  // guaranteed of termination).
118  void MarkForTermination() { terminate_thread_.Signal(); }
119
120 private:
121  // Runs an optional test on the newly created thread.
122  virtual void RunTest() {}
123
124  PlatformThreadId thread_id_;
125
126  mutable WaitableEvent termination_ready_;
127  WaitableEvent terminate_thread_;
128  bool done_;
129
130  DISALLOW_COPY_AND_ASSIGN(FunctionTestThread);
131};
132
133}  // namespace
134
135TEST(PlatformThreadTest, Function) {
136  PlatformThreadId main_thread_id = PlatformThread::CurrentId();
137
138  FunctionTestThread thread;
139  PlatformThreadHandle handle;
140
141  ASSERT_FALSE(thread.IsRunning());
142  ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
143  thread.WaitForTerminationReady();
144  ASSERT_TRUE(thread.IsRunning());
145  EXPECT_NE(thread.thread_id(), main_thread_id);
146
147  thread.MarkForTermination();
148  PlatformThread::Join(handle);
149  ASSERT_FALSE(thread.IsRunning());
150
151  // Make sure that the thread ID is the same across calls.
152  EXPECT_EQ(main_thread_id, PlatformThread::CurrentId());
153}
154
155TEST(PlatformThreadTest, FunctionTimesTen) {
156  PlatformThreadId main_thread_id = PlatformThread::CurrentId();
157
158  FunctionTestThread thread[10];
159  PlatformThreadHandle handle[arraysize(thread)];
160
161  for (size_t n = 0; n < arraysize(thread); n++)
162    ASSERT_FALSE(thread[n].IsRunning());
163
164  for (size_t n = 0; n < arraysize(thread); n++)
165    ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n]));
166  for (size_t n = 0; n < arraysize(thread); n++)
167    thread[n].WaitForTerminationReady();
168
169  for (size_t n = 0; n < arraysize(thread); n++) {
170    ASSERT_TRUE(thread[n].IsRunning());
171    EXPECT_NE(thread[n].thread_id(), main_thread_id);
172
173    // Make sure no two threads get the same ID.
174    for (size_t i = 0; i < n; ++i) {
175      EXPECT_NE(thread[i].thread_id(), thread[n].thread_id());
176    }
177  }
178
179  for (size_t n = 0; n < arraysize(thread); n++)
180    thread[n].MarkForTermination();
181  for (size_t n = 0; n < arraysize(thread); n++)
182    PlatformThread::Join(handle[n]);
183  for (size_t n = 0; n < arraysize(thread); n++)
184    ASSERT_FALSE(thread[n].IsRunning());
185
186  // Make sure that the thread ID is the same across calls.
187  EXPECT_EQ(main_thread_id, PlatformThread::CurrentId());
188}
189
190namespace {
191
192const ThreadPriority kThreadPriorityTestValues[] = {
193// The order should be higher to lower to cover as much cases as possible on
194// Linux trybots running without CAP_SYS_NICE permission.
195#if !defined(OS_ANDROID)
196    // PlatformThread::GetCurrentThreadPriority() on Android does not support
197    // REALTIME_AUDIO case. See http://crbug.com/505474.
198    ThreadPriority::REALTIME_AUDIO,
199#endif
200    ThreadPriority::DISPLAY,
201    // This redundant BACKGROUND priority is to test backgrounding from other
202    // priorities, and unbackgrounding.
203    ThreadPriority::BACKGROUND,
204    ThreadPriority::NORMAL,
205    ThreadPriority::BACKGROUND};
206
207bool IsBumpingPriorityAllowed() {
208#if defined(OS_POSIX)
209  // Only root can raise thread priority on POSIX environment. On Linux, users
210  // who have CAP_SYS_NICE permission also can raise the thread priority, but
211  // libcap.so would be needed to check the capability.
212  return geteuid() == 0;
213#else
214  return true;
215#endif
216}
217
218class ThreadPriorityTestThread : public FunctionTestThread {
219 public:
220  ThreadPriorityTestThread() = default;
221  ~ThreadPriorityTestThread() override = default;
222
223 private:
224  void RunTest() override {
225    // Confirm that the current thread's priority is as expected.
226    EXPECT_EQ(ThreadPriority::NORMAL,
227              PlatformThread::GetCurrentThreadPriority());
228
229    // Toggle each supported priority on the current thread and confirm it
230    // affects it.
231    const bool bumping_priority_allowed = IsBumpingPriorityAllowed();
232    for (size_t i = 0; i < arraysize(kThreadPriorityTestValues); ++i) {
233      SCOPED_TRACE(i);
234      if (!bumping_priority_allowed &&
235          kThreadPriorityTestValues[i] >
236              PlatformThread::GetCurrentThreadPriority()) {
237        continue;
238      }
239
240      // Alter and verify the current thread's priority.
241      PlatformThread::SetCurrentThreadPriority(kThreadPriorityTestValues[i]);
242      EXPECT_EQ(kThreadPriorityTestValues[i],
243                PlatformThread::GetCurrentThreadPriority());
244    }
245  }
246
247  DISALLOW_COPY_AND_ASSIGN(ThreadPriorityTestThread);
248};
249
250}  // namespace
251
252#if defined(OS_MACOSX)
253// PlatformThread::GetCurrentThreadPriority() is not implemented on OS X.
254#define MAYBE_ThreadPriorityCurrentThread DISABLED_ThreadPriorityCurrentThread
255#else
256#define MAYBE_ThreadPriorityCurrentThread ThreadPriorityCurrentThread
257#endif
258
259// Test changing a created thread's priority (which has different semantics on
260// some platforms).
261TEST(PlatformThreadTest, MAYBE_ThreadPriorityCurrentThread) {
262  ThreadPriorityTestThread thread;
263  PlatformThreadHandle handle;
264
265  ASSERT_FALSE(thread.IsRunning());
266  ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle));
267  thread.WaitForTerminationReady();
268  ASSERT_TRUE(thread.IsRunning());
269
270  thread.MarkForTermination();
271  PlatformThread::Join(handle);
272  ASSERT_FALSE(thread.IsRunning());
273}
274
275}  // namespace base
276