393 lines
12 KiB
C
393 lines
12 KiB
C
/*****************************************************************************
|
|
|
|
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<lock_t *>(
|
|
HASH_GET_FIRST(lock_hash, lock_rec_hash(space, page_no)));
|
|
lock != NULL; lock = static_cast<lock_t *>(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<lock_t *>(HASH_GET_FIRST(lock_hash, hash));
|
|
lock != NULL; lock = static_cast<lock_t *>(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<const lock_t *>(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<enum lock_mode>(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 <space, page_no, heap_no> 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: */
|