Index: base/threading/simple_thread_unittest.cc |
diff --git a/base/threading/simple_thread_unittest.cc b/base/threading/simple_thread_unittest.cc |
index 14dd4591f1819d8e505911cd9f4cb9fb09cd54f2..74e922dde63c6926387218cba31ef633c88b14b6 100644 |
--- a/base/threading/simple_thread_unittest.cc |
+++ b/base/threading/simple_thread_unittest.cc |
@@ -2,9 +2,13 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include <memory> |
+ |
#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" |
@@ -17,11 +21,49 @@ class SetIntRunner : public DelegateSimpleThread::Delegate { |
SetIntRunner(int* ptr, int val) : ptr_(ptr), val_(val) { } |
~SetIntRunner() override {} |
+ private: |
void Run() override { *ptr_ = val_; } |
- private: |
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() { 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 { |
@@ -29,22 +71,28 @@ class WaitEventRunner : public DelegateSimpleThread::Delegate { |
explicit WaitEventRunner(WaitableEvent* event) : event_(event) { } |
~WaitEventRunner() override {} |
+ private: |
void Run() override { |
EXPECT_FALSE(event_->IsSignaled()); |
event_->Signal(); |
EXPECT_TRUE(event_->IsSignaled()); |
} |
- private: |
+ |
WaitableEvent* event_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WaitEventRunner); |
}; |
class SeqRunner : public DelegateSimpleThread::Delegate { |
public: |
explicit SeqRunner(AtomicSequenceNumber* seq) : seq_(seq) { } |
- void Run() override { seq_->GetNext(); } |
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 |
@@ -56,6 +104,7 @@ class VerifyPoolRunner : public DelegateSimpleThread::Delegate { |
int total, WaitableEvent* event) |
: seq_(seq), total_(total), event_(event) { } |
+ private: |
void Run() override { |
if (seq_->GetNext() == total_) { |
event_->Signal(); |
@@ -64,10 +113,11 @@ class VerifyPoolRunner : public DelegateSimpleThread::Delegate { |
} |
} |
- private: |
AtomicSequenceNumber* seq_; |
int total_; |
WaitableEvent* event_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(VerifyPoolRunner); |
}; |
} // namespace |
@@ -133,6 +183,45 @@ TEST(SimpleThreadTest, NamedWithOptions) { |
std::string("event_waiter/") + IntToString(thread.tid())); |
} |
+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<ControlledRunner> runner(new ControlledRunner); |
+ |
+ SimpleThread::Options options; |
+ options.joinable = false; |
+ std::unique_ptr<DelegateSimpleThread> thread( |
+ new DelegateSimpleThread(runner.get(), "non_joinable", options)); |
+ |
+ thread->Start(); |
+ |
+ // Deleting a non-joinable SimpleThread after Start() 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); |