| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/compiler_specific.h" | 
 | #include "base/macros.h" | 
 | #include "base/synchronization/waitable_event.h" | 
 | #include "base/threading/platform_thread.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | #if defined(OS_WIN) | 
 | #include <windows.h> | 
 | #endif | 
 |  | 
 | namespace base { | 
 |  | 
 | // Trivial tests that thread runs and doesn't crash on create and join --------- | 
 |  | 
 | class TrivialThread : public PlatformThread::Delegate { | 
 |  public: | 
 |   TrivialThread() : did_run_(false) {} | 
 |  | 
 |   void ThreadMain() override { did_run_ = true; } | 
 |  | 
 |   bool did_run() const { return did_run_; } | 
 |  | 
 |  private: | 
 |   bool did_run_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(TrivialThread); | 
 | }; | 
 |  | 
 | TEST(PlatformThreadTest, Trivial) { | 
 |   TrivialThread thread; | 
 |   PlatformThreadHandle handle; | 
 |  | 
 |   ASSERT_FALSE(thread.did_run()); | 
 |   ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); | 
 |   PlatformThread::Join(handle); | 
 |   ASSERT_TRUE(thread.did_run()); | 
 | } | 
 |  | 
 | TEST(PlatformThreadTest, TrivialTimesTen) { | 
 |   TrivialThread thread[10]; | 
 |   PlatformThreadHandle handle[arraysize(thread)]; | 
 |  | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     ASSERT_FALSE(thread[n].did_run()); | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n])); | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     PlatformThread::Join(handle[n]); | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     ASSERT_TRUE(thread[n].did_run()); | 
 | } | 
 |  | 
 | // Tests of basic thread functions --------------------------------------------- | 
 |  | 
 | class FunctionTestThread : public PlatformThread::Delegate { | 
 |  public: | 
 |   FunctionTestThread() | 
 |       : thread_id_(kInvalidThreadId), | 
 |         thread_started_(true, false), | 
 |         terminate_thread_(true, false), | 
 |         done_(false) {} | 
 |   ~FunctionTestThread() override { | 
 |     EXPECT_TRUE(terminate_thread_.IsSignaled()) | 
 |         << "Need to mark thread for termination and join the underlying thread " | 
 |         << "before destroying a FunctionTestThread as it owns the " | 
 |         << "WaitableEvent blocking the underlying thread's main."; | 
 |   } | 
 |  | 
 |   // Grabs |thread_id_|, signals |thread_started_|, and then waits for | 
 |   // |terminate_thread_| to be signaled before exiting. | 
 |   void ThreadMain() override { | 
 |     thread_id_ = PlatformThread::CurrentId(); | 
 |     EXPECT_NE(thread_id_, kInvalidThreadId); | 
 |  | 
 |     // Make sure that the thread ID is the same across calls. | 
 |     EXPECT_EQ(thread_id_, PlatformThread::CurrentId()); | 
 |  | 
 |     thread_started_.Signal(); | 
 |  | 
 |     terminate_thread_.Wait(); | 
 |  | 
 |     done_ = true; | 
 |   } | 
 |  | 
 |   PlatformThreadId thread_id() const { | 
 |     EXPECT_TRUE(thread_started_.IsSignaled()) << "Thread ID still unknown"; | 
 |     return thread_id_; | 
 |   } | 
 |  | 
 |   bool IsRunning() const { | 
 |     return thread_started_.IsSignaled() && !done_; | 
 |   } | 
 |  | 
 |   // Blocks until this thread is started. | 
 |   void WaitForThreadStart() { thread_started_.Wait(); } | 
 |  | 
 |   // Mark this thread for termination (callers must then join this thread to be | 
 |   // guaranteed of termination). | 
 |   void MarkForTermination() { terminate_thread_.Signal(); } | 
 |  | 
 |  private: | 
 |   PlatformThreadId thread_id_; | 
 |  | 
 |   mutable WaitableEvent thread_started_; | 
 |   WaitableEvent terminate_thread_; | 
 |   bool done_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(FunctionTestThread); | 
 | }; | 
 |  | 
 | TEST(PlatformThreadTest, Function) { | 
 |   PlatformThreadId main_thread_id = PlatformThread::CurrentId(); | 
 |  | 
 |   FunctionTestThread thread; | 
 |   PlatformThreadHandle handle; | 
 |  | 
 |   ASSERT_FALSE(thread.IsRunning()); | 
 |   ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); | 
 |   thread.WaitForThreadStart(); | 
 |   ASSERT_TRUE(thread.IsRunning()); | 
 |   EXPECT_NE(thread.thread_id(), main_thread_id); | 
 |  | 
 |   thread.MarkForTermination(); | 
 |   PlatformThread::Join(handle); | 
 |   ASSERT_FALSE(thread.IsRunning()); | 
 |  | 
 |   // Make sure that the thread ID is the same across calls. | 
 |   EXPECT_EQ(main_thread_id, PlatformThread::CurrentId()); | 
 | } | 
 |  | 
 | TEST(PlatformThreadTest, FunctionTimesTen) { | 
 |   PlatformThreadId main_thread_id = PlatformThread::CurrentId(); | 
 |  | 
 |   FunctionTestThread thread[10]; | 
 |   PlatformThreadHandle handle[arraysize(thread)]; | 
 |  | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     ASSERT_FALSE(thread[n].IsRunning()); | 
 |  | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     ASSERT_TRUE(PlatformThread::Create(0, &thread[n], &handle[n])); | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     thread[n].WaitForThreadStart(); | 
 |  | 
 |   for (size_t n = 0; n < arraysize(thread); n++) { | 
 |     ASSERT_TRUE(thread[n].IsRunning()); | 
 |     EXPECT_NE(thread[n].thread_id(), main_thread_id); | 
 |  | 
 |     // Make sure no two threads get the same ID. | 
 |     for (size_t i = 0; i < n; ++i) { | 
 |       EXPECT_NE(thread[i].thread_id(), thread[n].thread_id()); | 
 |     } | 
 |   } | 
 |  | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     thread[n].MarkForTermination(); | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     PlatformThread::Join(handle[n]); | 
 |   for (size_t n = 0; n < arraysize(thread); n++) | 
 |     ASSERT_FALSE(thread[n].IsRunning()); | 
 |  | 
 |   // Make sure that the thread ID is the same across calls. | 
 |   EXPECT_EQ(main_thread_id, PlatformThread::CurrentId()); | 
 | } | 
 |  | 
 | namespace { | 
 |  | 
 | const ThreadPriority kThreadPriorityTestValues[] = { | 
 | // Disable non-normal priority toggling on POSIX as it appears to be broken | 
 | // (http://crbug.com/468793). This is prefered to disabling the tests altogether | 
 | // on POSIX as it at least provides coverage for running this code under | 
 | // "normal" priority. | 
 | #if !defined(OS_POSIX) | 
 |     ThreadPriority::DISPLAY, | 
 |     ThreadPriority::REALTIME_AUDIO, | 
 |     // Keep BACKGROUND second to last to test backgrounding from other | 
 |     // priorities. | 
 |     ThreadPriority::BACKGROUND, | 
 | #endif  // !defined(OS_POSIX) | 
 |     // Keep NORMAL last to test unbackgrounding. | 
 |     ThreadPriority::NORMAL | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Test changing another thread's priority. | 
 | // NOTE: This test is partially disabled on POSIX, see note above and | 
 | // http://crbug.com/468793. | 
 | TEST(PlatformThreadTest, ThreadPriorityOtherThread) { | 
 |   PlatformThreadHandle current_handle(PlatformThread::CurrentHandle()); | 
 |  | 
 |   // Confirm that the current thread's priority is as expected. | 
 |   EXPECT_EQ(ThreadPriority::NORMAL, | 
 |             PlatformThread::GetThreadPriority(current_handle)); | 
 |  | 
 |   // Create a test thread. | 
 |   FunctionTestThread thread; | 
 |   PlatformThreadHandle handle; | 
 |   ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); | 
 |   thread.WaitForThreadStart(); | 
 |   EXPECT_NE(thread.thread_id(), kInvalidThreadId); | 
 |   EXPECT_NE(thread.thread_id(), PlatformThread::CurrentId()); | 
 |  | 
 |   // New threads should get normal priority by default. | 
 |   EXPECT_EQ(ThreadPriority::NORMAL, PlatformThread::GetThreadPriority(handle)); | 
 |  | 
 |   // Toggle each supported priority on the test thread and confirm it only | 
 |   // affects it (and not the current thread). | 
 |   for (size_t i = 0; i < arraysize(kThreadPriorityTestValues); ++i) { | 
 |     SCOPED_TRACE(i); | 
 |  | 
 |     // Alter and verify the test thread's priority. | 
 |     PlatformThread::SetThreadPriority(handle, kThreadPriorityTestValues[i]); | 
 |     EXPECT_EQ(kThreadPriorityTestValues[i], | 
 |               PlatformThread::GetThreadPriority(handle)); | 
 |  | 
 |     // Make sure the current thread was otherwise unaffected. | 
 |     EXPECT_EQ(ThreadPriority::NORMAL, | 
 |               PlatformThread::GetThreadPriority(current_handle)); | 
 |   } | 
 |  | 
 |   thread.MarkForTermination(); | 
 |   PlatformThread::Join(handle); | 
 | } | 
 |  | 
 | // Test changing the current thread's priority (which has different semantics on | 
 | // some platforms). | 
 | // NOTE: This test is partially disabled on POSIX, see note above and | 
 | // http://crbug.com/468793. | 
 | TEST(PlatformThreadTest, ThreadPriorityCurrentThread) { | 
 |   PlatformThreadHandle current_handle(PlatformThread::CurrentHandle()); | 
 |  | 
 |   // Confirm that the current thread's priority is as expected. | 
 |   EXPECT_EQ(ThreadPriority::NORMAL, | 
 |             PlatformThread::GetThreadPriority(current_handle)); | 
 |  | 
 |   // Create a test thread for verification purposes only. | 
 |   FunctionTestThread thread; | 
 |   PlatformThreadHandle handle; | 
 |   ASSERT_TRUE(PlatformThread::Create(0, &thread, &handle)); | 
 |   thread.WaitForThreadStart(); | 
 |   EXPECT_NE(thread.thread_id(), kInvalidThreadId); | 
 |   EXPECT_NE(thread.thread_id(), PlatformThread::CurrentId()); | 
 |  | 
 |   // Confirm that the new thread's priority is as expected. | 
 |   EXPECT_EQ(ThreadPriority::NORMAL, PlatformThread::GetThreadPriority(handle)); | 
 |  | 
 |   // Toggle each supported priority on the current thread and confirm it only | 
 |   // affects it (and not the test thread). | 
 |   for (size_t i = 0; i < arraysize(kThreadPriorityTestValues); ++i) { | 
 |     SCOPED_TRACE(i); | 
 |  | 
 |     // Alter and verify the current thread's priority. | 
 |     PlatformThread::SetThreadPriority(current_handle, | 
 |                                       kThreadPriorityTestValues[i]); | 
 |     EXPECT_EQ(kThreadPriorityTestValues[i], | 
 |               PlatformThread::GetThreadPriority(current_handle)); | 
 |  | 
 |     // Make sure the test thread was otherwise unaffected. | 
 |     EXPECT_EQ(ThreadPriority::NORMAL, | 
 |               PlatformThread::GetThreadPriority(handle)); | 
 |   } | 
 |  | 
 |   // Restore current thread priority for follow-up tests. | 
 |   PlatformThread::SetThreadPriority(current_handle, ThreadPriority::NORMAL); | 
 |  | 
 |   thread.MarkForTermination(); | 
 |   PlatformThread::Join(handle); | 
 | } | 
 |  | 
 | }  // namespace base |