// 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 #include "base/atomic_sequence_num.h" #include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/test/gtest_util.h" #include "base/threading/simple_thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { namespace { class SetIntRunner : public DelegateSimpleThread::Delegate { public: SetIntRunner(int* ptr, int val) : ptr_(ptr), val_(val) { } ~SetIntRunner() override = default; private: void Run() override { *ptr_ = val_; } int* ptr_; int val_; DISALLOW_COPY_AND_ASSIGN(SetIntRunner); }; // Signals |started_| when Run() is invoked and waits until |released_| is // signaled to return, signaling |done_| before doing so. Useful for tests that // care to control Run()'s flow. class ControlledRunner : public DelegateSimpleThread::Delegate { public: ControlledRunner() : started_(WaitableEvent::ResetPolicy::MANUAL, WaitableEvent::InitialState::NOT_SIGNALED), released_(WaitableEvent::ResetPolicy::MANUAL, WaitableEvent::InitialState::NOT_SIGNALED), done_(WaitableEvent::ResetPolicy::MANUAL, WaitableEvent::InitialState::NOT_SIGNALED) {} ~ControlledRunner() override { ReleaseAndWaitUntilDone(); } void WaitUntilStarted() { started_.Wait(); } void ReleaseAndWaitUntilDone() { released_.Signal(); done_.Wait(); } private: void Run() override { started_.Signal(); released_.Wait(); done_.Signal(); } WaitableEvent started_; WaitableEvent released_; WaitableEvent done_; DISALLOW_COPY_AND_ASSIGN(ControlledRunner); }; class WaitEventRunner : public DelegateSimpleThread::Delegate { public: explicit WaitEventRunner(WaitableEvent* event) : event_(event) { } ~WaitEventRunner() override = default; private: void Run() override { EXPECT_FALSE(event_->IsSignaled()); event_->Signal(); EXPECT_TRUE(event_->IsSignaled()); } WaitableEvent* event_; DISALLOW_COPY_AND_ASSIGN(WaitEventRunner); }; class SeqRunner : public DelegateSimpleThread::Delegate { public: explicit SeqRunner(AtomicSequenceNumber* seq) : seq_(seq) { } private: void Run() override { seq_->GetNext(); } AtomicSequenceNumber* seq_; DISALLOW_COPY_AND_ASSIGN(SeqRunner); }; // We count up on a sequence number, firing on the event when we've hit our // expected amount, otherwise we wait on the event. This will ensure that we // have all threads outstanding until we hit our expected thread pool size. class VerifyPoolRunner : public DelegateSimpleThread::Delegate { public: VerifyPoolRunner(AtomicSequenceNumber* seq, int total, WaitableEvent* event) : seq_(seq), total_(total), event_(event) { } private: void Run() override { if (seq_->GetNext() == total_) { event_->Signal(); } else { event_->Wait(); } } AtomicSequenceNumber* seq_; int total_; WaitableEvent* event_; DISALLOW_COPY_AND_ASSIGN(VerifyPoolRunner); }; } // namespace TEST(SimpleThreadTest, CreateAndJoin) { int stack_int = 0; SetIntRunner runner(&stack_int, 7); EXPECT_EQ(0, stack_int); DelegateSimpleThread thread(&runner, "int_setter"); EXPECT_FALSE(thread.HasBeenStarted()); EXPECT_FALSE(thread.HasBeenJoined()); EXPECT_EQ(0, stack_int); thread.Start(); EXPECT_TRUE(thread.HasBeenStarted()); EXPECT_FALSE(thread.HasBeenJoined()); thread.Join(); EXPECT_TRUE(thread.HasBeenStarted()); EXPECT_TRUE(thread.HasBeenJoined()); EXPECT_EQ(7, stack_int); } TEST(SimpleThreadTest, WaitForEvent) { // Create a thread, and wait for it to signal us. WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL, WaitableEvent::InitialState::NOT_SIGNALED); WaitEventRunner runner(&event); DelegateSimpleThread thread(&runner, "event_waiter"); EXPECT_FALSE(event.IsSignaled()); thread.Start(); event.Wait(); EXPECT_TRUE(event.IsSignaled()); thread.Join(); } TEST(SimpleThreadTest, NonJoinableStartAndDieOnJoin) { ControlledRunner runner; SimpleThread::Options options; options.joinable = false; DelegateSimpleThread thread(&runner, "non_joinable", options); EXPECT_FALSE(thread.HasBeenStarted()); thread.Start(); EXPECT_TRUE(thread.HasBeenStarted()); // Note: this is not quite the same as |thread.HasBeenStarted()| which // represents ThreadMain() getting ready to invoke Run() whereas // |runner.WaitUntilStarted()| ensures Run() was actually invoked. runner.WaitUntilStarted(); EXPECT_FALSE(thread.HasBeenJoined()); EXPECT_DCHECK_DEATH({ thread.Join(); }); } TEST(SimpleThreadTest, NonJoinableInactiveDelegateDestructionIsOkay) { std::unique_ptr runner(new ControlledRunner); SimpleThread::Options options; options.joinable = false; std::unique_ptr thread( new DelegateSimpleThread(runner.get(), "non_joinable", options)); thread->Start(); runner->WaitUntilStarted(); // Deleting a non-joinable SimpleThread after Run() was invoked is okay. thread.reset(); runner->WaitUntilStarted(); runner->ReleaseAndWaitUntilDone(); // It should be safe to destroy a Delegate after its Run() method completed. runner.reset(); } TEST(SimpleThreadTest, ThreadPool) { AtomicSequenceNumber seq; SeqRunner runner(&seq); DelegateSimpleThreadPool pool("seq_runner", 10); // Add work before we're running. pool.AddWork(&runner, 300); EXPECT_EQ(seq.GetNext(), 0); pool.Start(); // Add work while we're running. pool.AddWork(&runner, 300); pool.JoinAll(); EXPECT_EQ(seq.GetNext(), 601); // We can reuse our pool. Verify that all 10 threads can actually run in // parallel, so this test will only pass if there are actually 10 threads. AtomicSequenceNumber seq2; WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL, WaitableEvent::InitialState::NOT_SIGNALED); // Changing 9 to 10, for example, would cause us JoinAll() to never return. VerifyPoolRunner verifier(&seq2, 9, &event); pool.Start(); pool.AddWork(&verifier, 10); pool.JoinAll(); EXPECT_EQ(seq2.GetNext(), 10); } } // namespace base