MySQL 8.0.30
Source Code Documentation
pfs_lock.h
Go to the documentation of this file.
1/* Copyright (c) 2009, 2022, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23#ifndef PFS_LOCK_H
24#define PFS_LOCK_H
25
26/**
27 @file storage/perfschema/pfs_lock.h
28 Performance schema internal locks (declarations).
29*/
30
31#include <assert.h>
32#include <atomic>
33
34/* to cause bugs, testing */
35// #define MEM(X) std::memory_order_relaxed
36/* correct code */
37#define MEM(X) X
38
39/**
40 @addtogroup performance_schema_buffers
41 @{
42*/
43
44/**
45 State of a free record.
46 Values of a free record should not be read by a reader.
47 Writers can concurrently attempt to allocate a free record.
48*/
49#define PFS_LOCK_FREE 0x00
50/**
51 State of a dirty record.
52 Values of a dirty record should not be read by a reader,
53 as the record is currently being modified.
54 Only one writer, the writer which owns the record, should
55 modify the record content.
56*/
57#define PFS_LOCK_DIRTY 0x01
58/**
59 State of an allocated record.
60 Values of an allocated record are safe to read by a reader.
61 A writer may modify some but not all properties of the record:
62 only modifying values that can never cause the reader to crash is allowed.
63*/
64#define PFS_LOCK_ALLOCATED 0x02
65
66#define VERSION_MASK 0xFFFFFFFC
67#define STATE_MASK 0x00000003
68#define VERSION_INC 4
69
72};
73
76};
77
78/**
79 A 'lock' protecting performance schema internal buffers.
80 This lock is used to mark the state of a record.
81 Access to the record is not enforced here,
82 it's up to the readers and writers to look at the record state
83 before making an actual read or write operation.
84
85 The following state diagram shows the general states maintained by the lock.
86
87 @startuml
88
89 state PFS_LOCK_FREE
90
91 state PFS_LOCK_DIRTY
92
93 state PFS_LOCK_ALLOCATED
94
95 [*] -down-> PFS_LOCK_FREE
96
97 PFS_LOCK_FREE -down-> [*]
98
99 PFS_LOCK_FREE -right-> PFS_LOCK_DIRTY : free_to_dirty()
100 PFS_LOCK_DIRTY -right-> PFS_LOCK_ALLOCATED : dirty_to_allocated()
101
102 PFS_LOCK_ALLOCATED --> PFS_LOCK_FREE : allocated_to_free()
103 PFS_LOCK_ALLOCATED --> PFS_LOCK_DIRTY : allocated_to_dirty()
104
105 @enduml
106
107 An internal version counter is also incremented, to detect each modification
108 done to a record.
109
110 The following diagram represent a fragment of all the states reached,
111 for an object creation, modification and destruction.
112
113 @startuml
114
115 state "..." as BEFORE
116
117 state "PFS_LOCK_FREE" as FREE_N
118 FREE_N : m_version = N
119
120 state "PFS_LOCK_DIRTY" as DIRTY_N
121 DIRTY_N : m_version = N
122
123 state "PFS_LOCK_ALLOCATED" as ALLOCATED_N1
124 ALLOCATED_N1 : m_version = N + 1
125
126 state "PFS_LOCK_DIRTY" as DIRTY_N1
127 DIRTY_N1 : m_version = N + 1
128
129 state "PFS_LOCK_ALLOCATED" as ALLOCATED_N2
130 ALLOCATED_N2: m_version = N + 2
131
132 state "PFS_LOCK_FREE" as FREE_N2
133 FREE_N2 : m_version = N + 2
134
135 state "..." as AFTER
136
137 BEFORE -down-> FREE_N
138 FREE_N -down-> DIRTY_N : free_to_dirty()
139 DIRTY_N -down-> ALLOCATED_N1 : dirty_to_allocated()
140 ALLOCATED_N1 -down-> DIRTY_N1 : allocated_to_dirty()
141 DIRTY_N1 -down-> ALLOCATED_N2 : dirty_to_allocated()
142 ALLOCATED_N2 -down-> FREE_N2 : allocated_to_free()
143 FREE_N2 -down-> AFTER
144
145 Note left of ALLOCATED_N1 : State after object creation
146 Note left of ALLOCATED_N2 : State after object modification
147 Note left of FREE_N2 : State after object destruction
148
149 @enduml
150*/
151struct pfs_lock {
152 /**
153 The record internal version and state
154 @sa PFS_LOCK_FREE
155 @sa PFS_LOCK_DIRTY
156 @sa PFS_LOCK_ALLOCATED
157 The version number is to transform the 'ABA' problem
158 (see http://en.wikipedia.org/wiki/ABA_problem)
159 into an 'A(n)BA(n + 1)' problem, where 'n' is the m_version number.
160 When the performance schema instrumentation deletes a record,
161 then create a different record reusing the same memory allocation,
162 the version number is incremented, so that a reader can detect that
163 the record was changed. Note that the version number is never
164 reset to zero when a new record is created.
165 The version number is stored in the high 30 bits.
166 The state is stored in the low 2 bits.
167 */
168 std::atomic<uint32> m_version_state;
169
171 uint32 copy;
172
173 copy = m_version_state; /* dirty read */
174
175 return copy;
176 }
177
178 /** Returns true if the record is free. */
179 bool is_free(void) {
180 uint32 copy = m_version_state.load();
181
182 return ((copy & STATE_MASK) == PFS_LOCK_FREE);
183 }
184
185 /** Returns true if the record contains values that can be read. */
186 bool is_populated(void) {
187 uint32 copy = m_version_state.load();
188
189 return ((copy & STATE_MASK) == PFS_LOCK_ALLOCATED);
190 }
191
192 /**
193 Execute a free to dirty transition.
194 This transition is safe to execute concurrently by multiple writers.
195 Only one writer will succeed to acquire the record.
196 @return true if the operation succeed
197 */
199 uint32 old_val = m_version_state.load();
200
201 if ((old_val & STATE_MASK) != PFS_LOCK_FREE) {
202 return false;
203 }
204
205 uint32 new_val = (old_val & VERSION_MASK) + PFS_LOCK_DIRTY;
206
207 bool pass =
208 atomic_compare_exchange_strong(&m_version_state, &old_val, new_val);
209
210 if (pass) {
211 copy_ptr->m_version_state = new_val;
212 }
213
214 return pass;
215 }
216
217 /**
218 Execute an allocated to dirty transition.
219 This transition should be executed by the writer that owns the record,
220 before the record is modified.
221 */
224 /* Make sure the record was ALLOCATED. */
225 assert((copy & STATE_MASK) == PFS_LOCK_ALLOCATED);
226 /* Keep the same version, set the DIRTY state */
227 uint32 new_val = (copy & VERSION_MASK) + PFS_LOCK_DIRTY;
228 /* We own the record, no need to use compare and swap. */
229
230 m_version_state.store(new_val);
231
232 copy_ptr->m_version_state = new_val;
233 }
234
235 /**
236 Execute a dirty to allocated transition.
237 This transition should be executed by the writer that owns the record,
238 after the record is in a state ready to be read.
239 */
241 /* Make sure the record was DIRTY. */
242 assert((copy->m_version_state & STATE_MASK) == PFS_LOCK_DIRTY);
243 /* Increment the version, set the ALLOCATED state */
244 uint32 new_val = (copy->m_version_state & VERSION_MASK) + VERSION_INC +
246
247 m_version_state.store(new_val);
248 }
249
250 /**
251 Initialize a lock to allocated.
252 This transition should be executed by the writer that owns the record and
253 the lock,
254 after the record is in a state ready to be read.
255 */
256 void set_allocated(void) {
257 /* Do not set the version to 0, read the previous value. */
259 /* Increment the version, set the ALLOCATED state */
261
262 m_version_state.store(new_val);
263 }
264
265 /**
266 Initialize a lock to dirty.
267 */
268 void set_dirty(pfs_dirty_state *copy_ptr) {
269 /* Do not set the version to 0, read the previous value. */
270 uint32 copy = m_version_state.load();
271 /* Increment the version, set the DIRTY state */
273 m_version_state.store(new_val);
274
275 copy_ptr->m_version_state = new_val;
276 }
277
278 /**
279 Execute a dirty to free transition.
280 This transition should be executed by the writer that owns the record.
281 */
283 /* Make sure the record was DIRTY. */
284 assert((copy->m_version_state & STATE_MASK) == PFS_LOCK_DIRTY);
285 /* Keep the same version, set the FREE state */
286 uint32 new_val = (copy->m_version_state & VERSION_MASK) + PFS_LOCK_FREE;
287
288 m_version_state.store(new_val);
289 }
290
291 /**
292 Execute an allocated to free transition.
293 This transition should be executed by the writer that owns the record.
294 */
295 void allocated_to_free(void) {
296 /*
297 If this record is not in the ALLOCATED state and the caller is trying
298 to free it, this is a bug: the caller is confused,
299 and potentially damaging data owned by another thread or object.
300 */
302 /* Make sure the record was ALLOCATED. */
303 assert(((copy & STATE_MASK) == PFS_LOCK_ALLOCATED));
304 /* Keep the same version, set the FREE state */
305 uint32 new_val = (copy & VERSION_MASK) + PFS_LOCK_FREE;
306
307 m_version_state.store(new_val);
308 }
309
310 /**
311 Start an optimistic read operation.
312 @param [out] copy Saved lock state
313 @sa end_optimist_lock.
314 */
316 copy->m_version_state = m_version_state.load();
317 }
318
319 /**
320 End an optimistic read operation.
321 @sa begin_optimist_lock.
322 @param copy Saved lock state
323 @return true if the data read is safe to use.
324 */
326 /* Check there was valid data to look at. */
327 if ((copy->m_version_state & STATE_MASK) != PFS_LOCK_ALLOCATED) {
328 return false;
329 }
330
331 uint32 version_state = m_version_state.load();
332
333 /* Check the version + state has not changed. */
334 if (copy->m_version_state != version_state) {
335 return false;
336 }
337
338 return true;
339 }
340
342 uint32 version_state = m_version_state.load();
343
344 return (version_state & VERSION_MASK);
345 }
346};
347
348/** @} */
349#endif
#define VERSION_MASK
Definition: pfs_lock.h:66
#define PFS_LOCK_DIRTY
State of a dirty record.
Definition: pfs_lock.h:57
#define STATE_MASK
Definition: pfs_lock.h:67
#define PFS_LOCK_FREE
State of a free record.
Definition: pfs_lock.h:49
#define VERSION_INC
Definition: pfs_lock.h:68
#define PFS_LOCK_ALLOCATED
State of an allocated record.
Definition: pfs_lock.h:64
uint32_t uint32
Definition: my_inttypes.h:66
void copy(Shards< COUNT > &dst, const Shards< COUNT > &src) noexcept
Copy the counters, overwrite destination.
Definition: ut0counter.h:353
Definition: pfs_lock.h:74
uint32 m_version_state
Definition: pfs_lock.h:75
A 'lock' protecting performance schema internal buffers.
Definition: pfs_lock.h:151
bool is_populated(void)
Returns true if the record contains values that can be read.
Definition: pfs_lock.h:186
void dirty_to_allocated(const pfs_dirty_state *copy)
Execute a dirty to allocated transition.
Definition: pfs_lock.h:240
uint32 copy_version_state()
Definition: pfs_lock.h:170
bool is_free(void)
Returns true if the record is free.
Definition: pfs_lock.h:179
bool end_optimistic_lock(const pfs_optimistic_state *copy)
End an optimistic read operation.
Definition: pfs_lock.h:325
void allocated_to_free(void)
Execute an allocated to free transition.
Definition: pfs_lock.h:295
void allocated_to_dirty(pfs_dirty_state *copy_ptr)
Execute an allocated to dirty transition.
Definition: pfs_lock.h:222
void begin_optimistic_lock(pfs_optimistic_state *copy)
Start an optimistic read operation.
Definition: pfs_lock.h:315
void dirty_to_free(const pfs_dirty_state *copy)
Execute a dirty to free transition.
Definition: pfs_lock.h:282
uint32 get_version()
Definition: pfs_lock.h:341
void set_allocated(void)
Initialize a lock to allocated.
Definition: pfs_lock.h:256
std::atomic< uint32 > m_version_state
The record internal version and state.
Definition: pfs_lock.h:168
void set_dirty(pfs_dirty_state *copy_ptr)
Initialize a lock to dirty.
Definition: pfs_lock.h:268
bool free_to_dirty(pfs_dirty_state *copy_ptr)
Execute a free to dirty transition.
Definition: pfs_lock.h:198
Definition: pfs_lock.h:70
uint32 m_version_state
Definition: pfs_lock.h:71