| // 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 <utility> | |
| #include <vector> | |
| #include "base/bind.h" | |
| #include "base/debug/leak_annotations.h" | |
| #include "base/macros.h" | |
| #include "base/memory/ptr_util.h" | |
| #include "base/message_loop/message_loop.h" | |
| #include "base/message_loop/message_loop_current.h" | |
| #include "base/run_loop.h" | |
| #include "base/single_thread_task_runner.h" | |
| #include "base/synchronization/waitable_event.h" | |
| #include "base/test/gtest_util.h" | |
| #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | |
| #include "base/threading/platform_thread.h" | |
| #include "base/time/time.h" | |
| #include "build/build_config.h" | |
| #include "starboard/types.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_; | |
| DISALLOW_COPY_AND_ASSIGN(SleepInsideInitThread); | |
| }; | |
| 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_; | |
| DISALLOW_COPY_AND_ASSIGN(CaptureToEventList); | |
| }; | |
| // Observer that writes a value into |event_list| when a message loop has been | |
| // destroyed. | |
| class CapturingDestructionObserver | |
| : public base::MessageLoopCurrent::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_ = nullptr; | |
| } | |
| private: | |
| EventList* event_list_; | |
| DISALLOW_COPY_AND_ASSIGN(CapturingDestructionObserver); | |
| }; | |
| // Task that adds a destruction observer to the current message loop. | |
| void RegisterDestructionObserver( | |
| base::MessageLoopCurrent::DestructionObserver* observer) { | |
| base::MessageLoopCurrent::Get()->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, StartWithOptions_StackSize) { | |
| Thread a("StartWithStackSize"); | |
| // Ensure that the thread can work with only 12 kb and still process a | |
| // message. At the same time, we should scale with the bitness of the system | |
| // where 12 kb is definitely not enough. | |
| // 12 kb = 3072 Slots on a 32-bit system, so we'll scale based off of that. | |
| Thread::Options options; | |
| #if defined(ADDRESS_SANITIZER) || !defined(NDEBUG) | |
| // ASan bloats the stack variables and overflows the 3072 slot stack. Some | |
| // debug builds also grow the stack too much. | |
| options.stack_size = 2 * 3072 * sizeof(uintptr_t); | |
| #else | |
| options.stack_size = 3072 * sizeof(uintptr_t); | |
| #endif | |
| EXPECT_TRUE(a.StartWithOptions(options)); | |
| EXPECT_TRUE(a.message_loop()); | |
| EXPECT_TRUE(a.IsRunning()); | |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
| base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| a.task_runner()->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event))); | |
| event.Wait(); | |
| } | |
| // Intentional test-only race for otherwise untestable code, won't fix. | |
| // https://crbug.com/634383 | |
| #if !defined(THREAD_SANITIZER) | |
| TEST_F(ThreadTest, StartWithOptions_NonJoinable) { | |
| Thread* a = new Thread("StartNonJoinable"); | |
| // Non-joinable threads have to be leaked for now (see | |
| // Thread::Options::joinable for details). | |
| ANNOTATE_LEAKING_OBJECT_PTR(a); | |
| Thread::Options options; | |
| options.joinable = false; | |
| EXPECT_TRUE(a->StartWithOptions(options)); | |
| EXPECT_TRUE(a->message_loop()); | |
| EXPECT_TRUE(a->IsRunning()); | |
| // Without this call this test is racy. The above IsRunning() succeeds because | |
| // of an early-return condition while between Start() and StopSoon(), after | |
| // invoking StopSoon() below this early-return condition is no longer | |
| // satisfied and the real |is_running_| bit has to be checked. It could still | |
| // be false if the message loop hasn't started for real in practice. This is | |
| // only a requirement for this test because the non-joinable property forces | |
| // it to use StopSoon() and not wait for a complete Stop(). | |
| EXPECT_TRUE(a->WaitUntilThreadStarted()); | |
| // Make the thread block until |block_event| is signaled. | |
| base::WaitableEvent block_event( | |
| base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
| base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| a->task_runner()->PostTask(FROM_HERE, | |
| base::BindOnce(&base::WaitableEvent::Wait, | |
| base::Unretained(&block_event))); | |
| a->StopSoon(); | |
| EXPECT_TRUE(a->IsRunning()); | |
| // Unblock the task and give a bit of extra time to unwind QuitWhenIdle(). | |
| block_event.Signal(); | |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); | |
| // The thread should now have stopped on its own. | |
| EXPECT_FALSE(a->IsRunning()); | |
| } | |
| #endif | |
| TEST_F(ThreadTest, TwoTasksOnJoinableThread) { | |
| bool was_invoked = false; | |
| { | |
| Thread a("TwoTasksOnJoinableThread"); | |
| 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::BindOnce(static_cast<void (*)(base::TimeDelta)>( | |
| &base::PlatformThread::Sleep), | |
| base::TimeDelta::FromMilliseconds(20))); | |
| a.task_runner()->PostTask(FROM_HERE, | |
| base::BindOnce(&ToggleValue, &was_invoked)); | |
| } | |
| EXPECT_TRUE(was_invoked); | |
| } | |
| TEST_F(ThreadTest, DestroyWhileRunningIsSafe) { | |
| Thread a("DestroyWhileRunningIsSafe"); | |
| EXPECT_TRUE(a.Start()); | |
| EXPECT_TRUE(a.WaitUntilThreadStarted()); | |
| } | |
| // TODO(gab): Enable this test when destroying a non-joinable Thread instance | |
| // is supported (proposal @ https://crbug.com/629139#c14). | |
| TEST_F(ThreadTest, DISABLED_DestroyWhileRunningNonJoinableIsSafe) { | |
| { | |
| Thread a("DestroyWhileRunningNonJoinableIsSafe"); | |
| Thread::Options options; | |
| options.joinable = false; | |
| EXPECT_TRUE(a.StartWithOptions(options)); | |
| EXPECT_TRUE(a.WaitUntilThreadStarted()); | |
| } | |
| // Attempt to catch use-after-frees from the non-joinable thread in the | |
| // scope of this test if any. | |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); | |
| } | |
| TEST_F(ThreadTest, StopSoon) { | |
| Thread a("StopSoon"); | |
| EXPECT_TRUE(a.Start()); | |
| EXPECT_TRUE(a.message_loop()); | |
| EXPECT_TRUE(a.IsRunning()); | |
| a.StopSoon(); | |
| a.Stop(); | |
| EXPECT_FALSE(a.message_loop()); | |
| EXPECT_FALSE(a.IsRunning()); | |
| } | |
| TEST_F(ThreadTest, StopTwiceNop) { | |
| Thread a("StopTwiceNop"); | |
| EXPECT_TRUE(a.Start()); | |
| EXPECT_TRUE(a.message_loop()); | |
| EXPECT_TRUE(a.IsRunning()); | |
| a.StopSoon(); | |
| // Calling StopSoon() a second time should be a nop. | |
| a.StopSoon(); | |
| a.Stop(); | |
| // Same with Stop(). | |
| a.Stop(); | |
| EXPECT_FALSE(a.message_loop()); | |
| EXPECT_FALSE(a.IsRunning()); | |
| // Calling them when not running should also nop. | |
| a.StopSoon(); | |
| a.Stop(); | |
| } | |
| // TODO(gab): Enable this test in conjunction with re-enabling the sequence | |
| // check in Thread::Stop() as part of http://crbug.com/629139. | |
| TEST_F(ThreadTest, DISABLED_StopOnNonOwningThreadIsDeath) { | |
| Thread a("StopOnNonOwningThreadDeath"); | |
| EXPECT_TRUE(a.StartAndWaitForTesting()); | |
| Thread b("NonOwningThread"); | |
| b.Start(); | |
| EXPECT_DCHECK_DEATH({ | |
| // Stopping |a| on |b| isn't allowed. | |
| b.task_runner()->PostTask( | |
| FROM_HERE, base::BindOnce(&Thread::Stop, base::Unretained(&a))); | |
| // Block here so the DCHECK on |b| always happens in this scope. | |
| base::PlatformThread::Sleep(base::TimeDelta::Max()); | |
| }); | |
| } | |
| TEST_F(ThreadTest, TransferOwnershipAndStop) { | |
| std::unique_ptr<Thread> a = | |
| std::make_unique<Thread>("TransferOwnershipAndStop"); | |
| EXPECT_TRUE(a->StartAndWaitForTesting()); | |
| EXPECT_TRUE(a->IsRunning()); | |
| Thread b("TakingOwnershipThread"); | |
| b.Start(); | |
| base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, | |
| base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| // a->DetachFromSequence() should allow |b| to use |a|'s Thread API. | |
| a->DetachFromSequence(); | |
| b.task_runner()->PostTask( | |
| FROM_HERE, base::BindOnce( | |
| [](std::unique_ptr<Thread> thread_to_stop, | |
| base::WaitableEvent* event_to_signal) -> void { | |
| thread_to_stop->Stop(); | |
| event_to_signal->Signal(); | |
| }, | |
| std::move(a), base::Unretained(&event))); | |
| event.Wait(); | |
| } | |
| TEST_F(ThreadTest, StartTwice) { | |
| Thread a("StartTwice"); | |
| 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()); | |
| } | |
| // Intentional test-only race for otherwise untestable code, won't fix. | |
| // https://crbug.com/634383 | |
| #if !defined(THREAD_SANITIZER) | |
| TEST_F(ThreadTest, StartTwiceNonJoinableNotAllowed) { | |
| LOG(ERROR) << __FUNCTION__; | |
| Thread* a = new Thread("StartTwiceNonJoinable"); | |
| // Non-joinable threads have to be leaked for now (see | |
| // Thread::Options::joinable for details). | |
| ANNOTATE_LEAKING_OBJECT_PTR(a); | |
| Thread::Options options; | |
| options.joinable = false; | |
| EXPECT_TRUE(a->StartWithOptions(options)); | |
| EXPECT_TRUE(a->message_loop()); | |
| EXPECT_TRUE(a->IsRunning()); | |
| // Signaled when last task on |a| is processed. | |
| base::WaitableEvent last_task_event( | |
| base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
| base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| a->task_runner()->PostTask( | |
| FROM_HERE, base::BindOnce(&base::WaitableEvent::Signal, | |
| base::Unretained(&last_task_event))); | |
| // StopSoon() is non-blocking, Yield() to |a|, wait for last task to be | |
| // processed and a little more for QuitWhenIdle() to unwind before considering | |
| // the thread "stopped". | |
| a->StopSoon(); | |
| base::PlatformThread::YieldCurrentThread(); | |
| last_task_event.Wait(); | |
| base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); | |
| // This test assumes that the above was sufficient to let the thread fully | |
| // stop. | |
| ASSERT_FALSE(a->IsRunning()); | |
| // Restarting it should not be allowed. | |
| EXPECT_DCHECK_DEATH(a->Start()); | |
| } | |
| #endif | |
| 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::BindOnce(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() | |
| // MessageLoopCurrent::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::BindOnce(&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()); | |
| } | |
| TEST_F(ThreadTest, FlushForTesting) { | |
| Thread a("FlushForTesting"); | |
| // Flushing a non-running thread should be a no-op. | |
| a.FlushForTesting(); | |
| ASSERT_TRUE(a.Start()); | |
| // Flushing a thread with no tasks shouldn't block. | |
| a.FlushForTesting(); | |
| constexpr base::TimeDelta kSleepPerTestTask = | |
| base::TimeDelta::FromMilliseconds(50); | |
| constexpr size_t kNumSleepTasks = 5; | |
| const base::TimeTicks ticks_before_post = base::TimeTicks::Now(); | |
| for (size_t i = 0; i < kNumSleepTasks; ++i) { | |
| a.task_runner()->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(&base::PlatformThread::Sleep, kSleepPerTestTask)); | |
| } | |
| // All tasks should have executed, as reflected by the elapsed time. | |
| a.FlushForTesting(); | |
| EXPECT_GE(base::TimeTicks::Now() - ticks_before_post, | |
| kNumSleepTasks * kSleepPerTestTask); | |
| a.Stop(); | |
| // Flushing a stopped thread should be a no-op. | |
| a.FlushForTesting(); | |
| } | |
| namespace { | |
| // A Thread which uses a MessageLoop on the stack. It won't start a real | |
| // underlying thread (instead its messages can be processed by a RunLoop on the | |
| // stack). | |
| class ExternalMessageLoopThread : public Thread { | |
| public: | |
| ExternalMessageLoopThread() : Thread("ExternalMessageLoopThread") {} | |
| ~ExternalMessageLoopThread() override { Stop(); } | |
| void InstallMessageLoop() { SetMessageLoop(&external_message_loop_); } | |
| void VerifyUsingExternalMessageLoop( | |
| bool expected_using_external_message_loop) { | |
| EXPECT_EQ(expected_using_external_message_loop, | |
| using_external_message_loop()); | |
| } | |
| private: | |
| base::MessageLoop external_message_loop_; | |
| DISALLOW_COPY_AND_ASSIGN(ExternalMessageLoopThread); | |
| }; | |
| } // namespace | |
| TEST_F(ThreadTest, ExternalMessageLoop) { | |
| ExternalMessageLoopThread a; | |
| EXPECT_FALSE(a.message_loop()); | |
| EXPECT_FALSE(a.IsRunning()); | |
| a.VerifyUsingExternalMessageLoop(false); | |
| a.InstallMessageLoop(); | |
| EXPECT_TRUE(a.message_loop()); | |
| EXPECT_TRUE(a.IsRunning()); | |
| a.VerifyUsingExternalMessageLoop(true); | |
| bool ran = false; | |
| a.task_runner()->PostTask( | |
| FROM_HERE, base::BindOnce([](bool* toggled) { *toggled = true; }, &ran)); | |
| base::RunLoop().RunUntilIdle(); | |
| EXPECT_TRUE(ran); | |
| a.Stop(); | |
| EXPECT_FALSE(a.message_loop()); | |
| EXPECT_FALSE(a.IsRunning()); | |
| a.VerifyUsingExternalMessageLoop(true); | |
| // Confirm that running any remaining tasks posted from Stop() goes smoothly | |
| // (e.g. https://codereview.chromium.org/2135413003/#ps300001 crashed if | |
| // StopSoon() posted Thread::ThreadQuitHelper() while |run_loop_| was null). | |
| base::RunLoop().RunUntilIdle(); | |
| } |