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