tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

thread_collision_warner.h (7877B)


      1 // Copyright 2012 The Chromium Authors
      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 "base/atomicops.h"
      9 #include "base/base_export.h"
     10 #include "base/compiler_specific.h"
     11 #include "base/macros/uniquify.h"
     12 #include "base/memory/raw_ptr.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 synchronization) 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 #if !defined(NDEBUG)
    101 
    102 // Defines a class member that acts like a mutex. It is used only as a
    103 // verification tool.
    104 #define DFAKE_MUTEX(obj) \
    105     mutable base::ThreadCollisionWarner obj
    106 // Asserts the call is never called simultaneously in two threads. Used at
    107 // member function scope.
    108 #define DFAKE_SCOPED_LOCK(obj) \
    109  base::ThreadCollisionWarner::ScopedCheck BASE_UNIQUIFY(s_check_)(&obj)
    110 // Asserts the call is never called simultaneously in two threads. Used at
    111 // member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks.
    112 #define DFAKE_SCOPED_RECURSIVE_LOCK(obj)                                     \
    113  base::ThreadCollisionWarner::ScopedRecursiveCheck BASE_UNIQUIFY(sr_check)( \
    114      &obj)
    115 // Asserts the code is always executed in the same thread.
    116 #define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \
    117  base::ThreadCollisionWarner::Check BASE_UNIQUIFY(check_)(&obj)
    118 
    119 #else
    120 
    121 #define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##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_EXPORT AsserterBase {
    135  virtual ~AsserterBase() = default;
    136  virtual void warn() = 0;
    137 };
    138 
    139 struct BASE_EXPORT DCheckAsserter : public AsserterBase {
    140  ~DCheckAsserter() override = default;
    141  void warn() override;
    142 };
    143 
    144 class BASE_EXPORT ThreadCollisionWarner {
    145 public:
    146  // The parameter asserter is there only for test purpose
    147  explicit ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter())
    148      : valid_thread_id_(0),
    149        counter_(0),
    150        asserter_(asserter) {}
    151 
    152  ThreadCollisionWarner(const ThreadCollisionWarner&) = delete;
    153  ThreadCollisionWarner& operator=(const ThreadCollisionWarner&) = delete;
    154 
    155  ~ThreadCollisionWarner() { asserter_.ClearAndDelete(); }
    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(const Check&) = delete;
    170    Check& operator=(const Check&) = delete;
    171 
    172    ~Check() = default;
    173 
    174   private:
    175    raw_ptr<ThreadCollisionWarner> warner_;
    176  };
    177 
    178  // This class is meant to be used through the macro
    179  // DFAKE_SCOPED_LOCK
    180  class BASE_EXPORT ScopedCheck {
    181   public:
    182    explicit ScopedCheck(ThreadCollisionWarner* warner)
    183        : warner_(warner) {
    184      warner_->Enter();
    185    }
    186 
    187    ScopedCheck(const ScopedCheck&) = delete;
    188    ScopedCheck& operator=(const ScopedCheck&) = delete;
    189 
    190    ~ScopedCheck() {
    191      warner_->Leave();
    192    }
    193 
    194   private:
    195    raw_ptr<ThreadCollisionWarner> warner_;
    196  };
    197 
    198  // This class is meant to be used through the macro
    199  // DFAKE_SCOPED_RECURSIVE_LOCK
    200  class BASE_EXPORT ScopedRecursiveCheck {
    201   public:
    202    explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner)
    203        : warner_(warner) {
    204      warner_->EnterSelf();
    205    }
    206 
    207    ScopedRecursiveCheck(const ScopedRecursiveCheck&) = delete;
    208    ScopedRecursiveCheck& operator=(const ScopedRecursiveCheck&) = delete;
    209 
    210    ~ScopedRecursiveCheck() {
    211      warner_->Leave();
    212    }
    213 
    214   private:
    215    raw_ptr<ThreadCollisionWarner> warner_;
    216  };
    217 
    218 private:
    219  // This method stores the current thread identifier and does a DCHECK
    220  // if a another thread has already done it, it is safe if same thread
    221  // calls this multiple time (recursion allowed).
    222  void EnterSelf();
    223 
    224  // Same as EnterSelf but recursion is not allowed.
    225  void Enter();
    226 
    227  // Removes the thread_id stored in order to allow other threads to
    228  // call EnterSelf or Enter.
    229  void Leave();
    230 
    231  // This stores the thread id that is inside the critical section, if the
    232  // value is 0 then no thread is inside.
    233  volatile subtle::Atomic32 valid_thread_id_;
    234 
    235  // Counter to trace how many time a critical section was "pinned"
    236  // (when allowed) in order to unpin it when counter_ reaches 0.
    237  volatile subtle::Atomic32 counter_;
    238 
    239  // Here only for class unit tests purpose, during the test I need to not
    240  // DCHECK but notify the collision with something else.
    241  raw_ptr<AsserterBase> asserter_;
    242 };
    243 
    244 }  // namespace base
    245 
    246 #endif  // BASE_THREADING_THREAD_COLLISION_WARNER_H_