Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef BASE_THREAD_COLLISION_WARNER_H_
      6 #define BASE_THREAD_COLLISION_WARNER_H_
      7 
      8 #include <memory>
      9 
     10 #include "base/atomicops.h"
     11 
     12 // A helper class alongside macros to be used to verify assumptions about thread
     13 // safety of a class.
     14 //
     15 // Example: Queue implementation non thread-safe but still usable if clients
     16 //          are synchronized somehow.
     17 //
     18 //          In this case the macro DFAKE_SCOPED_LOCK has to be
     19 //          used, it checks that if a thread is inside the push/pop then
     20 //          noone else is still inside the pop/push
     21 //
     22 // class NonThreadSafeQueue {
     23 //  public:
     24 //   ...
     25 //   void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... }
     26 //   int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... }
     27 //   ...
     28 //  private:
     29 //   DFAKE_MUTEX(push_pop_);
     30 // };
     31 //
     32 //
     33 // Example: Queue implementation non thread-safe but still usable if clients
     34 //          are synchronized somehow, it calls a method to "protect" from
     35 //          a "protected" method
     36 //
     37 //          In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK
     38 //          has to be used, it checks that if a thread is inside the push/pop
     39 //          then noone else is still inside the pop/push
     40 //
     41 // class NonThreadSafeQueue {
     42 //  public:
     43 //   void push(int) {
     44 //     DFAKE_SCOPED_LOCK(push_pop_);
     45 //     ...
     46 //   }
     47 //   int pop() {
     48 //     DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
     49 //     bar();
     50 //     ...
     51 //   }
     52 //   void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... }
     53 //   ...
     54 //  private:
     55 //   DFAKE_MUTEX(push_pop_);
     56 // };
     57 //
     58 //
     59 // Example: Queue implementation not usable even if clients are synchronized,
     60 //          so only one thread in the class life cycle can use the two members
     61 //          push/pop.
     62 //
     63 //          In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the
     64 //          specified
     65 //          critical section the first time a thread enters push or pop, from
     66 //          that time on only that thread is allowed to execute push or pop.
     67 //
     68 // class NonThreadSafeQueue {
     69 //  public:
     70 //   ...
     71 //   void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
     72 //   int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... }
     73 //   ...
     74 //  private:
     75 //   DFAKE_MUTEX(push_pop_);
     76 // };
     77 //
     78 //
     79 // Example: Class that has to be contructed/destroyed on same thread, it has
     80 //          a "shareable" method (with external syncronization) and a not
     81 //          shareable method (even with external synchronization).
     82 //
     83 //          In this case 3 Critical sections have to be defined
     84 //
     85 // class ExoticClass {
     86 //  public:
     87 //   ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
     88 //   ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
     89 //
     90 //   void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... }
     91 //   void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... }
     92 //   ...
     93 //  private:
     94 //   DFAKE_MUTEX(ctor_dtor_);
     95 //   DFAKE_MUTEX(shareable_section_);
     96 // };
     97 
     98 
     99 #if !defined(NDEBUG)
    100 
    101 // Defines a class member that acts like a mutex. It is used only as a
    102 // verification tool.
    103 #define DFAKE_MUTEX(obj) \
    104      mutable base::ThreadCollisionWarner obj
    105 // Asserts the call is never called simultaneously in two threads. Used at
    106 // member function scope.
    107 #define DFAKE_SCOPED_LOCK(obj) \
    108      base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj)
    109 // Asserts the call is never called simultaneously in two threads. Used at
    110 // member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks.
    111 #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \
    112      base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj)
    113 // Asserts the code is always executed in the same thread.
    114 #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \
    115      base::ThreadCollisionWarner::Check check_##obj(&obj)
    116 
    117 #else
    118 
    119 #define DFAKE_MUTEX(obj)
    120 #define DFAKE_SCOPED_LOCK(obj) ((void)0)
    121 #define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0)
    122 #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0)
    123 
    124 #endif
    125 
    126 namespace base {
    127 
    128 // The class ThreadCollisionWarner uses an Asserter to notify the collision
    129 // AsserterBase is the interfaces and DCheckAsserter is the default asserter
    130 // used. During the unit tests is used another class that doesn't "DCHECK"
    131 // in case of collision (check thread_collision_warner_unittests.cc)
    132 struct AsserterBase {
    133   virtual ~AsserterBase() {}
    134   virtual void warn() = 0;
    135 };
    136 
    137 struct DCheckAsserter : public AsserterBase {
    138   virtual ~DCheckAsserter() {}
    139   virtual void warn();
    140 };
    141 
    142 class ThreadCollisionWarner {
    143  public:
    144   // The parameter asserter is there only for test purpose
    145   ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter())
    146       : valid_thread_id_(0),
    147         counter_(0),
    148         asserter_(asserter) {}
    149 
    150   ~ThreadCollisionWarner() {
    151     delete asserter_;
    152   }
    153 
    154   // This class is meant to be used through the macro
    155   // DFAKE_SCOPED_LOCK_THREAD_LOCKED
    156   // it doesn't leave the critical section, as opposed to ScopedCheck,
    157   // because the critical section being pinned is allowed to be used only
    158   // from one thread
    159   class Check {
    160    public:
    161     explicit Check(ThreadCollisionWarner* warner)
    162         : warner_(warner) {
    163       warner_->EnterSelf();
    164     }
    165 
    166     ~Check() {}
    167 
    168    private:
    169     ThreadCollisionWarner* warner_;
    170 
    171     DISALLOW_COPY_AND_ASSIGN(Check);
    172   };
    173 
    174   // This class is meant to be used through the macro
    175   // DFAKE_SCOPED_LOCK
    176   class ScopedCheck {
    177    public:
    178     explicit ScopedCheck(ThreadCollisionWarner* warner)
    179         : warner_(warner) {
    180       warner_->Enter();
    181     }
    182 
    183     ~ScopedCheck() {
    184       warner_->Leave();
    185     }
    186 
    187    private:
    188     ThreadCollisionWarner* warner_;
    189 
    190     DISALLOW_COPY_AND_ASSIGN(ScopedCheck);
    191   };
    192 
    193   // This class is meant to be used through the macro
    194   // DFAKE_SCOPED_RECURSIVE_LOCK
    195   class ScopedRecursiveCheck {
    196    public:
    197     explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner)
    198         : warner_(warner) {
    199       warner_->EnterSelf();
    200     }
    201 
    202     ~ScopedRecursiveCheck() {
    203       warner_->Leave();
    204     }
    205 
    206    private:
    207     ThreadCollisionWarner* warner_;
    208 
    209     DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck);
    210   };
    211 
    212  private:
    213   // This method stores the current thread identifier and does a DCHECK
    214   // if a another thread has already done it, it is safe if same thread
    215   // calls this multiple time (recursion allowed).
    216   void EnterSelf();
    217 
    218   // Same as EnterSelf but recursion is not allowed.
    219   void Enter();
    220 
    221   // Removes the thread_id stored in order to allow other threads to
    222   // call EnterSelf or Enter.
    223   void Leave();
    224 
    225   // This stores the thread id that is inside the critical section, if the
    226   // value is 0 then no thread is inside.
    227   volatile subtle::Atomic32 valid_thread_id_;
    228 
    229   // Counter to trace how many time a critical section was "pinned"
    230   // (when allowed) in order to unpin it when counter_ reaches 0.
    231   volatile subtle::Atomic32 counter_;
    232 
    233   // Here only for class unit tests purpose, during the test I need to not
    234   // DCHECK but notify the collision with something else.
    235   AsserterBase* asserter_;
    236 
    237   DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner);
    238 };
    239 
    240 }  // namespace base
    241 
    242 #endif  // BASE_THREAD_COLLISION_WARNER_H_
    243