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