1/* Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
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 as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software Foundation,
14 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15
16#ifndef PFS_LOCK_H
17#define PFS_LOCK_H
18
19/**
20 @file storage/perfschema/pfs_lock.h
21 Performance schema internal locks (declarations).
22*/
23
24#include "pfs_atomic.h"
25
26/**
27 @addtogroup Performance_schema_buffers
28 @{
29*/
30
31/**
32 State of a free record.
33 Values of a free record should not be read by a reader.
34 Writers can concurrently attempt to allocate a free record.
35*/
36#define PFS_LOCK_FREE 0x00
37/**
38 State of a dirty record.
39 Values of a dirty record should not be read by a reader,
40 as the record is currently being modified.
41 Only one writer, the writer which owns the record, should
42 modify the record content.
43*/
44#define PFS_LOCK_DIRTY 0x01
45/**
46 State of an allocated record.
47 Values of an allocated record are safe to read by a reader.
48 A writer may modify some but not all properties of the record:
49 only modifying values that can never cause the reader to crash is allowed.
50*/
51#define PFS_LOCK_ALLOCATED 0x02
52
53#define VERSION_MASK 0xFFFFFFFC
54#define STATE_MASK 0x00000003
55#define VERSION_INC 4
56
57/**
58 A 'lock' protecting performance schema internal buffers.
59 This lock is used to mark the state of a record.
60 Access to the record is not enforced here,
61 it's up to the readers and writers to look at the record state
62 before making an actual read or write operation.
63*/
64struct pfs_lock
65{
66 /**
67 The record internal version and state
68 @sa PFS_LOCK_FREE
69 @sa PFS_LOCK_DIRTY
70 @sa PFS_LOCK_ALLOCATED
71 The version number is to transform the 'ABA' problem
72 (see http://en.wikipedia.org/wiki/ABA_problem)
73 into an 'A(n)BA(n + 1)' problem, where 'n' is the m_version number.
74 When the performance schema instrumentation deletes a record,
75 then create a different record reusing the same memory allocation,
76 the version number is incremented, so that a reader can detect that
77 the record was changed. Note that the version number is never
78 reset to zero when a new record is created.
79 The version number is stored in the high 30 bits.
80 The state is stored in the low 2 bits.
81 */
82 volatile uint32 m_version_state;
83
84 /** Returns true if the record is free. */
85 bool is_free(void)
86 {
87 uint32 copy= m_version_state; /* non volatile copy, and dirty read */
88 return ((copy & STATE_MASK) == PFS_LOCK_FREE);
89 }
90
91 /** Returns true if the record contains values that can be read. */
92 bool is_populated(void)
93 {
94 uint32 copy= m_version_state; /* non volatile copy, and dirty read */
95 return ((copy & STATE_MASK) == PFS_LOCK_ALLOCATED);
96 }
97
98 /**
99 Execute a free to dirty transition.
100 This transition is safe to execute concurrently by multiple writers.
101 Only one writer will succeed to acquire the record.
102 @return true if the operation succeed
103 */
104 bool free_to_dirty(void)
105 {
106 uint32 copy= m_version_state; /* non volatile copy, and dirty read */
107 uint32 old_val= (copy & VERSION_MASK) + PFS_LOCK_FREE;
108 uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_DIRTY;
109
110 return (PFS_atomic::cas_u32(&m_version_state, &old_val, new_val));
111 }
112
113 /**
114 Execute an allocated to dirty transition.
115 This transition should be executed by the writer that owns the record,
116 before the record is modified.
117 */
118 void allocated_to_dirty(void)
119 {
120 uint32 copy= PFS_atomic::load_u32(&m_version_state);
121 /* Make sure the record was ALLOCATED. */
122 DBUG_ASSERT((copy & STATE_MASK) == PFS_LOCK_ALLOCATED);
123 /* Keep the same version, set the DIRTY state */
124 uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_DIRTY;
125 /* We own the record, no need to use compare and swap. */
126 PFS_atomic::store_u32(&m_version_state, new_val);
127 }
128
129 /**
130 Execute a dirty to allocated transition.
131 This transition should be executed by the writer that owns the record,
132 after the record is in a state ready to be read.
133 */
134 void dirty_to_allocated(void)
135 {
136 uint32 copy= PFS_atomic::load_u32(&m_version_state);
137 /* Make sure the record was DIRTY. */
138 DBUG_ASSERT((copy & STATE_MASK) == PFS_LOCK_DIRTY);
139 /* Increment the version, set the ALLOCATED state */
140 uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_ALLOCATED;
141 PFS_atomic::store_u32(&m_version_state, new_val);
142 }
143
144 /**
145 Initialize a lock to allocated.
146 This transition should be executed by the writer that owns the record and the lock,
147 after the record is in a state ready to be read.
148 */
149 void set_allocated(void)
150 {
151 /* Do not set the version to 0, read the previous value. */
152 uint32 copy= PFS_atomic::load_u32(&m_version_state);
153 /* Increment the version, set the ALLOCATED state */
154 uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_ALLOCATED;
155 PFS_atomic::store_u32(&m_version_state, new_val);
156 }
157
158 /**
159 Initialize a lock to dirty.
160 */
161 void set_dirty(void)
162 {
163 /* Do not set the version to 0, read the previous value. */
164 uint32 copy= PFS_atomic::load_u32(&m_version_state);
165 /* Increment the version, set the DIRTY state */
166 uint32 new_val= (copy & VERSION_MASK) + VERSION_INC + PFS_LOCK_DIRTY;
167 PFS_atomic::store_u32(&m_version_state, new_val);
168 }
169
170 /**
171 Execute a dirty to free transition.
172 This transition should be executed by the writer that owns the record.
173 */
174 void dirty_to_free(void)
175 {
176 uint32 copy= PFS_atomic::load_u32(&m_version_state);
177 /* Make sure the record was DIRTY. */
178 DBUG_ASSERT((copy & STATE_MASK) == PFS_LOCK_DIRTY);
179 /* Keep the same version, set the FREE state */
180 uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_FREE;
181 PFS_atomic::store_u32(&m_version_state, new_val);
182 }
183
184 /**
185 Execute an allocated to free transition.
186 This transition should be executed by the writer that owns the record.
187 */
188 void allocated_to_free(void)
189 {
190 /*
191 If this record is not in the ALLOCATED state and the caller is trying
192 to free it, this is a bug: the caller is confused,
193 and potentially damaging data owned by another thread or object.
194 The correct assert to use here to guarantee data integrity is simply:
195 DBUG_ASSERT(m_state == PFS_LOCK_ALLOCATED);
196 */
197 uint32 copy= PFS_atomic::load_u32(&m_version_state);
198 /* Make sure the record was ALLOCATED. */
199 DBUG_ASSERT(((copy & STATE_MASK) == PFS_LOCK_ALLOCATED));
200 /* Keep the same version, set the FREE state */
201 uint32 new_val= (copy & VERSION_MASK) + PFS_LOCK_FREE;
202 PFS_atomic::store_u32(&m_version_state, new_val);
203 }
204
205 /**
206 Start an optimistic read operation.
207 @sa end_optimist_lock.
208 */
209 void begin_optimistic_lock(struct pfs_lock *copy)
210 {
211 copy->m_version_state= PFS_atomic::load_u32(&m_version_state);
212 }
213
214 /**
215 End an optimistic read operation.
216 @sa begin_optimist_lock.
217 @return true if the data read is safe to use.
218 */
219 bool end_optimistic_lock(struct pfs_lock *copy)
220 {
221 /* Check there was valid data to look at. */
222 if ((copy->m_version_state & STATE_MASK) != PFS_LOCK_ALLOCATED)
223 return false;
224
225 /* Check the version + state has not changed. */
226 if (copy->m_version_state != PFS_atomic::load_u32(&m_version_state))
227 return false;
228
229 return true;
230 }
231
232 uint32 get_version()
233 {
234 return (PFS_atomic::load_u32(&m_version_state) & VERSION_MASK);
235 }
236};
237
238
239/** @} */
240#endif
241
242