MySQL 8.0.39
Source Code Documentation
ut0stateful_latching_rules.h
Go to the documentation of this file.
1/*****************************************************************************
2
3Copyright (c) 2021, 2024, Oracle and/or its affiliates.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is designed to work with certain software (including
10but not limited to OpenSSL) that is licensed under separate terms,
11as designated in a particular file or component or in included license
12documentation. The authors of MySQL hereby grant you an additional
13permission to link the program and your derivative works with the
14separately licensed software that they have either included with
15the program or referenced in the documentation.
16
17This program is distributed in the hope that it will be useful, but WITHOUT
18ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
20for more details.
21
22You should have received a copy of the GNU General Public License along with
23this program; if not, write to the Free Software Foundation, Inc.,
2451 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
26*****************************************************************************/
27
28/** @file include/ut0stateful_latching_rules.h
29The Stateful_latching_rules class template which can be used to describe
30possible states, latches required to transition between them, and then validate
31if transitions performed by application take required latches, and that queries
32for the state are performed when holding enough latches to prevent state from
33changing. */
34
35#ifndef ut0stateful_latching_rules
36#define ut0stateful_latching_rules
37
38#include <bitset>
39
40namespace ut {
41/** This is a generic mechanism for verifying correctness of latching rules for
42state transitions and querying for state of a system. It was created for io_fix
43field of buf_page_t, but can be configured via template instantiation for other
44usages as long as they fit into the following model:
45
461. The object can be in one of fixed possible states. In case of io_fix, these
47states were io_fix values, but in general the state can be a tuple of several
48important, perhaps abstract, properties of the object, for example it could be
49a std::pair<io_fix, is_in_page_hash>. As long you can pass the list of possible
50states to constructor, you're good to go.
51
522. There is a fixed set of latches which are relevant to protecting the state.
53In case of io_fix there were 3: each page has a latch, and its buffer pool has
54a latch, and there is also a more abstract property of "being the thread which
55is responsible for IO operation for this page", which is fine, as long as you
56can quickly determine which of the three the current thread has, and you can
57prove that no two threads can hold a given "latch" at the same time (which is
58trivial for regular mutexes, but puts a burden of proof on you in case of
59abstract concepts like "IO responsibility")
60
613. For each pair of states you can specify what latches a thread must hold to
62perform it. For example a thread can change io_fix from BUF_IO_NONE to
63BUF_IO_WRITE only while holding latches #0, #1 and #2. But the rule could be
64more complex, for example "you either must hold 1 and 2, or just 0" - as long as
65you can express it as an alternative of conjunctions (without negation) it's OK.
66
67In other words, we model the situation as a graph, with states as nodes, and
68edges being possible transitions, where each edge is labeled with a subset of
69latches (and there might be zero or more edges between any pair of states).
70
71For example:
72+---+ +---+ +---+
73| a | ---{0,2}--> | b | <--{2}--> | c |
74| | <---{0,2}-- | | ---{2}--> | |
75| | <---{1}---- | | | |
76+---+ +---+ +---+
77
78If we know that current state *MUST* be 'a' or 'c', and want to determine which
79of the two it is, then it is sufficient to hold just latch #2, because edges
80going out of 'a' and edges going out of 'c' all require latch #2.
81OTOH if we don't know what the current state is, but want to determine if the
82state belongs to the set {b,c}, then it is sufficient to hold just latches
83#0 and #1, because there are only three edges going in or out of {b,c}, namely
84a-{0,2}->b, a<-{0,2}-b, a<-{1}-b, and each of them requires #0 or #1.
85I hope this example shows that verifying correctness case by case like that is
86possible to do manually, but seems error prone, subject to code rot, and would
87benefit from automation.
88
89You specify the type of nodes and number of latches by template instatiation,
90and shape of the graph in the constructor.
91
92This class then can then offer you two things:
931. If you call on_transition(from, to, owned_latches) it will verify for you
94that such transition is allowed as at least one of the edges requires a subset
95of owned_latches. This way you can verify that the model you described matches
96reality. For example we call it from buf_page_set_io_fix(page,state).
97
982. If you call assert_latches_let_distinguish(owned_latches,A,B) where A and B
99are two disjoint sets of states, it will tell you if the current thread holds
100enough latches to make it possible to determine if state belongs to A or to B in
101a meaningful way - that is the answer to such question can not change while
102being asked, because each edge going out from A and out from B has at least one
103of the owned_latches.
104For example if a thread knows that io_fix is BUF_IO_READ or BUF_IO_WRITE but is
105unsure which one of them it is, it might check io_fix state, but only if it has
106latches which prevent a change from BUF_IO_READ to BUF_IO_PIN or BUF_IO_NONE or
107BUF_IO_WRITE, and from BUF_IO_WRITE to BUF_IO_PIN or BUF_IO_NONE or BUF_IO_READ
108- otherwise the answer could change during query.
109Note that this might be a smaller set of latches than needed to prevent any
110activity at all. For example we don't care about changes from BUF_IO_PIN to
111BUF_IO_FIX. Also, in this example A and B are both singletons, but the mechanism
112works for arbitrarily large sets. */
113template <typename Node, size_t LATCHES_COUNT>
115 public:
116 /** The type for a state in our automata */
117 using node_t = Node;
118
119 /** The type for a set of states */
120 using nodes_set_t = std::set<node_t>;
121
122 /** The type for a set of latches - there are LATCHES_COUNT distinct
123 latches, each one corresponds to a fixed position in a bitset. The mapping is
124 up to the user of this class - just be consistent. */
125 using latches_set_t = std::bitset<LATCHES_COUNT>;
126
127 /** The type for a possible transition from one state to another while holding
128 at least a given set of latches. */
129 struct edge_t {
130 /** The old state from which the transition starts. */
132
133 /** The required subset of latches for this particular transition. Note that
134 there might be several edges for the same from->to pair, which means that a
135 thread can pick any of them for which it has the required latches. */
137
138 /** The new state from to which the transition leads. */
140
141 /** Creates a description of one of allowed state transitions from given
142 state to another while holding given latches. For example
143 {BUF_IO_READ, {0, 2}, BUF_IO_NONE}
144 says a if a thread holds latches 0 and 2 then it can transition from
145 BUF_IO_READ to BUF_IO_NONE.
146 @param[in] from The old state from which the transition starts
147 @param[in] idxs The list of integers in range <0,LATCHES_COUNT) used to
148 construct set of latches for this edge.
149 @param[in] to The new state from to which the transition leads. */
150 edge_t(node_t from, std::initializer_list<int> &&idxs, node_t to)
151 : m_from(from), m_latches(), m_to(to) {
152 for (auto id : idxs) m_latches[id] = true;
153 }
154 };
155
156 private:
157 /** The set of all possible states. Useful for computing the complement of
158 any given set */
160
161 /** The list of allowed state transitions. */
162 const std::vector<edge_t> m_edges;
163
164 /** A helper function which prints the ids of latches from the set of latches
165 to an ostream-like object (such as our ib::logger).
166 @param[in] sout the ostream-like object to print latches to
167 @param[in] latches a set of latches to print to sout
168 @*/
169 template <typename T>
170 void print(T &sout, const latches_set_t &latches) const {
171 sout << '{';
172 bool first = true;
173 for (size_t i = 0; i < latches.size(); ++i) {
174 if (latches[i]) {
175 if (!first) {
176 sout << ", ";
177 }
178 sout << i;
179 first = false;
180 }
181 }
182 sout << '}';
183 }
184
185 /** Checks if another thread could change the state even though we hold some
186 latches. In other words, checks if the rules allow a thread which ISN'T
187 holding any of the `forbiden_latches` to transition from at least one state
188 inside `source` to at least one state inside `destination`, where `source`
189 and `destination` are sets of states.
190 It prints an error if transition is possible.
191 @param[in] forbiden_latches The set of latches which the hypothetical
192 transitioning thread can't hold (because we are
193 holding them).
194 @param[in] source The set of source states
195 @param[in] destination The set of destination states
196 @return true iff there is at least one edge from a state inside `source` to
197 a state inside `destination` labeled with a set of latches disjoint with
198 `forbiden_latches`. */
199 bool is_transition_possible(const latches_set_t &forbiden_latches,
200 const nodes_set_t &source,
201 const nodes_set_t &destination) const {
202 for (const edge_t &edge : m_edges) {
203 if (source.count(edge.m_from) && destination.count(edge.m_to) &&
204 (edge.m_latches & forbiden_latches) == 0) {
205 {
206 ib::error the_err{};
207 the_err << "It is possible to transition from " << edge.m_from
208 << " to " << edge.m_to << " holding just ";
209 print(the_err, edge.m_latches);
210 the_err << " even when we hold ";
211 print(the_err, forbiden_latches);
212 }
213
214 return true;
215 }
216 }
217 return false;
218 }
219
220 /** Computes the complement of the given set of states, that is a set of all
221 of the other states.
222 @param[in] states The set of states
223 @return the complement of the set of states */
224 nodes_set_t complement(const nodes_set_t &states) const {
225 nodes_set_t other_states;
226 std::set_difference(m_states.begin(), m_states.end(), states.begin(),
227 states.end(),
228 std::inserter(other_states, other_states.end()));
229 return other_states;
230 }
231
232 /** A convenience function for the special case of
233 @see is_transition_possible(forbiden_latches, A,B) where B=complement(A).
234 In other words, it checks if another thread can cause the state to leave the
235 given set of states even though we hold a set of `forbiden_latches`.
236 @param[in] forbiden_latches The set of latches which the hypothetical
237 transitioning thread can't hold (because we are
238 holding them).
239 @param[in] source The set of source states
240 @return true iff there is at least one edge from a state inside `source` to
241 a state outside of `source` labeled with a set of latches disjoint with
242 `forbiden_latches`.*/
243 bool can_leave(const latches_set_t &forbiden_latches,
244 const nodes_set_t &source) const {
245 return is_transition_possible(forbiden_latches, source, complement(source));
246 }
247
248 public:
249 /** Creates a set of rules for `allowed_transitions` between `all_states`.
250 @param[in] all_states The set of all possible states of the system
251 @param[in] allowed_transitions The set of allowed transitions. There might be
252 multiple edges between the same pare of states
253 which is interpreted as an alternative:
254 {{x, {0,2}, y}, {x, {1}, y}} would mean, that
255 a thread which holds just latch #2 can't
256 transition from x to y, but a thread which
257 holds #0 and #2 can, and so does a thread
258 which holds just #1 or #0 and #1 or #1 and #2
259 or all three or even more. */
261 std::vector<edge_t> allowed_transitions)
262 : m_states(std::move(all_states)),
263 m_edges(std::move(allowed_transitions)) {}
264
265 /** Checks if `owned_latches` (presumably held by the current thread) are
266 enough to meaningfully ask a question if current state belongs to set A as
267 opposed to set B. In other words it checks if `owned_latches` prevent other
268 threads from performing state transition from the inside of set A outside,
269 and from the inside of set B outside. Otherwise it prints debug information
270 as fatal error.
271 @param[in] owned_latches The latches the current thread owns
272 @param[in] A The first set of latches
273 @param[in] B The second set of latches */
275 const nodes_set_t &A,
276 const nodes_set_t &B) const {
277 const bool can_leave_A = can_leave(owned_latches, A);
278 const bool can_leave_B = can_leave(owned_latches, B);
279
280 if (can_leave_A || can_leave_B) {
282 the_err << "We can leave "
283 << (can_leave_A && can_leave_B ? "both A and B"
284 : (can_leave_A ? "A" : "B"))
285 << " as we only hold: ";
286 print(the_err, owned_latches);
287 }
288 }
289
290 /** A convenience function for the special case of
291 @see assert_latches_let_distinguish(owned_latches, A, complement(A)).
292 In other words, it checks if the current thread's owned_latches prevent state
293 transitions to and from the set A.
294 @param[in] owned_latches The latches the current thread owns
295 @param[in] A The set of latches*/
297 const nodes_set_t &A) const {
298 assert_latches_let_distinguish(owned_latches, A, complement(A));
299 }
300
301 /** Checks if the transition between given states holding specified latches is
302 allowed by the rules. In other words it checks if there is at least one edge
303 between `from` and `to` nodes labeled with a subset of `ownwed_latches`.
304 It prints a debug information as fatal error if rules are violated. */
305 void on_transition(const node_t &from, const node_t &to,
306 const latches_set_t &owned_latches) const {
307 if (from == to) {
308 return;
309 }
310 const auto missing_latches = ~owned_latches;
311
312 if (std::any_of(std::begin(m_edges), std::end(m_edges), [&](auto edge) {
313 return (edge.m_from == from && edge.m_to == to) &&
314 (edge.m_latches & missing_latches) == 0;
315 })) {
316 return;
317 }
319 the_err << "Disallowed transition FROM " << from << " TO " << to
320 << " WITH ";
321 print(the_err, owned_latches);
322 }
323};
324} // namespace ut
325#endif /* ut0stateful_latching_rules */
The class error is used to emit error messages.
Definition: ut0log.h:230
The class fatal is used to emit an error message and stop the server by crashing it.
Definition: ut0log.h:252
This is a generic mechanism for verifying correctness of latching rules for state transitions and que...
Definition: ut0stateful_latching_rules.h:114
const nodes_set_t m_states
The set of all possible states.
Definition: ut0stateful_latching_rules.h:159
const std::vector< edge_t > m_edges
The list of allowed state transitions.
Definition: ut0stateful_latching_rules.h:162
std::bitset< LATCHES_COUNT > latches_set_t
The type for a set of latches - there are LATCHES_COUNT distinct latches, each one corresponds to a f...
Definition: ut0stateful_latching_rules.h:125
bool is_transition_possible(const latches_set_t &forbiden_latches, const nodes_set_t &source, const nodes_set_t &destination) const
Checks if another thread could change the state even though we hold some latches.
Definition: ut0stateful_latching_rules.h:199
void assert_latches_let_distinguish(const latches_set_t &owned_latches, const nodes_set_t &A, const nodes_set_t &B) const
Checks if owned_latches (presumably held by the current thread) are enough to meaningfully ask a ques...
Definition: ut0stateful_latching_rules.h:274
Stateful_latching_rules(nodes_set_t all_states, std::vector< edge_t > allowed_transitions)
Creates a set of rules for allowed_transitions between all_states.
Definition: ut0stateful_latching_rules.h:260
nodes_set_t complement(const nodes_set_t &states) const
Computes the complement of the given set of states, that is a set of all of the other states.
Definition: ut0stateful_latching_rules.h:224
Node node_t
The type for a state in our automata.
Definition: ut0stateful_latching_rules.h:117
std::set< node_t > nodes_set_t
The type for a set of states.
Definition: ut0stateful_latching_rules.h:120
void assert_latches_let_distinguish(const latches_set_t &owned_latches, const nodes_set_t &A) const
A convenience function for the special case of.
Definition: ut0stateful_latching_rules.h:296
void on_transition(const node_t &from, const node_t &to, const latches_set_t &owned_latches) const
Checks if the transition between given states holding specified latches is allowed by the rules.
Definition: ut0stateful_latching_rules.h:305
void print(T &sout, const latches_set_t &latches) const
A helper function which prints the ids of latches from the set of latches to an ostream-like object (...
Definition: ut0stateful_latching_rules.h:170
bool can_leave(const latches_set_t &forbiden_latches, const nodes_set_t &source) const
A convenience function for the special case of.
Definition: ut0stateful_latching_rules.h:243
Definition: gcs_xcom_synode.h:64
This file contains a set of libraries providing overloads for regular dynamic allocation routines whi...
Definition: aligned_alloc.h:48
repeated Source source
Definition: replication_asynchronous_connection_failover.proto:42
The type for a possible transition from one state to another while holding at least a given set of la...
Definition: ut0stateful_latching_rules.h:129
node_t m_to
The new state from to which the transition leads.
Definition: ut0stateful_latching_rules.h:139
edge_t(node_t from, std::initializer_list< int > &&idxs, node_t to)
Creates a description of one of allowed state transitions from given state to another while holding g...
Definition: ut0stateful_latching_rules.h:150
node_t m_from
The old state from which the transition starts.
Definition: ut0stateful_latching_rules.h:131
latches_set_t m_latches
The required subset of latches for this particular transition.
Definition: ut0stateful_latching_rules.h:136
#define UT_LOCATION_HERE
Definition: ut0core.h:47
unsigned long id[MAX_DEAD]
Definition: xcom_base.cc:510