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
128namespace 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)
134struct BASE_API AsserterBase {
135  virtual ~AsserterBase() {}
136  virtual void warn() = 0;
137};
138
139struct BASE_API DCheckAsserter : public AsserterBase {
140  virtual ~DCheckAsserter() {}
141  virtual void warn();
142};
143
144class 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