// 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/logging.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/threading/simple_thread.h" #include "base/threading/thread_checker.h" #include "testing/gtest/include/gtest/gtest.h" // Duplicated from base/threading/thread_checker.h so that we can be // good citizens there and undef the macro. #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) #define ENABLE_THREAD_CHECKER 1 #else #define ENABLE_THREAD_CHECKER 0 #endif namespace base { namespace { // Simple class to exercise the basics of ThreadChecker. // Both the destructor and DoStuff should verify that they were // called on the same thread as the constructor. class ThreadCheckerClass : public ThreadChecker { public: ThreadCheckerClass() {} // Verifies that it was called on the same thread as the constructor. void DoStuff() { DCHECK(CalledOnValidThread()); } void DetachFromThread() { ThreadChecker::DetachFromThread(); } static void MethodOnDifferentThreadImpl(); static void DetachThenCallFromDifferentThreadImpl(); private: DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass); }; // Calls ThreadCheckerClass::DoStuff on another thread. class CallDoStuffOnThread : public base::SimpleThread { public: explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class) : SimpleThread("call_do_stuff_on_thread"), thread_checker_class_(thread_checker_class) { } void Run() override { thread_checker_class_->DoStuff(); } private: ThreadCheckerClass* thread_checker_class_; DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread); }; // Deletes ThreadCheckerClass on a different thread. class DeleteThreadCheckerClassOnThread : public base::SimpleThread { public: explicit DeleteThreadCheckerClassOnThread( ThreadCheckerClass* thread_checker_class) : SimpleThread("delete_thread_checker_class_on_thread"), thread_checker_class_(thread_checker_class) { } void Run() override { thread_checker_class_.reset(); } private: scoped_ptr<ThreadCheckerClass> thread_checker_class_; DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread); }; } // namespace TEST(ThreadCheckerTest, CallsAllowedOnSameThread) { scoped_ptr<ThreadCheckerClass> thread_checker_class( new ThreadCheckerClass); // Verify that DoStuff doesn't assert. thread_checker_class->DoStuff(); // Verify that the destructor doesn't assert. thread_checker_class.reset(); } TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) { scoped_ptr<ThreadCheckerClass> thread_checker_class( new ThreadCheckerClass); // Verify that the destructor doesn't assert // when called on a different thread. DeleteThreadCheckerClassOnThread delete_on_thread( thread_checker_class.release()); delete_on_thread.Start(); delete_on_thread.Join(); } TEST(ThreadCheckerTest, DetachFromThread) { scoped_ptr<ThreadCheckerClass> thread_checker_class( new ThreadCheckerClass); // Verify that DoStuff doesn't assert when called on a different thread after // a call to DetachFromThread. thread_checker_class->DetachFromThread(); CallDoStuffOnThread call_on_thread(thread_checker_class.get()); call_on_thread.Start(); call_on_thread.Join(); } #if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER void ThreadCheckerClass::MethodOnDifferentThreadImpl() { scoped_ptr<ThreadCheckerClass> thread_checker_class( new ThreadCheckerClass); // DoStuff should assert in debug builds only when called on a // different thread. CallDoStuffOnThread call_on_thread(thread_checker_class.get()); call_on_thread.Start(); call_on_thread.Join(); } #if ENABLE_THREAD_CHECKER TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) { ASSERT_DEATH({ ThreadCheckerClass::MethodOnDifferentThreadImpl(); }, ""); } #else TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) { ThreadCheckerClass::MethodOnDifferentThreadImpl(); } #endif // ENABLE_THREAD_CHECKER void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() { scoped_ptr<ThreadCheckerClass> thread_checker_class( new ThreadCheckerClass); // DoStuff doesn't assert when called on a different thread // after a call to DetachFromThread. thread_checker_class->DetachFromThread(); CallDoStuffOnThread call_on_thread(thread_checker_class.get()); call_on_thread.Start(); call_on_thread.Join(); // DoStuff should assert in debug builds only after moving to // another thread. thread_checker_class->DoStuff(); } #if ENABLE_THREAD_CHECKER TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) { ASSERT_DEATH({ ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); }, ""); } #else TEST(ThreadCheckerTest, DetachFromThreadInRelease) { ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); } #endif // ENABLE_THREAD_CHECKER #endif // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER // Just in case we ever get lumped together with other compilation units. #undef ENABLE_THREAD_CHECKER } // namespace base