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