1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 2007, 2014, Oracle and/or its affiliates. All Rights Reserved. |
4 | |
5 | This program is free software; you can redistribute it and/or modify it under |
6 | the terms of the GNU General Public License as published by the Free Software |
7 | Foundation; version 2 of the License. |
8 | |
9 | This program is distributed in the hope that it will be useful, but WITHOUT |
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
11 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License along with |
14 | this program; if not, write to the Free Software Foundation, Inc., |
15 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
16 | |
17 | *****************************************************************************/ |
18 | |
19 | /**************************************************//** |
20 | @file include/lock0priv.ic |
21 | Lock module internal inline methods. |
22 | |
23 | Created July 16, 2007 Vasil Dimov |
24 | *******************************************************/ |
25 | |
26 | /* This file contains only methods which are used in |
27 | lock/lock0* files, other than lock/lock0lock.cc. |
28 | I.e. lock/lock0lock.cc contains more internal inline |
29 | methods but they are used only in that file. */ |
30 | |
31 | #ifndef LOCK_MODULE_IMPLEMENTATION |
32 | #error Do not include lock0priv.ic outside of the lock/ module |
33 | #endif |
34 | |
35 | #include "row0row.h" |
36 | |
37 | /*********************************************************************//** |
38 | Gets the type of a lock. |
39 | @return LOCK_TABLE or LOCK_REC */ |
40 | UNIV_INLINE |
41 | ulint |
42 | lock_get_type_low( |
43 | /*==============*/ |
44 | const lock_t* lock) /*!< in: lock */ |
45 | { |
46 | ut_ad(lock); |
47 | |
48 | return(lock->type_mode & LOCK_TYPE_MASK); |
49 | } |
50 | |
51 | /*********************************************************************//** |
52 | Checks if some transaction has an implicit x-lock on a record in a clustered |
53 | index. |
54 | @return transaction id of the transaction which has the x-lock, or 0 */ |
55 | UNIV_INLINE |
56 | trx_id_t |
57 | lock_clust_rec_some_has_impl( |
58 | /*=========================*/ |
59 | const rec_t* rec, /*!< in: user record */ |
60 | const dict_index_t* index, /*!< in: clustered index */ |
61 | const ulint* offsets)/*!< in: rec_get_offsets(rec, index) */ |
62 | { |
63 | ut_ad(dict_index_is_clust(index)); |
64 | ut_ad(page_rec_is_user_rec(rec)); |
65 | |
66 | return(row_get_rec_trx_id(rec, index, offsets)); |
67 | } |
68 | |
69 | /*********************************************************************//** |
70 | Gets the number of bits in a record lock bitmap. |
71 | @return number of bits */ |
72 | UNIV_INLINE |
73 | ulint |
74 | lock_rec_get_n_bits( |
75 | /*================*/ |
76 | const lock_t* lock) /*!< in: record lock */ |
77 | { |
78 | return(lock->un_member.rec_lock.n_bits); |
79 | } |
80 | |
81 | /**********************************************************************//** |
82 | Sets the nth bit of a record lock to TRUE. */ |
83 | UNIV_INLINE |
84 | void |
85 | lock_rec_set_nth_bit( |
86 | /*=================*/ |
87 | lock_t* lock, /*!< in: record lock */ |
88 | ulint i) /*!< in: index of the bit */ |
89 | { |
90 | ulint byte_index; |
91 | ulint bit_index; |
92 | |
93 | ut_ad(lock); |
94 | ut_ad(lock_get_type_low(lock) == LOCK_REC); |
95 | ut_ad(i < lock->un_member.rec_lock.n_bits); |
96 | |
97 | byte_index = i / 8; |
98 | bit_index = i % 8; |
99 | |
100 | ((byte*) &lock[1])[byte_index] |= 1 << bit_index; |
101 | |
102 | ++lock->trx->lock.n_rec_locks; |
103 | } |
104 | |
105 | /*********************************************************************//** |
106 | Gets the first or next record lock on a page. |
107 | @return next lock, NULL if none exists */ |
108 | UNIV_INLINE |
109 | lock_t* |
110 | lock_rec_get_next_on_page( |
111 | /*======================*/ |
112 | lock_t* lock) /*!< in: a record lock */ |
113 | { |
114 | return((lock_t*) lock_rec_get_next_on_page_const(lock)); |
115 | } |
116 | |
117 | /*********************************************************************//** |
118 | Gets the first record lock on a page, where the page is identified by its |
119 | file address. |
120 | @return first lock, NULL if none exists */ |
121 | UNIV_INLINE |
122 | lock_t* |
123 | lock_rec_get_first_on_page_addr( |
124 | /*============================*/ |
125 | hash_table_t* lock_hash, /* Lock hash table */ |
126 | ulint space, /*!< in: space */ |
127 | ulint page_no) /*!< in: page number */ |
128 | { |
129 | ut_ad(lock_mutex_own()); |
130 | |
131 | for (lock_t* lock = static_cast<lock_t*>( |
132 | HASH_GET_FIRST(lock_hash, |
133 | lock_rec_hash(space, page_no))); |
134 | lock != NULL; |
135 | lock = static_cast<lock_t*>(HASH_GET_NEXT(hash, lock))) { |
136 | |
137 | if (lock->un_member.rec_lock.space == space |
138 | && lock->un_member.rec_lock.page_no == page_no) { |
139 | |
140 | return(lock); |
141 | } |
142 | } |
143 | |
144 | return(NULL); |
145 | } |
146 | |
147 | /*********************************************************************//** |
148 | Gets the first record lock on a page, where the page is identified by a |
149 | pointer to it. |
150 | @return first lock, NULL if none exists */ |
151 | UNIV_INLINE |
152 | lock_t* |
153 | lock_rec_get_first_on_page( |
154 | /*=======================*/ |
155 | hash_table_t* lock_hash, /*!< in: lock hash table */ |
156 | const buf_block_t* block) /*!< in: buffer block */ |
157 | { |
158 | ut_ad(lock_mutex_own()); |
159 | |
160 | ulint space = block->page.id.space(); |
161 | ulint page_no = block->page.id.page_no(); |
162 | ulint hash = buf_block_get_lock_hash_val(block); |
163 | |
164 | for (lock_t* lock = static_cast<lock_t*>( |
165 | HASH_GET_FIRST(lock_hash, hash)); |
166 | lock != NULL; |
167 | lock = static_cast<lock_t*>(HASH_GET_NEXT(hash, lock))) { |
168 | |
169 | if (lock->un_member.rec_lock.space == space |
170 | && lock->un_member.rec_lock.page_no == page_no) { |
171 | |
172 | return(lock); |
173 | } |
174 | } |
175 | |
176 | return(NULL); |
177 | } |
178 | |
179 | /*********************************************************************//** |
180 | Gets the next explicit lock request on a record. |
181 | @return next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED */ |
182 | UNIV_INLINE |
183 | lock_t* |
184 | lock_rec_get_next( |
185 | /*==============*/ |
186 | ulint heap_no,/*!< in: heap number of the record */ |
187 | lock_t* lock) /*!< in: lock */ |
188 | { |
189 | ut_ad(lock_mutex_own()); |
190 | |
191 | do { |
192 | ut_ad(lock_get_type_low(lock) == LOCK_REC); |
193 | lock = lock_rec_get_next_on_page(lock); |
194 | } while (lock && !lock_rec_get_nth_bit(lock, heap_no)); |
195 | |
196 | return(lock); |
197 | } |
198 | |
199 | /*********************************************************************//** |
200 | Gets the next explicit lock request on a record. |
201 | @return next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED */ |
202 | UNIV_INLINE |
203 | const lock_t* |
204 | lock_rec_get_next_const( |
205 | /*====================*/ |
206 | ulint heap_no,/*!< in: heap number of the record */ |
207 | const lock_t* lock) /*!< in: lock */ |
208 | { |
209 | return(lock_rec_get_next(heap_no, (lock_t*) lock)); |
210 | } |
211 | |
212 | /*********************************************************************//** |
213 | Gets the first explicit lock request on a record. |
214 | @return first lock, NULL if none exists */ |
215 | UNIV_INLINE |
216 | lock_t* |
217 | lock_rec_get_first( |
218 | /*===============*/ |
219 | hash_table_t* hash, /*!< in: hash chain the lock on */ |
220 | const buf_block_t* block, /*!< in: block containing the record */ |
221 | ulint heap_no)/*!< in: heap number of the record */ |
222 | { |
223 | ut_ad(lock_mutex_own()); |
224 | |
225 | for (lock_t* lock = lock_rec_get_first_on_page(hash, block); lock; |
226 | lock = lock_rec_get_next_on_page(lock)) { |
227 | if (lock_rec_get_nth_bit(lock, heap_no)) { |
228 | return(lock); |
229 | } |
230 | } |
231 | |
232 | return(NULL); |
233 | } |
234 | |
235 | /*********************************************************************//** |
236 | Gets the nth bit of a record lock. |
237 | @return TRUE if bit set also if i == ULINT_UNDEFINED return FALSE*/ |
238 | UNIV_INLINE |
239 | ibool |
240 | lock_rec_get_nth_bit( |
241 | /*=================*/ |
242 | const lock_t* lock, /*!< in: record lock */ |
243 | ulint i) /*!< in: index of the bit */ |
244 | { |
245 | const byte* b; |
246 | |
247 | ut_ad(lock); |
248 | ut_ad(lock_get_type_low(lock) == LOCK_REC); |
249 | |
250 | if (i >= lock->un_member.rec_lock.n_bits) { |
251 | |
252 | return(FALSE); |
253 | } |
254 | |
255 | b = ((const byte*) &lock[1]) + (i / 8); |
256 | |
257 | return(1 & *b >> (i % 8)); |
258 | } |
259 | |
260 | /*********************************************************************//** |
261 | Gets the first or next record lock on a page. |
262 | @return next lock, NULL if none exists */ |
263 | UNIV_INLINE |
264 | const lock_t* |
265 | lock_rec_get_next_on_page_const( |
266 | /*============================*/ |
267 | const lock_t* lock) /*!< in: a record lock */ |
268 | { |
269 | ut_ad(lock_mutex_own()); |
270 | ut_ad(lock_get_type_low(lock) == LOCK_REC); |
271 | |
272 | ulint space = lock->un_member.rec_lock.space; |
273 | ulint page_no = lock->un_member.rec_lock.page_no; |
274 | |
275 | while ((lock = static_cast<const lock_t*>(HASH_GET_NEXT(hash, lock))) |
276 | != NULL) { |
277 | |
278 | if (lock->un_member.rec_lock.space == space |
279 | && lock->un_member.rec_lock.page_no == page_no) { |
280 | |
281 | return(lock); |
282 | } |
283 | } |
284 | |
285 | return(NULL); |
286 | } |
287 | |
288 | /*********************************************************************//** |
289 | Gets the mode of a lock. |
290 | @return mode */ |
291 | UNIV_INLINE |
292 | enum lock_mode |
293 | lock_get_mode( |
294 | /*==========*/ |
295 | const lock_t* lock) /*!< in: lock */ |
296 | { |
297 | ut_ad(lock); |
298 | |
299 | return(static_cast<enum lock_mode>(lock->type_mode & LOCK_MODE_MASK)); |
300 | } |
301 | |
302 | /*********************************************************************//** |
303 | Calculates if lock mode 1 is compatible with lock mode 2. |
304 | @return nonzero if mode1 compatible with mode2 */ |
305 | UNIV_INLINE |
306 | ulint |
307 | lock_mode_compatible( |
308 | /*=================*/ |
309 | enum lock_mode mode1, /*!< in: lock mode */ |
310 | enum lock_mode mode2) /*!< in: lock mode */ |
311 | { |
312 | ut_ad((ulint) mode1 < lock_types); |
313 | ut_ad((ulint) mode2 < lock_types); |
314 | |
315 | return(lock_compatibility_matrix[mode1][mode2]); |
316 | } |
317 | |
318 | /*********************************************************************//** |
319 | Calculates if lock mode 1 is stronger or equal to lock mode 2. |
320 | @return nonzero if mode1 stronger or equal to mode2 */ |
321 | UNIV_INLINE |
322 | ulint |
323 | lock_mode_stronger_or_eq( |
324 | /*=====================*/ |
325 | enum lock_mode mode1, /*!< in: lock mode */ |
326 | enum lock_mode mode2) /*!< in: lock mode */ |
327 | { |
328 | ut_ad((ulint) mode1 < lock_types); |
329 | ut_ad((ulint) mode2 < lock_types); |
330 | |
331 | return(lock_strength_matrix[mode1][mode2]); |
332 | } |
333 | |
334 | /*********************************************************************//** |
335 | Gets the wait flag of a lock. |
336 | @return LOCK_WAIT if waiting, 0 if not */ |
337 | UNIV_INLINE |
338 | ulint |
339 | lock_get_wait( |
340 | /*==========*/ |
341 | const lock_t* lock) /*!< in: lock */ |
342 | { |
343 | ut_ad(lock); |
344 | |
345 | return(lock->type_mode & LOCK_WAIT); |
346 | } |
347 | |
348 | /*********************************************************************//** |
349 | Looks for a suitable type record lock struct by the same trx on the same page. |
350 | This can be used to save space when a new record lock should be set on a page: |
351 | no new struct is needed, if a suitable old is found. |
352 | @return lock or NULL */ |
353 | UNIV_INLINE |
354 | lock_t* |
355 | lock_rec_find_similar_on_page( |
356 | /*==========================*/ |
357 | ulint type_mode, /*!< in: lock type_mode field */ |
358 | ulint heap_no, /*!< in: heap number of the record */ |
359 | lock_t* lock, /*!< in: lock_rec_get_first_on_page() */ |
360 | const trx_t* trx) /*!< in: transaction */ |
361 | { |
362 | ut_ad(lock_mutex_own()); |
363 | |
364 | for (/* No op */; |
365 | lock != NULL; |
366 | lock = lock_rec_get_next_on_page(lock)) { |
367 | |
368 | if (lock->trx == trx |
369 | && lock->type_mode == type_mode |
370 | && lock_rec_get_n_bits(lock) > heap_no) { |
371 | |
372 | return(lock); |
373 | } |
374 | } |
375 | |
376 | return(NULL); |
377 | } |
378 | |
379 | /*********************************************************************//** |
380 | Checks if a transaction has the specified table lock, or stronger. This |
381 | function should only be called by the thread that owns the transaction. |
382 | @return lock or NULL */ |
383 | UNIV_INLINE |
384 | const lock_t* |
385 | lock_table_has( |
386 | /*===========*/ |
387 | const trx_t* trx, /*!< in: transaction */ |
388 | const dict_table_t* table, /*!< in: table */ |
389 | lock_mode in_mode)/*!< in: lock mode */ |
390 | { |
391 | if (trx->lock.table_locks.empty()) { |
392 | return(NULL); |
393 | } |
394 | |
395 | typedef lock_pool_t::const_reverse_iterator iterator; |
396 | |
397 | iterator end = trx->lock.table_locks.rend(); |
398 | |
399 | /* Look for stronger locks the same trx already has on the table */ |
400 | |
401 | for (iterator it = trx->lock.table_locks.rbegin(); it != end; ++it) { |
402 | |
403 | const lock_t* lock = *it; |
404 | |
405 | if (lock == NULL) { |
406 | continue; |
407 | } |
408 | |
409 | lock_mode mode = lock_get_mode(lock); |
410 | |
411 | ut_ad(trx == lock->trx); |
412 | ut_ad(lock_get_type_low(lock) & LOCK_TABLE); |
413 | ut_ad(lock->un_member.tab_lock.table != NULL); |
414 | |
415 | if (table == lock->un_member.tab_lock.table |
416 | && lock_mode_stronger_or_eq(mode, in_mode)) { |
417 | |
418 | ut_ad(!lock_get_wait(lock)); |
419 | |
420 | return(lock); |
421 | } |
422 | } |
423 | |
424 | return(NULL); |
425 | } |
426 | |
427 | /* vim: set filetype=c: */ |
428 | |