/***************************************************************************** Copyright (c) 2007, 2019, Oracle and/or its affiliates. All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ /** @file include/lock0priv.ic Lock module internal inline methods. Created July 16, 2007 Vasil Dimov *******************************************************/ /* This file contains only methods which are used in lock/lock0* files, other than lock/lock0lock.cc. I.e. lock/lock0lock.cc contains more internal inline methods but they are used only in that file. */ #ifndef LOCK_MODULE_IMPLEMENTATION #error Do not include lock0priv.ic outside of the lock/ module #endif /** Gets the type of a lock. @return LOCK_TABLE or LOCK_REC */ UNIV_INLINE uint32_t lock_get_type_low(const lock_t *lock) /*!< in: lock */ { ut_ad(lock); return (lock->type_mode & LOCK_TYPE_MASK); } /** Checks if some transaction has an implicit x-lock on a record in a clustered index. @return transaction id of the transaction which has the x-lock, or 0 */ UNIV_INLINE trx_id_t lock_clust_rec_some_has_impl( const rec_t *rec, /*!< in: user record */ const dict_index_t *index, /*!< in: clustered index */ const ulint *offsets) /*!< in: rec_get_offsets(rec, index) */ { ut_ad(index->is_clustered()); ut_ad(page_rec_is_user_rec(rec)); return (row_get_rec_trx_id(rec, index, offsets)); } /** Gets the number of bits in a record lock bitmap. @return number of bits */ UNIV_INLINE ulint lock_rec_get_n_bits(const lock_t *lock) /*!< in: record lock */ { return (lock->rec_lock.n_bits); } /** Sets the nth bit of a record lock to TRUE. */ UNIV_INLINE void lock_rec_set_nth_bit(lock_t *lock, /*!< in: record lock */ ulint i) /*!< in: index of the bit */ { ulint byte_index; ulint bit_index; ut_ad(lock); ut_ad(lock_get_type_low(lock) == LOCK_REC); ut_ad(i < lock->rec_lock.n_bits); byte_index = i / 8; bit_index = i % 8; ((byte *)&lock[1])[byte_index] |= 1 << bit_index; lock->trx->lock.n_rec_locks.fetch_add(1, std::memory_order_relaxed); } /** Gets the first or next record lock on a page. @return next lock, NULL if none exists */ UNIV_INLINE lock_t *lock_rec_get_next_on_page(lock_t *lock) /*!< in: a record lock */ { return ((lock_t *)lock_rec_get_next_on_page_const(lock)); } /** Gets the first record lock on a page, where the page is identified by its file address. @return first lock, NULL if none exists */ UNIV_INLINE lock_t *lock_rec_get_first_on_page_addr( hash_table_t *lock_hash, /*!< in: Lock hash table */ space_id_t space, /*!< in: space */ page_no_t page_no) /*!< in: page number */ { ut_ad(lock_mutex_own()); for (lock_t *lock = static_cast( HASH_GET_FIRST(lock_hash, lock_rec_hash(space, page_no))); lock != NULL; lock = static_cast(HASH_GET_NEXT(hash, lock))) { if (lock->space_id() == space && lock->page_no() == page_no) { return (lock); } } return (NULL); } /** Gets the first record lock on a page, where the page is identified by a pointer to it. @return first lock, NULL if none exists */ UNIV_INLINE lock_t *lock_rec_get_first_on_page( hash_table_t *lock_hash, /*!< in: lock hash table */ const buf_block_t *block) /*!< in: buffer block */ { ut_ad(lock_mutex_own()); space_id_t space = block->page.id.space(); page_no_t page_no = block->page.id.page_no(); ulint hash = buf_block_get_lock_hash_val(block); for (lock_t *lock = static_cast(HASH_GET_FIRST(lock_hash, hash)); lock != NULL; lock = static_cast(HASH_GET_NEXT(hash, lock))) { if (lock->space_id() == space && lock->page_no() == page_no) { return (lock); } } return (NULL); } /** Gets the next explicit lock request on a record. @return next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED */ UNIV_INLINE lock_t *lock_rec_get_next(ulint heap_no, /*!< in: heap number of the record */ lock_t *lock) /*!< in: lock */ { ut_ad(lock_mutex_own()); do { ut_ad(lock_get_type_low(lock) == LOCK_REC); lock = lock_rec_get_next_on_page(lock); } while (lock != nullptr && !lock_rec_get_nth_bit(lock, heap_no)); return (lock); } /** Gets the next explicit lock request on a record. @return next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED */ UNIV_INLINE const lock_t *lock_rec_get_next_const( ulint heap_no, /*!< in: heap number of the record */ const lock_t *lock) /*!< in: lock */ { return (lock_rec_get_next(heap_no, (lock_t *)lock)); } /** Gets the first explicit lock request on a record. @param[in] hash Record hash @param[in] rec_id Record ID @return first lock, nullptr if none exists */ UNIV_INLINE lock_t *lock_rec_get_first(hash_table_t *hash, const RecID &rec_id) { ut_ad(lock_mutex_own()); auto lock = lock_rec_get_first_on_page_addr(hash, rec_id.m_space_id, rec_id.m_page_no); if (lock != nullptr && !lock_rec_get_nth_bit(lock, rec_id.m_heap_no)) { lock = lock_rec_get_next(rec_id.m_heap_no, lock); } return (lock); } /** Gets the first explicit lock request on a record. @return first lock, NULL if none exists */ UNIV_INLINE lock_t *lock_rec_get_first( hash_table_t *hash, /*!< in: hash chain the lock on */ const buf_block_t *block, /*!< in: block containing the record */ ulint heap_no) /*!< in: heap number of the record */ { ut_ad(lock_mutex_own()); for (lock_t *lock = lock_rec_get_first_on_page(hash, block); lock; lock = lock_rec_get_next_on_page(lock)) { if (lock_rec_get_nth_bit(lock, heap_no)) { return (lock); } } return (NULL); } /** Gets the nth bit of a record lock. @param[in] lock Record lock @param[in] i Index of the bit to check @return true if bit set also if i == ULINT_UNDEFINED return false */ UNIV_INLINE bool lock_rec_get_nth_bit(const lock_t *lock, ulint i) { const byte *b; ut_ad(lock); ut_ad(lock_get_type_low(lock) == LOCK_REC); if (i >= lock->rec_lock.n_bits) { return (false); } b = ((const byte *)&lock[1]) + (i / 8); return (true & *b >> (i % 8)); } /** Gets the first or next record lock on a page. @return next lock, NULL if none exists */ UNIV_INLINE const lock_t *lock_rec_get_next_on_page_const( const lock_t *lock) /*!< in: a record lock */ { ut_ad(lock_mutex_own()); ut_ad(lock_get_type_low(lock) == LOCK_REC); space_id_t space = lock->space_id(); page_no_t page_no = lock->page_no(); while ((lock = static_cast(HASH_GET_NEXT(hash, lock))) != NULL) { if (lock->space_id() == space && lock->page_no() == page_no) { return (lock); } } return (NULL); } /** Gets the mode of a lock. @return mode */ UNIV_INLINE enum lock_mode lock_get_mode(const lock_t *lock) /*!< in: lock */ { ut_ad(lock); return (static_cast(lock->type_mode & LOCK_MODE_MASK)); } /** Calculates if lock mode 1 is compatible with lock mode 2. @return nonzero if mode1 compatible with mode2 */ UNIV_INLINE ulint lock_mode_compatible(enum lock_mode mode1, /*!< in: lock mode */ enum lock_mode mode2) /*!< in: lock mode */ { ut_ad((ulint)mode1 < lock_types); ut_ad((ulint)mode2 < lock_types); return (lock_compatibility_matrix[mode1][mode2]); } UNIV_INLINE bool lock_mode_stronger_or_eq(enum lock_mode mode1, enum lock_mode mode2) { ut_ad((ulint)mode1 < lock_types); ut_ad((ulint)mode2 < lock_types); return (lock_strength_matrix[mode1][mode2] != 0); } /** Gets the wait flag of a lock. @return LOCK_WAIT if waiting, 0 if not */ UNIV_INLINE ulint lock_get_wait(const lock_t *lock) /*!< in: lock */ { ut_ad(lock); return (lock->type_mode & LOCK_WAIT); } /** The back pointer to a waiting lock request in the transaction is set to NULL and the wait bit in lock type_mode is reset. */ UNIV_INLINE void lock_reset_lock_and_trx_wait(lock_t *lock) /*!< in/out: record lock */ { ut_ad(lock->trx->lock.wait_lock == lock); ut_ad(lock_get_wait(lock)); ut_ad(lock_mutex_own()); /* We intentionally don't clear trx->lock.blocking_trx here, as lock_reset_lock_and_trx_wait() is called also during movements of locks from one page to another, which does not really change the structure of the wait-for graph. Instead the lock_reset_wait_and_release_thread_if_suspended() is responsible for clearing the blocking_trx field once it is sure that we really want to remove the edge from the wait-for graph.*/ lock->trx->lock.wait_lock = NULL; /* We intentionally don't clear lock->trx->lock.wait_lock_type here, to make it easier to obtain stats about the last wait in lock_wait_suspend_thread(). @see trx_lock_t::wait_lock_type for more detailed explanation. */ lock->type_mode &= ~LOCK_WAIT; } /** Looks for a suitable type record lock struct by the same trx on the same page. This can be used to save space when a new record lock should be set on a page: no new struct is needed, if a suitable old is found. @return lock or NULL */ UNIV_INLINE lock_t *lock_rec_find_similar_on_page( ulint type_mode, /*!< in: lock type_mode field */ ulint heap_no, /*!< in: heap number of the record */ lock_t *lock, /*!< in: lock_rec_get_first_on_page() */ const trx_t *trx) /*!< in: transaction */ { ut_ad(lock_mutex_own()); for (/* No op */; lock != NULL; lock = lock_rec_get_next_on_page(lock)) { if (lock->trx == trx && lock->type_mode == type_mode && lock_rec_get_n_bits(lock) > heap_no) { return (lock); } } return (NULL); } UNIV_INLINE bool lock_table_has(const trx_t *trx, const dict_table_t *table, lock_mode in_mode) { ut_ad(!trx_mutex_own(trx)); trx_mutex_enter(trx); if (trx->lock.table_locks.empty()) { trx_mutex_exit(trx); return (false); } typedef lock_pool_t::const_reverse_iterator iterator; iterator end = trx->lock.table_locks.rend(); /* Look for stronger locks the same trx already has on the table */ for (iterator it = trx->lock.table_locks.rbegin(); it != end; ++it) { const lock_t *lock = *it; ut_ad(lock != nullptr); lock_mode mode = lock_get_mode(lock); ut_ad(trx == lock->trx); ut_ad(lock_get_type_low(lock) & LOCK_TABLE); ut_ad(lock->tab_lock.table != NULL); if (table == lock->tab_lock.table && lock_mode_stronger_or_eq(mode, in_mode)) { ut_ad(!lock_get_wait(lock)); trx_mutex_exit(trx); return (true); } } trx_mutex_exit(trx); return (false); } /* Check if the rec id matches the lock instance. @param[i] lock Lock to compare with @return true if matches the lock. */ bool RecID::matches(const lock_t *lock) const { return (lock->rec_lock.space == m_space_id && lock->rec_lock.page_no == m_page_no && lock_rec_get_nth_bit(lock, m_heap_no)); } /* vim: set filetype=c: */