| // 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/threading/thread.h" |
| |
| #include <stddef.h> |
| |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/third_party/dynamic_annotations/dynamic_annotations.h" |
| #include "build/build_config.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "testing/platform_test.h" |
| |
| using base::Thread; |
| |
| typedef PlatformTest ThreadTest; |
| |
| namespace { |
| |
| void ToggleValue(bool* value) { |
| ANNOTATE_BENIGN_RACE(value, "Test-only data race on boolean " |
| "in base/thread_unittest"); |
| *value = !*value; |
| } |
| |
| class SleepInsideInitThread : public Thread { |
| public: |
| SleepInsideInitThread() : Thread("none") { |
| init_called_ = false; |
| ANNOTATE_BENIGN_RACE( |
| this, "Benign test-only data race on vptr - http://crbug.com/98219"); |
| } |
| ~SleepInsideInitThread() override { Stop(); } |
| |
| void Init() override { |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500)); |
| init_called_ = true; |
| } |
| bool InitCalled() { return init_called_; } |
| private: |
| bool init_called_; |
| }; |
| |
| enum ThreadEvent { |
| // Thread::Init() was called. |
| THREAD_EVENT_INIT = 0, |
| |
| // The MessageLoop for the thread was deleted. |
| THREAD_EVENT_MESSAGE_LOOP_DESTROYED, |
| |
| // Thread::CleanUp() was called. |
| THREAD_EVENT_CLEANUP, |
| |
| // Keep at end of list. |
| THREAD_NUM_EVENTS |
| }; |
| |
| typedef std::vector<ThreadEvent> EventList; |
| |
| class CaptureToEventList : public Thread { |
| public: |
| // This Thread pushes events into the vector |event_list| to show |
| // the order they occured in. |event_list| must remain valid for the |
| // lifetime of this thread. |
| explicit CaptureToEventList(EventList* event_list) |
| : Thread("none"), |
| event_list_(event_list) { |
| } |
| |
| ~CaptureToEventList() override { Stop(); } |
| |
| void Init() override { event_list_->push_back(THREAD_EVENT_INIT); } |
| |
| void CleanUp() override { event_list_->push_back(THREAD_EVENT_CLEANUP); } |
| |
| private: |
| EventList* event_list_; |
| }; |
| |
| // Observer that writes a value into |event_list| when a message loop has been |
| // destroyed. |
| class CapturingDestructionObserver |
| : public base::MessageLoop::DestructionObserver { |
| public: |
| // |event_list| must remain valid throughout the observer's lifetime. |
| explicit CapturingDestructionObserver(EventList* event_list) |
| : event_list_(event_list) { |
| } |
| |
| // DestructionObserver implementation: |
| void WillDestroyCurrentMessageLoop() override { |
| event_list_->push_back(THREAD_EVENT_MESSAGE_LOOP_DESTROYED); |
| event_list_ = NULL; |
| } |
| |
| private: |
| EventList* event_list_; |
| }; |
| |
| // Task that adds a destruction observer to the current message loop. |
| void RegisterDestructionObserver( |
| base::MessageLoop::DestructionObserver* observer) { |
| base::MessageLoop::current()->AddDestructionObserver(observer); |
| } |
| |
| // Task that calls GetThreadId() of |thread|, stores the result into |id|, then |
| // signal |event|. |
| void ReturnThreadId(base::Thread* thread, |
| base::PlatformThreadId* id, |
| base::WaitableEvent* event) { |
| *id = thread->GetThreadId(); |
| event->Signal(); |
| } |
| |
| } // namespace |
| |
| TEST_F(ThreadTest, Restart) { |
| Thread a("Restart"); |
| a.Stop(); |
| EXPECT_FALSE(a.message_loop()); |
| EXPECT_FALSE(a.IsRunning()); |
| EXPECT_TRUE(a.Start()); |
| EXPECT_TRUE(a.message_loop()); |
| EXPECT_TRUE(a.IsRunning()); |
| a.Stop(); |
| EXPECT_FALSE(a.message_loop()); |
| EXPECT_FALSE(a.IsRunning()); |
| EXPECT_TRUE(a.Start()); |
| EXPECT_TRUE(a.message_loop()); |
| EXPECT_TRUE(a.IsRunning()); |
| a.Stop(); |
| EXPECT_FALSE(a.message_loop()); |
| EXPECT_FALSE(a.IsRunning()); |
| a.Stop(); |
| EXPECT_FALSE(a.message_loop()); |
| EXPECT_FALSE(a.IsRunning()); |
| } |
| |
| TEST_F(ThreadTest, StartWithOptions_StackSize) { |
| Thread a("StartWithStackSize"); |
| // Ensure that the thread can work with only 12 kb and still process a |
| // message. |
| Thread::Options options; |
| #if defined(ADDRESS_SANITIZER) |
| // ASan bloats the stack variables and overflows the 12 kb stack. |
| options.stack_size = 24*1024; |
| #else |
| options.stack_size = 12*1024; |
| #endif |
| EXPECT_TRUE(a.StartWithOptions(options)); |
| EXPECT_TRUE(a.message_loop()); |
| EXPECT_TRUE(a.IsRunning()); |
| |
| bool was_invoked = false; |
| a.task_runner()->PostTask(FROM_HERE, base::Bind(&ToggleValue, &was_invoked)); |
| |
| // wait for the task to run (we could use a kernel event here |
| // instead to avoid busy waiting, but this is sufficient for |
| // testing purposes). |
| for (int i = 100; i >= 0 && !was_invoked; --i) { |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); |
| } |
| EXPECT_TRUE(was_invoked); |
| } |
| |
| TEST_F(ThreadTest, TwoTasks) { |
| bool was_invoked = false; |
| { |
| Thread a("TwoTasks"); |
| EXPECT_TRUE(a.Start()); |
| EXPECT_TRUE(a.message_loop()); |
| |
| // Test that all events are dispatched before the Thread object is |
| // destroyed. We do this by dispatching a sleep event before the |
| // event that will toggle our sentinel value. |
| a.task_runner()->PostTask( |
| FROM_HERE, base::Bind(static_cast<void (*)(base::TimeDelta)>( |
| &base::PlatformThread::Sleep), |
| base::TimeDelta::FromMilliseconds(20))); |
| a.task_runner()->PostTask(FROM_HERE, |
| base::Bind(&ToggleValue, &was_invoked)); |
| } |
| EXPECT_TRUE(was_invoked); |
| } |
| |
| TEST_F(ThreadTest, StopSoon) { |
| Thread a("StopSoon"); |
| EXPECT_TRUE(a.Start()); |
| EXPECT_TRUE(a.message_loop()); |
| EXPECT_TRUE(a.IsRunning()); |
| a.StopSoon(); |
| a.StopSoon(); |
| a.Stop(); |
| EXPECT_FALSE(a.message_loop()); |
| EXPECT_FALSE(a.IsRunning()); |
| } |
| |
| TEST_F(ThreadTest, ThreadName) { |
| Thread a("ThreadName"); |
| EXPECT_TRUE(a.Start()); |
| EXPECT_EQ("ThreadName", a.thread_name()); |
| } |
| |
| TEST_F(ThreadTest, ThreadId) { |
| Thread a("ThreadId0"); |
| Thread b("ThreadId1"); |
| a.Start(); |
| b.Start(); |
| |
| // Post a task that calls GetThreadId() on the created thread. |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| base::PlatformThreadId id_from_new_thread; |
| a.task_runner()->PostTask( |
| FROM_HERE, base::Bind(ReturnThreadId, &a, &id_from_new_thread, &event)); |
| |
| // Call GetThreadId() on the current thread before calling event.Wait() so |
| // that this test can find a race issue with TSAN. |
| base::PlatformThreadId id_from_current_thread = a.GetThreadId(); |
| |
| // Check if GetThreadId() returns consistent value in both threads. |
| event.Wait(); |
| EXPECT_EQ(id_from_current_thread, id_from_new_thread); |
| |
| // A started thread should have a valid ID. |
| EXPECT_NE(base::kInvalidThreadId, a.GetThreadId()); |
| EXPECT_NE(base::kInvalidThreadId, b.GetThreadId()); |
| |
| // Each thread should have a different thread ID. |
| EXPECT_NE(a.GetThreadId(), b.GetThreadId()); |
| } |
| |
| TEST_F(ThreadTest, ThreadIdWithRestart) { |
| Thread a("ThreadIdWithRestart"); |
| base::PlatformThreadId previous_id = base::kInvalidThreadId; |
| |
| for (size_t i = 0; i < 16; ++i) { |
| EXPECT_TRUE(a.Start()); |
| base::PlatformThreadId current_id = a.GetThreadId(); |
| EXPECT_NE(previous_id, current_id); |
| previous_id = current_id; |
| a.Stop(); |
| } |
| } |
| |
| // Make sure Init() is called after Start() and before |
| // WaitUntilThreadInitialized() returns. |
| TEST_F(ThreadTest, SleepInsideInit) { |
| SleepInsideInitThread t; |
| EXPECT_FALSE(t.InitCalled()); |
| t.StartAndWaitForTesting(); |
| EXPECT_TRUE(t.InitCalled()); |
| } |
| |
| // Make sure that the destruction sequence is: |
| // |
| // (1) Thread::CleanUp() |
| // (2) MessageLoop::~MessageLoop() |
| // MessageLoop::DestructionObservers called. |
| TEST_F(ThreadTest, CleanUp) { |
| EventList captured_events; |
| CapturingDestructionObserver loop_destruction_observer(&captured_events); |
| |
| { |
| // Start a thread which writes its event into |captured_events|. |
| CaptureToEventList t(&captured_events); |
| EXPECT_TRUE(t.Start()); |
| EXPECT_TRUE(t.message_loop()); |
| EXPECT_TRUE(t.IsRunning()); |
| |
| // Register an observer that writes into |captured_events| once the |
| // thread's message loop is destroyed. |
| t.task_runner()->PostTask( |
| FROM_HERE, base::Bind(&RegisterDestructionObserver, |
| base::Unretained(&loop_destruction_observer))); |
| |
| // Upon leaving this scope, the thread is deleted. |
| } |
| |
| // Check the order of events during shutdown. |
| ASSERT_EQ(static_cast<size_t>(THREAD_NUM_EVENTS), captured_events.size()); |
| EXPECT_EQ(THREAD_EVENT_INIT, captured_events[0]); |
| EXPECT_EQ(THREAD_EVENT_CLEANUP, captured_events[1]); |
| EXPECT_EQ(THREAD_EVENT_MESSAGE_LOOP_DESTROYED, captured_events[2]); |
| } |
| |
| TEST_F(ThreadTest, ThreadNotStarted) { |
| Thread a("Inert"); |
| EXPECT_FALSE(a.task_runner()); |
| } |
| |
| TEST_F(ThreadTest, MultipleWaitUntilThreadStarted) { |
| Thread a("MultipleWaitUntilThreadStarted"); |
| EXPECT_TRUE(a.Start()); |
| // It's OK to call WaitUntilThreadStarted() multiple times. |
| EXPECT_TRUE(a.WaitUntilThreadStarted()); |
| EXPECT_TRUE(a.WaitUntilThreadStarted()); |
| } |