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