|  | // 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/basictypes.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/scoped_ptr.h" | 
|  | #include "base/threading/non_thread_safe.h" | 
|  | #include "base/threading/simple_thread.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | // Duplicated from base/threading/non_thread_safe.h so that we can be | 
|  | // good citizens there and undef the macro. | 
|  | #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) | 
|  | #define ENABLE_NON_THREAD_SAFE 1 | 
|  | #else | 
|  | #define ENABLE_NON_THREAD_SAFE 0 | 
|  | #endif | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Simple class to exersice the basics of NonThreadSafe. | 
|  | // Both the destructor and DoStuff should verify that they were | 
|  | // called on the same thread as the constructor. | 
|  | class NonThreadSafeClass : public NonThreadSafe { | 
|  | public: | 
|  | NonThreadSafeClass() {} | 
|  |  | 
|  | // Verifies that it was called on the same thread as the constructor. | 
|  | void DoStuff() { | 
|  | DCHECK(CalledOnValidThread()); | 
|  | } | 
|  |  | 
|  | void DetachFromThread() { | 
|  | NonThreadSafe::DetachFromThread(); | 
|  | } | 
|  |  | 
|  | static void MethodOnDifferentThreadImpl(); | 
|  | static void DestructorOnDifferentThreadImpl(); | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(NonThreadSafeClass); | 
|  | }; | 
|  |  | 
|  | // Calls NonThreadSafeClass::DoStuff on another thread. | 
|  | class CallDoStuffOnThread : public SimpleThread { | 
|  | public: | 
|  | CallDoStuffOnThread(NonThreadSafeClass* non_thread_safe_class) | 
|  | : SimpleThread("call_do_stuff_on_thread"), | 
|  | non_thread_safe_class_(non_thread_safe_class) { | 
|  | } | 
|  |  | 
|  | virtual void Run() OVERRIDE { | 
|  | non_thread_safe_class_->DoStuff(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | NonThreadSafeClass* non_thread_safe_class_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread); | 
|  | }; | 
|  |  | 
|  | // Deletes NonThreadSafeClass on a different thread. | 
|  | class DeleteNonThreadSafeClassOnThread : public SimpleThread { | 
|  | public: | 
|  | DeleteNonThreadSafeClassOnThread(NonThreadSafeClass* non_thread_safe_class) | 
|  | : SimpleThread("delete_non_thread_safe_class_on_thread"), | 
|  | non_thread_safe_class_(non_thread_safe_class) { | 
|  | } | 
|  |  | 
|  | virtual void Run() OVERRIDE { | 
|  | non_thread_safe_class_.reset(); | 
|  | } | 
|  |  | 
|  | private: | 
|  | scoped_ptr<NonThreadSafeClass> non_thread_safe_class_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(DeleteNonThreadSafeClassOnThread); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST(NonThreadSafeTest, CallsAllowedOnSameThread) { | 
|  | scoped_ptr<NonThreadSafeClass> non_thread_safe_class( | 
|  | new NonThreadSafeClass); | 
|  |  | 
|  | // Verify that DoStuff doesn't assert. | 
|  | non_thread_safe_class->DoStuff(); | 
|  |  | 
|  | // Verify that the destructor doesn't assert. | 
|  | non_thread_safe_class.reset(); | 
|  | } | 
|  |  | 
|  | TEST(NonThreadSafeTest, DetachThenDestructOnDifferentThread) { | 
|  | scoped_ptr<NonThreadSafeClass> non_thread_safe_class( | 
|  | new NonThreadSafeClass); | 
|  |  | 
|  | // Verify that the destructor doesn't assert when called on a different thread | 
|  | // after a detach. | 
|  | non_thread_safe_class->DetachFromThread(); | 
|  | DeleteNonThreadSafeClassOnThread delete_on_thread( | 
|  | non_thread_safe_class.release()); | 
|  |  | 
|  | delete_on_thread.Start(); | 
|  | delete_on_thread.Join(); | 
|  | } | 
|  |  | 
|  | #if GTEST_HAS_DEATH_TEST || !ENABLE_NON_THREAD_SAFE | 
|  |  | 
|  | void NonThreadSafeClass::MethodOnDifferentThreadImpl() { | 
|  | scoped_ptr<NonThreadSafeClass> non_thread_safe_class( | 
|  | new NonThreadSafeClass); | 
|  |  | 
|  | // Verify that DoStuff asserts in debug builds only when called | 
|  | // on a different thread. | 
|  | CallDoStuffOnThread call_on_thread(non_thread_safe_class.get()); | 
|  |  | 
|  | call_on_thread.Start(); | 
|  | call_on_thread.Join(); | 
|  | } | 
|  |  | 
|  | #if ENABLE_NON_THREAD_SAFE | 
|  | TEST(NonThreadSafeDeathTest, MethodNotAllowedOnDifferentThreadInDebug) { | 
|  | ASSERT_DEATH({ | 
|  | NonThreadSafeClass::MethodOnDifferentThreadImpl(); | 
|  | }, ""); | 
|  | } | 
|  | #else | 
|  | TEST(NonThreadSafeTest, MethodAllowedOnDifferentThreadInRelease) { | 
|  | NonThreadSafeClass::MethodOnDifferentThreadImpl(); | 
|  | } | 
|  | #endif  // ENABLE_NON_THREAD_SAFE | 
|  |  | 
|  | void NonThreadSafeClass::DestructorOnDifferentThreadImpl() { | 
|  | scoped_ptr<NonThreadSafeClass> non_thread_safe_class( | 
|  | new NonThreadSafeClass); | 
|  |  | 
|  | // Verify that the destructor asserts in debug builds only | 
|  | // when called on a different thread. | 
|  | DeleteNonThreadSafeClassOnThread delete_on_thread( | 
|  | non_thread_safe_class.release()); | 
|  |  | 
|  | delete_on_thread.Start(); | 
|  | delete_on_thread.Join(); | 
|  | } | 
|  |  | 
|  | #if ENABLE_NON_THREAD_SAFE | 
|  | TEST(NonThreadSafeDeathTest, DestructorNotAllowedOnDifferentThreadInDebug) { | 
|  | ASSERT_DEATH({ | 
|  | NonThreadSafeClass::DestructorOnDifferentThreadImpl(); | 
|  | }, ""); | 
|  | } | 
|  | #else | 
|  | TEST(NonThreadSafeTest, DestructorAllowedOnDifferentThreadInRelease) { | 
|  | NonThreadSafeClass::DestructorOnDifferentThreadImpl(); | 
|  | } | 
|  | #endif  // ENABLE_NON_THREAD_SAFE | 
|  |  | 
|  | #endif  // GTEST_HAS_DEATH_TEST || !ENABLE_NON_THREAD_SAFE | 
|  |  | 
|  | // Just in case we ever get lumped together with other compilation units. | 
|  | #undef ENABLE_NON_THREAD_SAFE | 
|  |  | 
|  | }  // namespace base |