| // Copyright 2013 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/sequence_checker.h" | |
| #include <memory> | |
| #include <string> | |
| #include "base/bind.h" | |
| #include "base/bind_helpers.h" | |
| #include "base/callback_forward.h" | |
| #include "base/macros.h" | |
| #include "base/sequence_token.h" | |
| #include "base/single_thread_task_runner.h" | |
| #include "base/test/gtest_util.h" | |
| #include "base/threading/simple_thread.h" | |
| #include "starboard/types.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| namespace base { | |
| namespace { | |
| // Runs a callback on another thread. | |
| class RunCallbackThread : public SimpleThread { | |
| public: | |
| explicit RunCallbackThread(const Closure& callback) | |
| : SimpleThread("RunCallbackThread"), callback_(callback) { | |
| Start(); | |
| Join(); | |
| } | |
| private: | |
| // SimpleThread: | |
| void Run() override { callback_.Run(); } | |
| const Closure callback_; | |
| DISALLOW_COPY_AND_ASSIGN(RunCallbackThread); | |
| }; | |
| void ExpectCalledOnValidSequence(SequenceCheckerImpl* sequence_checker) { | |
| ASSERT_TRUE(sequence_checker); | |
| // This should bind |sequence_checker| to the current sequence if it wasn't | |
| // already bound to a sequence. | |
| EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); | |
| // Since |sequence_checker| is now bound to the current sequence, another call | |
| // to CalledOnValidSequence() should return true. | |
| EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); | |
| } | |
| void ExpectCalledOnValidSequenceWithSequenceToken( | |
| SequenceCheckerImpl* sequence_checker, | |
| SequenceToken sequence_token) { | |
| ScopedSetSequenceTokenForCurrentThread | |
| scoped_set_sequence_token_for_current_thread(sequence_token); | |
| ExpectCalledOnValidSequence(sequence_checker); | |
| } | |
| void ExpectNotCalledOnValidSequence(SequenceCheckerImpl* sequence_checker) { | |
| ASSERT_TRUE(sequence_checker); | |
| EXPECT_FALSE(sequence_checker->CalledOnValidSequence()); | |
| } | |
| } // namespace | |
| TEST(SequenceCheckerTest, CallsAllowedOnSameThreadNoSequenceToken) { | |
| SequenceCheckerImpl sequence_checker; | |
| EXPECT_TRUE(sequence_checker.CalledOnValidSequence()); | |
| } | |
| TEST(SequenceCheckerTest, CallsAllowedOnSameThreadSameSequenceToken) { | |
| ScopedSetSequenceTokenForCurrentThread | |
| scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | |
| SequenceCheckerImpl sequence_checker; | |
| EXPECT_TRUE(sequence_checker.CalledOnValidSequence()); | |
| } | |
| TEST(SequenceCheckerTest, CallsDisallowedOnDifferentThreadsNoSequenceToken) { | |
| SequenceCheckerImpl sequence_checker; | |
| RunCallbackThread thread( | |
| Bind(&ExpectNotCalledOnValidSequence, Unretained(&sequence_checker))); | |
| } | |
| TEST(SequenceCheckerTest, CallsAllowedOnDifferentThreadsSameSequenceToken) { | |
| const SequenceToken sequence_token(SequenceToken::Create()); | |
| ScopedSetSequenceTokenForCurrentThread | |
| scoped_set_sequence_token_for_current_thread(sequence_token); | |
| SequenceCheckerImpl sequence_checker; | |
| EXPECT_TRUE(sequence_checker.CalledOnValidSequence()); | |
| RunCallbackThread thread(Bind(&ExpectCalledOnValidSequenceWithSequenceToken, | |
| Unretained(&sequence_checker), sequence_token)); | |
| } | |
| TEST(SequenceCheckerTest, CallsDisallowedOnSameThreadDifferentSequenceToken) { | |
| std::unique_ptr<SequenceCheckerImpl> sequence_checker; | |
| { | |
| ScopedSetSequenceTokenForCurrentThread | |
| scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | |
| sequence_checker.reset(new SequenceCheckerImpl); | |
| } | |
| { | |
| // Different SequenceToken. | |
| ScopedSetSequenceTokenForCurrentThread | |
| scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | |
| EXPECT_FALSE(sequence_checker->CalledOnValidSequence()); | |
| } | |
| // No SequenceToken. | |
| EXPECT_FALSE(sequence_checker->CalledOnValidSequence()); | |
| } | |
| TEST(SequenceCheckerTest, DetachFromSequence) { | |
| std::unique_ptr<SequenceCheckerImpl> sequence_checker; | |
| { | |
| ScopedSetSequenceTokenForCurrentThread | |
| scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | |
| sequence_checker.reset(new SequenceCheckerImpl); | |
| } | |
| sequence_checker->DetachFromSequence(); | |
| { | |
| // Verify that CalledOnValidSequence() returns true when called with | |
| // a different sequence token after a call to DetachFromSequence(). | |
| ScopedSetSequenceTokenForCurrentThread | |
| scoped_set_sequence_token_for_current_thread(SequenceToken::Create()); | |
| EXPECT_TRUE(sequence_checker->CalledOnValidSequence()); | |
| } | |
| } | |
| TEST(SequenceCheckerTest, DetachFromSequenceNoSequenceToken) { | |
| SequenceCheckerImpl sequence_checker; | |
| sequence_checker.DetachFromSequence(); | |
| // Verify that CalledOnValidSequence() returns true when called on a | |
| // different thread after a call to DetachFromSequence(). | |
| RunCallbackThread thread( | |
| Bind(&ExpectCalledOnValidSequence, Unretained(&sequence_checker))); | |
| EXPECT_FALSE(sequence_checker.CalledOnValidSequence()); | |
| } | |
| TEST(SequenceCheckerMacroTest, Macros) { | |
| auto scope = std::make_unique<ScopedSetSequenceTokenForCurrentThread>( | |
| SequenceToken::Create()); | |
| SEQUENCE_CHECKER(my_sequence_checker); | |
| // Don't expect a DCHECK death when a SequenceChecker is used on the right | |
| // sequence. | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker) << "Error message."; | |
| scope.reset(); | |
| #if DCHECK_IS_ON() | |
| // Expect DCHECK death when used on a different sequence. | |
| EXPECT_DCHECK_DEATH({ | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker) << "Error message."; | |
| }); | |
| #else | |
| // Happily no-ops on non-dcheck builds. | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker) << "Error message."; | |
| #endif | |
| DETACH_FROM_SEQUENCE(my_sequence_checker); | |
| // Don't expect a DCHECK death when a SequenceChecker is used for the first | |
| // time after having been detached. | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker) << "Error message."; | |
| } | |
| } // namespace base |