MySQL 8.3.0
Source Code Documentation
resource_blocker.h
Go to the documentation of this file.
1#ifndef RPL_RESOURCE_BLOCKER_H_INCLUDED
2#define RPL_RESOURCE_BLOCKER_H_INCLUDED
3
4/* Copyright (c) 2023, Oracle and/or its affiliates.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License, version 2.0,
8 as published by the Free Software Foundation.
9
10 This program is also distributed with certain software (including
11 but not limited to OpenSSL) that is licensed under separate terms,
12 as designated in a particular file or component or in included license
13 documentation. The authors of MySQL hereby grant you an additional
14 permission to link the program and your derivative works with the
15 separately licensed software that they have included with MySQL.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License, version 2.0, for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
25
26#include <string.h> // std::string
27#include <set> // std::set
28#include "map_helpers.h" // std::atomic
29#include "mutex_lock.h" // Mutex_lock
30
32
33/// Represents a "Resource" which can be either "used" by a number of
34/// threads, or "blocked" by a number of threads. When one or more
35/// threads use the resource, no thread can block it. When a one or
36/// more threads is blocking the resource, no thread can use it. Each
37/// blocker provides a message that explains why the resource is
38/// blocked.
39///
40/// The resource is blocked or used by creating instances of the
41/// Blocker and User classes.
42///
43/// @code
44/// Resource museum;
45///
46/// // If the museum is open, visit it. This may be called by multiple threads.
47/// void visit_museum() {
48/// User user(museum);
49/// if (!user) return;
50/// // walk around the museum
51/// }
52///
53/// // If there are no guests in the museum, close it temporarily for
54/// // maintenance.
55/// void close_museum_for_maintenance() {
56/// Blocker blocker(museum, "Museum is currently closed for maintenance.");
57/// if (!blocker) return;
58/// // repair exhibitions
59/// }
60///
61/// // If there are no guests in the museum, close it temporarily for
62/// // cleaning.
63/// void close_museum_for_cleaning() {
64/// Blocker blocker(museum, "Museum is currently closed for cleaning.");
65/// if (!blocker) return;
66/// // clean the floors
67/// }
68/// @endcode
69class Resource {
70 public:
71 using String_set = std::set<std::string>;
72
75 }
76
78 assert(m_user_count == 0);
79 assert(m_block_reasons.empty());
81 }
82
83 Resource(Resource &other) = delete;
84 Resource(Resource &&other) = delete;
85 Resource &operator=(Resource const &other) = delete;
86 Resource &operator=(Resource &&other) = delete;
87
88 private:
89 /// Try to block the resource from being used, for the given reason.
90 [[nodiscard]] int try_block(const std::string &reason) {
91 MUTEX_LOCK(guard, &get_lock());
92 if (m_user_count == 0) {
93 m_block_reasons.insert(reason);
94 }
95 return m_user_count;
96 }
97
98 /// Unblock the resource if try_block was previously called successfully.
99 void end_block(const std::string &reason) {
100 MUTEX_LOCK(guard, &get_lock());
101 [[maybe_unused]] auto removed_count = m_block_reasons.erase(reason);
102 assert(removed_count == 1);
103 }
104
105 /// Try to start using the resource.
106 [[nodiscard]] String_set try_use() {
107 MUTEX_LOCK(guard, &get_lock());
108 if (m_block_reasons.empty()) {
109 ++m_user_count;
110 }
111 //++m_user_count;
112 return m_block_reasons;
113 }
114
115 /// Stop using the resource if try_use was previously called successfully.
116 void end_use() {
117 MUTEX_LOCK(guard, &get_lock());
118 assert(m_user_count > 0);
119 --m_user_count;
120 }
121
123
126 std::atomic<int> m_initialized;
128
129 friend class User;
130 friend class Blocker;
131}; // class Resource
132
133// RAII class that attempts to use a Resource. When an instance is
134// constructed, it checks if the Resource is blocked; if not, it holds
135// the Resource in 'used' state during the instance life time.
136class User {
137 public:
138 /// By default, does not use any Resource.
140
141 /// Attempt to start using the given Resource. This may fail, so the
142 /// caller must check if it succeeded or not, using `operator bool`
143 /// or `operator!`.
144 explicit User(Resource &resource) {
145 m_block_reasons = resource.try_use();
146 if (m_block_reasons.empty()) {
147 m_resource = &resource;
148 } else {
149 m_resource = nullptr;
150 }
151 }
152
153 /// Use the same Resource that `other` uses, if any.
154 User(User &other) : m_resource(nullptr) { *this = other; }
155
156 /// Take over the Resource that `other` uses, if any.
157 User(User &&other) noexcept : m_resource(nullptr) {
158 *this = std::move(other);
159 }
160
161 /// Release our own Resource, if any, and then start using the
162 /// Resource that `other` uses, if any.
163 User &operator=(User const &other) {
164 if (this == &other) {
165 return *this;
166 }
167 end_use();
168 m_resource = other.m_resource;
170 if (m_resource != nullptr) {
171 [[maybe_unused]] auto ret = m_resource->try_use();
172 }
173 return *this;
174 }
175
176 /// Release our own resource, if any, and then steal the Resource
177 /// that `other` uses, if any, so that `other` will not use it any
178 /// more.
179 User &operator=(User &&other) noexcept {
180 end_use();
181 m_resource = other.m_resource;
182 m_block_reasons = other.m_block_reasons;
183 other.m_resource = nullptr;
184 return *this;
185 }
186
187 /// Return true if we hold the Resource in 'used' state.
188 explicit operator bool() { return m_resource != nullptr; }
189
190 /// Return false if we hold the Resource in 'used' state.
191 bool operator!() { return m_resource == nullptr; }
192
193 /// Return the set of strings that explain the reasons why the
194 /// resource was blocked.
196
197 /// Stop holding the Resource in 'used' state, if we do.
198 void end_use() {
199 if (m_resource != nullptr) {
201 m_resource = nullptr;
202 } else {
203 m_block_reasons.clear();
204 }
205 }
206
207 /// Stop holding the Resource in 'used' state, if we do.
208 ~User() { end_use(); }
209
210 private:
213}; // class User
214
215// RAII class that attempts to block a Resource. When an instance is
216// constructed, it checks if the Resource is used; if not, it blocks
217// the Resource during the instance life time.
218class Blocker {
219 public:
220 /// By default, does not block any Resource.
222
223 /// Attempt to start using the given Resource. This may fail, so the
224 /// caller must check if it succeeded or not, using `operator bool`
225 /// or `operator!`.
226 Blocker(Resource &resource, const std::string &reason)
227 : m_resource(nullptr), m_user_count(resource.try_block(reason)) {
228 if (m_user_count == 0) {
229 m_resource = &resource;
230 m_reason = reason;
231 }
232 }
233
234 /// Block the same Resource that `other` blocks, if any.
236 *this = other;
237 }
238
239 /// Take over the Resource that `other` blocks, if any.
240 Blocker(Blocker &&other) noexcept : m_resource(nullptr), m_user_count(0) {
241 *this = std::move(other);
242 }
243
244 /// Release our own Resource, if any, and then block the same Resource
245 /// that `other` blocks, if any.
246 Blocker &operator=(Blocker const &other) {
247 if (this == &other) {
248 return *this;
249 }
250 end_block();
251 m_resource = other.m_resource;
252 m_reason = other.m_reason;
254 if (m_resource != nullptr) {
255 [[maybe_unused]] auto ret = m_resource->try_block(m_reason);
256 }
257 return *this;
258 }
259
260 /// Release our own resource, if any, and then steal the Resource
261 /// that `other` blocks, if any, so that `other` does not block
262 /// it any more.
263 Blocker &operator=(Blocker &&other) noexcept {
264 end_block();
265 m_resource = other.m_resource;
266 m_reason = other.m_reason;
267 m_user_count = other.m_user_count;
268 other.m_resource = nullptr;
269 other.m_reason = "";
270 other.m_user_count = 0;
271 return *this;
272 }
273
274 /// Return true if we block the Resource.
275 explicit operator bool() { return m_resource != nullptr; }
276
277 /// Return false if we block the Resource.
278 bool operator!() { return m_resource == nullptr; }
279
280 /// Return the number of users of the Resource at the time we tried
281 /// to block it.
282 int user_count() const { return m_user_count; }
283
284 /// Stop blocking the Resource, if we do.
285 void end_block() {
286 if (m_resource != nullptr) {
288 m_resource = nullptr;
289 m_reason = "";
290 m_user_count = 0;
291 }
292 }
293
294 /// Stop holding the Resource in 'used' state, if we do.
296
297 private:
299 std::string m_reason;
301}; // class Blocker
302
303} // namespace resource_blocker
304
305#endif /* RPL_RESOURCE_BLOCKER_H_INCLUDED */
Kerberos Client Authentication nullptr
Definition: auth_kerberos_client_plugin.cc:250
Definition: resource_blocker.h:218
Blocker & operator=(Blocker const &other)
Release our own Resource, if any, and then block the same Resource that other blocks,...
Definition: resource_blocker.h:246
std::string m_reason
Definition: resource_blocker.h:299
Blocker(Blocker &&other) noexcept
Take over the Resource that other blocks, if any.
Definition: resource_blocker.h:240
Blocker()
By default, does not block any Resource.
Definition: resource_blocker.h:221
Blocker(Blocker &other)
Block the same Resource that other blocks, if any.
Definition: resource_blocker.h:235
Resource * m_resource
Definition: resource_blocker.h:298
Blocker(Resource &resource, const std::string &reason)
Attempt to start using the given Resource.
Definition: resource_blocker.h:226
void end_block()
Stop blocking the Resource, if we do.
Definition: resource_blocker.h:285
int m_user_count
Definition: resource_blocker.h:300
bool operator!()
Return false if we block the Resource.
Definition: resource_blocker.h:278
int user_count() const
Return the number of users of the Resource at the time we tried to block it.
Definition: resource_blocker.h:282
Blocker & operator=(Blocker &&other) noexcept
Release our own resource, if any, and then steal the Resource that other blocks, if any,...
Definition: resource_blocker.h:263
~Blocker()
Stop holding the Resource in 'used' state, if we do.
Definition: resource_blocker.h:295
Represents a "Resource" which can be either "used" by a number of threads, or "blocked" by a number o...
Definition: resource_blocker.h:69
Resource & operator=(Resource const &other)=delete
void end_block(const std::string &reason)
Unblock the resource if try_block was previously called successfully.
Definition: resource_blocker.h:99
mysql_mutex_t m_lock
Definition: resource_blocker.h:127
String_set try_use()
Try to start using the resource.
Definition: resource_blocker.h:106
Resource & operator=(Resource &&other)=delete
mysql_mutex_t & get_lock()
Definition: resource_blocker.h:122
~Resource()
Definition: resource_blocker.h:77
std::set< std::string > String_set
Definition: resource_blocker.h:71
int m_user_count
Definition: resource_blocker.h:125
void end_use()
Stop using the resource if try_use was previously called successfully.
Definition: resource_blocker.h:116
Resource(Resource &other)=delete
String_set m_block_reasons
Definition: resource_blocker.h:124
Resource(Resource &&other)=delete
Resource()
Definition: resource_blocker.h:73
int try_block(const std::string &reason)
Try to block the resource from being used, for the given reason.
Definition: resource_blocker.h:90
std::atomic< int > m_initialized
Definition: resource_blocker.h:126
Definition: resource_blocker.h:136
~User()
Stop holding the Resource in 'used' state, if we do.
Definition: resource_blocker.h:208
User(User &&other) noexcept
Take over the Resource that other uses, if any.
Definition: resource_blocker.h:157
void end_use()
Stop holding the Resource in 'used' state, if we do.
Definition: resource_blocker.h:198
User()
By default, does not use any Resource.
Definition: resource_blocker.h:139
User(Resource &resource)
Attempt to start using the given Resource.
Definition: resource_blocker.h:144
User & operator=(User const &other)
Release our own Resource, if any, and then start using the Resource that other uses,...
Definition: resource_blocker.h:163
bool operator!()
Return false if we hold the Resource in 'used' state.
Definition: resource_blocker.h:191
Resource * m_resource
Definition: resource_blocker.h:211
User & operator=(User &&other) noexcept
Release our own resource, if any, and then steal the Resource that other uses, if any,...
Definition: resource_blocker.h:179
Resource::String_set block_reasons()
Return the set of strings that explain the reasons why the resource was blocked.
Definition: resource_blocker.h:195
User(User &other)
Use the same Resource that other uses, if any.
Definition: resource_blocker.h:154
Resource::String_set m_block_reasons
Definition: resource_blocker.h:212
#define mysql_mutex_destroy(M)
Definition: mysql_mutex.h:45
#define mysql_mutex_init(K, M, A)
Definition: mysql_mutex.h:40
static constexpr unsigned PSI_INSTRUMENT_ME
Definition: psi_bits.h:42
#define MUTEX_LOCK(NAME, X)
Definition: mutex_lock.h:82
Definition: resource_blocker.h:31
An instrumented mutex structure.
Definition: mysql_mutex_bits.h:49
#define MY_MUTEX_INIT_FAST
Definition: thr_mutex.h:67