1/***************************************************************************** 2 3Copyright (c) 2007, 2020, Oracle and/or its affiliates. All Rights Reserved. 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 also distributed with certain software (including but not 10limited to OpenSSL) that is licensed under separate terms, as designated in a 11particular file or component or in included license documentation. The authors 12of MySQL hereby grant you an additional permission to link the program and 13your derivative works with the separately licensed software that they have 14included with MySQL. 15 16This program is distributed in the hope that it will be useful, but WITHOUT 17ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, 19for more details. 20 21You should have received a copy of the GNU General Public License along with 22this program; if not, write to the Free Software Foundation, Inc., 2351 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 25*****************************************************************************/ 26 27/** @file include/lock0priv.ic 28 Lock module internal inline methods. 29 30 Created July 16, 2007 Vasil Dimov 31 *******************************************************/ 32 33/* This file contains only methods which are used in 34lock/lock0* files, other than lock/lock0lock.cc. 35I.e. lock/lock0lock.cc contains more internal inline 36methods but they are used only in that file. */ 37 38#ifndef LOCK_MODULE_IMPLEMENTATION 39#error Do not include lock0priv.ic outside of the lock/ module 40#endif 41 42/** Gets the type of a lock. 43 @return LOCK_TABLE or LOCK_REC */ 44UNIV_INLINE 45uint32_t lock_get_type_low(const lock_t *lock) /*!< in: lock */ 46{ 47 ut_ad(lock); 48 49 return (lock->type_mode & LOCK_TYPE_MASK); 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 */ 55UNIV_INLINE 56trx_id_t lock_clust_rec_some_has_impl( 57 const rec_t *rec, /*!< in: user record */ 58 const dict_index_t *index, /*!< in: clustered index */ 59 const ulint *offsets) /*!< in: rec_get_offsets(rec, index) */ 60{ 61 ut_ad(index->is_clustered()); 62 ut_ad(page_rec_is_user_rec(rec)); 63 64 return (row_get_rec_trx_id(rec, index, offsets)); 65} 66 67/** Gets the number of bits in a record lock bitmap. 68 @return number of bits */ 69UNIV_INLINE 70ulint lock_rec_get_n_bits(const lock_t *lock) /*!< in: record lock */ 71{ 72 return (lock->rec_lock.n_bits); 73} 74 75/** Sets the nth bit of a record lock to TRUE. */ 76UNIV_INLINE 77void lock_rec_set_nth_bit(lock_t *lock, /*!< in: record lock */ 78 ulint i) /*!< in: index of the bit */ 79{ 80 ulint byte_index; 81 ulint bit_index; 82 83 ut_ad(lock); 84 ut_ad(lock_get_type_low(lock) == LOCK_REC); 85 ut_ad(i < lock->rec_lock.n_bits); 86 87 byte_index = i / 8; 88 bit_index = i % 8; 89 90 ((byte *)&lock[1])[byte_index] |= 1 << bit_index; 91 92 lock->trx->lock.n_rec_locks.fetch_add(1, std::memory_order_relaxed); 93} 94 95/** Gets the first or next record lock on a page. 96 @return next lock, NULL if none exists */ 97UNIV_INLINE 98lock_t *lock_rec_get_next_on_page(lock_t *lock) /*!< in: a record lock */ 99{ 100 return ((lock_t *)lock_rec_get_next_on_page_const(lock)); 101} 102 103UNIV_INLINE 104lock_t *lock_rec_get_first_on_page_addr(hash_table_t *lock_hash, 105 const page_id_t &page_id) { 106 ut_ad(locksys::owns_page_shard(page_id)); 107 108 for (lock_t *lock = static_cast<lock_t *>( 109 HASH_GET_FIRST(lock_hash, lock_rec_hash(page_id))); 110 lock != nullptr; 111 lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, lock))) { 112 if (page_id == lock->rec_lock.page_id) { 113 return (lock); 114 } 115 } 116 117 return (nullptr); 118} 119 120/** Gets the first record lock on a page, where the page is identified by a 121 pointer to it. 122 @return first lock, NULL if none exists */ 123UNIV_INLINE 124lock_t *lock_rec_get_first_on_page( 125 hash_table_t *lock_hash, /*!< in: lock hash table */ 126 const buf_block_t *block) /*!< in: buffer block */ 127{ 128 ut_ad(locksys::owns_page_shard(block->get_page_id())); 129 130 const page_id_t page_id = block->page.id; 131 ulint hash = buf_block_get_lock_hash_val(block); 132 133 for (lock_t *lock = static_cast<lock_t *>(HASH_GET_FIRST(lock_hash, hash)); 134 lock != nullptr; 135 lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, lock))) { 136 if (page_id == lock->rec_lock.page_id) { 137 return (lock); 138 } 139 } 140 141 return (nullptr); 142} 143 144/** Gets the next explicit lock request on a record. 145 @return next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED 146 */ 147UNIV_INLINE 148lock_t *lock_rec_get_next(ulint heap_no, /*!< in: heap number of the record */ 149 lock_t *lock) /*!< in: lock */ 150{ 151 ut_ad(locksys::owns_page_shard(lock->rec_lock.page_id)); 152 153 do { 154 ut_ad(lock_get_type_low(lock) == LOCK_REC); 155 lock = lock_rec_get_next_on_page(lock); 156 } while (lock != nullptr && !lock_rec_get_nth_bit(lock, heap_no)); 157 158 return (lock); 159} 160 161/** Gets the next explicit lock request on a record. 162 @return next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED 163 */ 164UNIV_INLINE 165const lock_t *lock_rec_get_next_const( 166 ulint heap_no, /*!< in: heap number of the record */ 167 const lock_t *lock) /*!< in: lock */ 168{ 169 return (lock_rec_get_next(heap_no, (lock_t *)lock)); 170} 171 172/** Gets the first explicit lock request on a record. 173 @return first lock, NULL if none exists */ 174UNIV_INLINE 175lock_t *lock_rec_get_first( 176 hash_table_t *hash, /*!< in: hash chain the lock on */ 177 const buf_block_t *block, /*!< in: block containing the record */ 178 ulint heap_no) /*!< in: heap number of the record */ 179{ 180 ut_ad(locksys::owns_page_shard(block->get_page_id())); 181 182 for (lock_t *lock = lock_rec_get_first_on_page(hash, block); lock; 183 lock = lock_rec_get_next_on_page(lock)) { 184 if (lock_rec_get_nth_bit(lock, heap_no)) { 185 return (lock); 186 } 187 } 188 189 return (nullptr); 190} 191 192/** Gets the nth bit of a record lock. 193@param[in] lock Record lock 194@param[in] i Index of the bit to check 195@return true if bit set also if i == ULINT_UNDEFINED return false */ 196UNIV_INLINE 197bool lock_rec_get_nth_bit(const lock_t *lock, ulint i) { 198 const byte *b; 199 200 ut_ad(lock); 201 ut_ad(lock_get_type_low(lock) == LOCK_REC); 202 203 if (i >= lock->rec_lock.n_bits) { 204 return (false); 205 } 206 207 b = ((const byte *)&lock[1]) + (i / 8); 208 209 return (true & *b >> (i % 8)); 210} 211 212/** Gets the first or next record lock on a page. 213 @return next lock, NULL if none exists */ 214UNIV_INLINE 215const lock_t *lock_rec_get_next_on_page_const( 216 const lock_t *lock) /*!< in: a record lock */ 217{ 218 ut_ad(lock_get_type_low(lock) == LOCK_REC); 219 const auto page_id = lock->rec_lock.page_id; 220 ut_ad(locksys::owns_page_shard(page_id)); 221 222 while ((lock = static_cast<const lock_t *>(HASH_GET_NEXT(hash, lock))) != 223 nullptr) { 224 if (page_id == lock->rec_lock.page_id) { 225 return (lock); 226 } 227 } 228 229 return (nullptr); 230} 231 232/** Gets the mode of a lock. 233 @return mode */ 234UNIV_INLINE 235enum lock_mode lock_get_mode(const lock_t *lock) /*!< in: lock */ 236{ 237 ut_ad(lock); 238 239 return (static_cast<enum lock_mode>(lock->type_mode & LOCK_MODE_MASK)); 240} 241 242/** Calculates if lock mode 1 is compatible with lock mode 2. 243 @return nonzero if mode1 compatible with mode2 */ 244UNIV_INLINE 245ulint lock_mode_compatible(enum lock_mode mode1, /*!< in: lock mode */ 246 enum lock_mode mode2) /*!< in: lock mode */ 247{ 248 ut_ad((ulint)mode1 < lock_types); 249 ut_ad((ulint)mode2 < lock_types); 250 251 return (lock_compatibility_matrix[mode1][mode2]); 252} 253 254UNIV_INLINE 255bool lock_mode_stronger_or_eq(enum lock_mode mode1, enum lock_mode mode2) { 256 ut_ad((ulint)mode1 < lock_types); 257 ut_ad((ulint)mode2 < lock_types); 258 259 return (lock_strength_matrix[mode1][mode2] != 0); 260} 261 262/** Gets the wait flag of a lock. 263 @return LOCK_WAIT if waiting, 0 if not */ 264UNIV_INLINE 265ulint lock_get_wait(const lock_t *lock) /*!< in: lock */ 266{ 267 ut_ad(lock); 268 269 return (lock->type_mode & LOCK_WAIT); 270} 271 272/** The back pointer to a waiting lock request in the transaction is set to NULL 273 and the wait bit in lock type_mode is reset. */ 274UNIV_INLINE 275void lock_reset_lock_and_trx_wait(lock_t *lock) /*!< in/out: record lock */ 276{ 277 ut_ad(locksys::owns_lock_shard(lock)); 278 ut_ad(lock_get_wait(lock)); 279 ut_ad(lock->trx->lock.wait_lock == lock); 280 /* We intentionally don't clear trx->lock.blocking_trx here, as 281 lock_reset_lock_and_trx_wait() is called also during movements of locks from 282 one page to another, which does not really change the structure of the 283 wait-for graph. Instead the lock_reset_wait_and_release_thread_if_suspended() 284 is responsible for clearing the blocking_trx field once it is sure that 285 we really want to remove the edge from the wait-for graph.*/ 286 lock->trx->lock.wait_lock = nullptr; 287 288 /* We intentionally don't clear lock->trx->lock.wait_lock_type here, to make 289 it easier to obtain stats about the last wait in lock_wait_suspend_thread(). 290 @see trx_lock_t::wait_lock_type for more detailed explanation. */ 291 lock->type_mode &= ~LOCK_WAIT; 292} 293 294UNIV_INLINE 295bool lock_table_has(const trx_t *trx, const dict_table_t *table, 296 lock_mode in_mode) { 297 ut_ad(!trx_mutex_own(trx)); 298 trx_mutex_enter(trx); 299 ut_ad(trx_can_be_handled_by_current_thread(trx)); 300 301 if (trx->lock.table_locks.empty()) { 302 trx_mutex_exit(trx); 303 return (false); 304 } 305 306 typedef lock_pool_t::const_reverse_iterator iterator; 307 308 iterator end = trx->lock.table_locks.rend(); 309 310 /* Look for stronger locks the same trx already has on the table */ 311 312 for (iterator it = trx->lock.table_locks.rbegin(); it != end; ++it) { 313 const lock_t *lock = *it; 314 315 ut_ad(lock != nullptr); 316 317 lock_mode mode = lock_get_mode(lock); 318 319 ut_ad(trx == lock->trx); 320 ut_ad(lock_get_type_low(lock) & LOCK_TABLE); 321 ut_ad(lock->tab_lock.table != nullptr); 322 323 if (table == lock->tab_lock.table && 324 lock_mode_stronger_or_eq(mode, in_mode)) { 325 ut_ad(!lock_get_wait(lock)); 326 327 trx_mutex_exit(trx); 328 return (true); 329 } 330 } 331 332 trx_mutex_exit(trx); 333 return (false); 334} 335 336/* Check if the rec id matches the lock instance. 337@param[i] lock Lock to compare with 338@return true if <space, page_no, heap_no> matches the lock. */ 339bool RecID::matches(const lock_t *lock) const { 340 return (lock->rec_lock.page_id == get_page_id() && 341 lock_rec_get_nth_bit(lock, m_heap_no)); 342} 343 344/* vim: set filetype=c: */ 345