YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

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