michael@0: // Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #ifndef BASE_THREADING_THREAD_COLLISION_WARNER_H_ michael@0: #define BASE_THREADING_THREAD_COLLISION_WARNER_H_ michael@0: michael@0: #include michael@0: michael@0: #include "base/atomicops.h" michael@0: #include "base/base_export.h" michael@0: #include "base/compiler_specific.h" michael@0: michael@0: // A helper class alongside macros to be used to verify assumptions about thread michael@0: // safety of a class. michael@0: // michael@0: // Example: Queue implementation non thread-safe but still usable if clients michael@0: // are synchronized somehow. michael@0: // michael@0: // In this case the macro DFAKE_SCOPED_LOCK has to be michael@0: // used, it checks that if a thread is inside the push/pop then michael@0: // noone else is still inside the pop/push michael@0: // michael@0: // class NonThreadSafeQueue { michael@0: // public: michael@0: // ... michael@0: // void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... } michael@0: // int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... } michael@0: // ... michael@0: // private: michael@0: // DFAKE_MUTEX(push_pop_); michael@0: // }; michael@0: // michael@0: // michael@0: // Example: Queue implementation non thread-safe but still usable if clients michael@0: // are synchronized somehow, it calls a method to "protect" from michael@0: // a "protected" method michael@0: // michael@0: // In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK michael@0: // has to be used, it checks that if a thread is inside the push/pop michael@0: // then noone else is still inside the pop/push michael@0: // michael@0: // class NonThreadSafeQueue { michael@0: // public: michael@0: // void push(int) { michael@0: // DFAKE_SCOPED_LOCK(push_pop_); michael@0: // ... michael@0: // } michael@0: // int pop() { michael@0: // DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); michael@0: // bar(); michael@0: // ... michael@0: // } michael@0: // void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... } michael@0: // ... michael@0: // private: michael@0: // DFAKE_MUTEX(push_pop_); michael@0: // }; michael@0: // michael@0: // michael@0: // Example: Queue implementation not usable even if clients are synchronized, michael@0: // so only one thread in the class life cycle can use the two members michael@0: // push/pop. michael@0: // michael@0: // In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the michael@0: // specified michael@0: // critical section the first time a thread enters push or pop, from michael@0: // that time on only that thread is allowed to execute push or pop. michael@0: // michael@0: // class NonThreadSafeQueue { michael@0: // public: michael@0: // ... michael@0: // void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } michael@0: // int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } michael@0: // ... michael@0: // private: michael@0: // DFAKE_MUTEX(push_pop_); michael@0: // }; michael@0: // michael@0: // michael@0: // Example: Class that has to be contructed/destroyed on same thread, it has michael@0: // a "shareable" method (with external synchronization) and a not michael@0: // shareable method (even with external synchronization). michael@0: // michael@0: // In this case 3 Critical sections have to be defined michael@0: // michael@0: // class ExoticClass { michael@0: // public: michael@0: // ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } michael@0: // ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } michael@0: // michael@0: // void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... } michael@0: // void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } michael@0: // ... michael@0: // private: michael@0: // DFAKE_MUTEX(ctor_dtor_); michael@0: // DFAKE_MUTEX(shareable_section_); michael@0: // }; michael@0: michael@0: michael@0: #if !defined(NDEBUG) michael@0: michael@0: // Defines a class member that acts like a mutex. It is used only as a michael@0: // verification tool. michael@0: #define DFAKE_MUTEX(obj) \ michael@0: mutable base::ThreadCollisionWarner obj michael@0: // Asserts the call is never called simultaneously in two threads. Used at michael@0: // member function scope. michael@0: #define DFAKE_SCOPED_LOCK(obj) \ michael@0: base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj) michael@0: // Asserts the call is never called simultaneously in two threads. Used at michael@0: // member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. michael@0: #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ michael@0: base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj) michael@0: // Asserts the code is always executed in the same thread. michael@0: #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ michael@0: base::ThreadCollisionWarner::Check check_##obj(&obj) michael@0: michael@0: #else michael@0: michael@0: #define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##obj michael@0: #define DFAKE_SCOPED_LOCK(obj) ((void)0) michael@0: #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0) michael@0: #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0) michael@0: michael@0: #endif michael@0: michael@0: namespace base { michael@0: michael@0: // The class ThreadCollisionWarner uses an Asserter to notify the collision michael@0: // AsserterBase is the interfaces and DCheckAsserter is the default asserter michael@0: // used. During the unit tests is used another class that doesn't "DCHECK" michael@0: // in case of collision (check thread_collision_warner_unittests.cc) michael@0: struct BASE_EXPORT AsserterBase { michael@0: virtual ~AsserterBase() {} michael@0: virtual void warn() = 0; michael@0: }; michael@0: michael@0: struct BASE_EXPORT DCheckAsserter : public AsserterBase { michael@0: virtual ~DCheckAsserter() {} michael@0: virtual void warn() OVERRIDE; michael@0: }; michael@0: michael@0: class BASE_EXPORT ThreadCollisionWarner { michael@0: public: michael@0: // The parameter asserter is there only for test purpose michael@0: explicit ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()) michael@0: : valid_thread_id_(0), michael@0: counter_(0), michael@0: asserter_(asserter) {} michael@0: michael@0: ~ThreadCollisionWarner() { michael@0: delete asserter_; michael@0: } michael@0: michael@0: // This class is meant to be used through the macro michael@0: // DFAKE_SCOPED_LOCK_THREAD_LOCKED michael@0: // it doesn't leave the critical section, as opposed to ScopedCheck, michael@0: // because the critical section being pinned is allowed to be used only michael@0: // from one thread michael@0: class BASE_EXPORT Check { michael@0: public: michael@0: explicit Check(ThreadCollisionWarner* warner) michael@0: : warner_(warner) { michael@0: warner_->EnterSelf(); michael@0: } michael@0: michael@0: ~Check() {} michael@0: michael@0: private: michael@0: ThreadCollisionWarner* warner_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(Check); michael@0: }; michael@0: michael@0: // This class is meant to be used through the macro michael@0: // DFAKE_SCOPED_LOCK michael@0: class BASE_EXPORT ScopedCheck { michael@0: public: michael@0: explicit ScopedCheck(ThreadCollisionWarner* warner) michael@0: : warner_(warner) { michael@0: warner_->Enter(); michael@0: } michael@0: michael@0: ~ScopedCheck() { michael@0: warner_->Leave(); michael@0: } michael@0: michael@0: private: michael@0: ThreadCollisionWarner* warner_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(ScopedCheck); michael@0: }; michael@0: michael@0: // This class is meant to be used through the macro michael@0: // DFAKE_SCOPED_RECURSIVE_LOCK michael@0: class BASE_EXPORT ScopedRecursiveCheck { michael@0: public: michael@0: explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) michael@0: : warner_(warner) { michael@0: warner_->EnterSelf(); michael@0: } michael@0: michael@0: ~ScopedRecursiveCheck() { michael@0: warner_->Leave(); michael@0: } michael@0: michael@0: private: michael@0: ThreadCollisionWarner* warner_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck); michael@0: }; michael@0: michael@0: private: michael@0: // This method stores the current thread identifier and does a DCHECK michael@0: // if a another thread has already done it, it is safe if same thread michael@0: // calls this multiple time (recursion allowed). michael@0: void EnterSelf(); michael@0: michael@0: // Same as EnterSelf but recursion is not allowed. michael@0: void Enter(); michael@0: michael@0: // Removes the thread_id stored in order to allow other threads to michael@0: // call EnterSelf or Enter. michael@0: void Leave(); michael@0: michael@0: // This stores the thread id that is inside the critical section, if the michael@0: // value is 0 then no thread is inside. michael@0: volatile subtle::Atomic32 valid_thread_id_; michael@0: michael@0: // Counter to trace how many time a critical section was "pinned" michael@0: // (when allowed) in order to unpin it when counter_ reaches 0. michael@0: volatile subtle::Atomic32 counter_; michael@0: michael@0: // Here only for class unit tests purpose, during the test I need to not michael@0: // DCHECK but notify the collision with something else. michael@0: AsserterBase* asserter_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner); michael@0: }; michael@0: michael@0: } // namespace base michael@0: michael@0: #endif // BASE_THREADING_THREAD_COLLISION_WARNER_H_