Commit 8d16da14 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-24789: Reduce lock_sys mutex contention further

lock_sys_t::deadlock_check(): Assume that only lock_sys.wait_mutex
is being held by the caller.

lock_sys_t::rd_lock_try(): New function.

lock_sys_t::cancel(trx_t*): Kill an active transaction that may be
holding a lock.

lock_sys_t::cancel(trx_t*, lock_t*): Cancel a waiting lock request.

lock_trx_handle_wait(): Avoid acquiring mutexes in some cases,
and in never acquire lock_sys.latch in exclusive mode.
This function is only invoked in a semi-consistent read
(locking a clustered index record only if it matches the search condition).
Normally, lock_wait() will take care of lock waits.

lock_wait(): Invoke the new function lock_sys_t::cancel() at the end,
to avoid acquiring exclusive lock_sys.latch.

lock_rec_other_trx_holds_expl(): Use LockGuard instead of LockMutexGuard.

lock_release_autoinc_locks(): Explicitly acquire table->lock_mutex,
in case only a shared lock_sys.latch is being held. Deadlock::report()
will still hold exclusive lock_sys.latch while invoking
lock_cancel_waiting_and_release().

lock_cancel_waiting_and_release(): Acquire trx->mutex in this function,
instead of expecting the caller to do so.

lock_unlock_table_autoinc(): Only acquire shared lock_sys.latch.

lock_table_has_locks(): Do not acquire lock_sys.latch at all.

Deadlock::check_and_resolve(): Only acquire shared lock_sys.latchm
for invoking lock_sys_t::cancel(trx, wait_lock).

innobase_query_caching_table_check_low(),
row_drop_tables_for_mysql_in_background(): Do not acquire lock_sys.latch.
parent ebb2db59
...@@ -2688,8 +2688,7 @@ the query cache. ...@@ -2688,8 +2688,7 @@ the query cache.
@param[in] trx transaction object @param[in] trx transaction object
@return whether the storing or retrieving from the query cache is permitted */ @return whether the storing or retrieving from the query cache is permitted */
static bool innobase_query_caching_table_check_low( static bool innobase_query_caching_table_check_low(
const dict_table_t* table, dict_table_t* table, trx_t* trx)
trx_t* trx)
{ {
/* The following conditions will decide the query cache /* The following conditions will decide the query cache
retrieval or storing into: retrieval or storing into:
...@@ -2714,8 +2713,10 @@ static bool innobase_query_caching_table_check_low( ...@@ -2714,8 +2713,10 @@ static bool innobase_query_caching_table_check_low(
return false; return false;
} }
LockMutexGuard g{SRW_LOCK_CALL}; table->lock_mutex_lock();
return UT_LIST_GET_LEN(table->locks) == 0; auto len= UT_LIST_GET_LEN(table->locks);
table->lock_mutex_unlock();
return len == 0;
} }
/** Checks if MySQL at the moment is allowed for this table to retrieve a /** Checks if MySQL at the moment is allowed for this table to retrieve a
...@@ -4483,21 +4484,7 @@ static void innobase_kill_query(handlerton*, THD *thd, enum thd_kill_levels) ...@@ -4483,21 +4484,7 @@ static void innobase_kill_query(handlerton*, THD *thd, enum thd_kill_levels)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
#endif /* WITH_WSREP */ #endif /* WITH_WSREP */
if (trx->lock.wait_lock) if (trx->lock.wait_lock)
{ lock_sys_t::cancel(trx);
{
LockMutexGuard g{SRW_LOCK_CALL};
mysql_mutex_lock(&lock_sys.wait_mutex);
if (lock_t *lock= trx->lock.wait_lock)
{
trx->mutex_lock();
trx->error_state= DB_INTERRUPTED;
lock_cancel_waiting_and_release(lock);
trx->mutex_unlock();
}
lock_sys.deadlock_check(true);
}
mysql_mutex_unlock(&lock_sys.wait_mutex);
}
} }
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
......
...@@ -504,15 +504,9 @@ lock_rec_get_index( ...@@ -504,15 +504,9 @@ lock_rec_get_index(
/*===============*/ /*===============*/
const lock_t* lock); /*!< in: lock */ const lock_t* lock); /*!< in: lock */
/*******************************************************************//** /** Check if there are any locks on a table.
Check if there are any locks (table or rec) against table. @return true if table has either table or record locks. */
@return TRUE if locks exist */ bool lock_table_has_locks(dict_table_t *table);
bool
lock_table_has_locks(
/*=================*/
const dict_table_t* table); /*!< in: check if there are any locks
held on records in this table or on the
table itself */
/** Wait for a lock to be released. /** Wait for a lock to be released.
@retval DB_DEADLOCK if this transaction was chosen as the deadlock victim @retval DB_DEADLOCK if this transaction was chosen as the deadlock victim
...@@ -528,15 +522,15 @@ void ...@@ -528,15 +522,15 @@ void
lock_unlock_table_autoinc( lock_unlock_table_autoinc(
/*======================*/ /*======================*/
trx_t* trx); /*!< in/out: transaction */ trx_t* trx); /*!< in/out: transaction */
/*********************************************************************//**
Check whether the transaction has already been rolled back because it /** Handle a pending lock wait (DB_LOCK_WAIT) in a semi-consistent read
was selected as a deadlock victim, or if it has to wait then cancel while holding a clustered index leaf page latch.
the wait lock. @param trx transaction that is or was waiting for a lock
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */ @retval DB_SUCCESS if the lock was granted
dberr_t @retval DB_DEADLOCK if the transaction must be aborted due to a deadlock
lock_trx_handle_wait( @retval DB_LOCK_WAIT if a lock wait would be necessary; the pending
/*=================*/ lock request was released */
trx_t* trx); /*!< in/out: trx lock state */ dberr_t lock_trx_handle_wait(trx_t *trx);
/*********************************************************************//** /*********************************************************************//**
Checks that a transaction id is sensible, i.e., not in the future. Checks that a transaction id is sensible, i.e., not in the future.
...@@ -775,6 +769,16 @@ class lock_sys_t ...@@ -775,6 +769,16 @@ class lock_sys_t
std::memory_order_relaxed)); std::memory_order_relaxed));
return true; return true;
} }
/** Try to acquire shared lock_sys.latch
@return whether the latch was acquired */
bool rd_lock_try()
{
ut_ad(!is_writer());
if (!latch.rd_lock_try()) return false;
ut_ad(!writer.load(std::memory_order_relaxed));
ut_d(readers.fetch_add(1, std::memory_order_relaxed));
return true;
}
/** Assert that wr_lock() has been invoked by this thread */ /** Assert that wr_lock() has been invoked by this thread */
void assert_locked() const { ut_ad(is_writer()); } void assert_locked() const { ut_ad(is_writer()); }
...@@ -816,10 +820,15 @@ class lock_sys_t ...@@ -816,10 +820,15 @@ class lock_sys_t
void close(); void close();
/** Check for deadlocks /** Check for deadlocks while holding only lock_sys.wait_mutex. */
@param locked lock_sys.is_writer() */ void deadlock_check();
static void deadlock_check(bool locked);
/** Cancel a waiting lock request.
@param lock waiting lock request
@param trx active transaction */
static void cancel(trx_t *trx, lock_t *lock);
/** Cancel a waiting lock request (if any) when killing a transaction */
static void cancel(trx_t *trx);
/** Note that a record lock wait started */ /** Note that a record lock wait started */
inline void wait_start(); inline void wait_start();
...@@ -1034,9 +1043,6 @@ lock_rtr_move_rec_list( ...@@ -1034,9 +1043,6 @@ lock_rtr_move_rec_list(
moved */ moved */
ulint num_move); /*!< in: num of rec to move */ ulint num_move); /*!< in: num of rec to move */
/** Cancel a waiting lock request and release possibly waiting transactions */
void lock_cancel_waiting_and_release(lock_t *lock);
#include "lock0lock.ic" #include "lock0lock.ic"
#endif #endif
...@@ -1813,24 +1813,26 @@ dberr_t lock_wait(que_thr_t *thr) ...@@ -1813,24 +1813,26 @@ dberr_t lock_wait(que_thr_t *thr)
lock_sys.wait_resume(trx->mysql_thd, suspend_time, my_hrtime_coarse()); lock_sys.wait_resume(trx->mysql_thd, suspend_time, my_hrtime_coarse());
end_wait: end_wait:
mysql_mutex_unlock(&lock_sys.wait_mutex); if (lock_t *lock= trx->lock.wait_lock)
thd_wait_end(trx->mysql_thd);
if (trx->lock.wait_lock)
{ {
if (lock_sys.rd_lock_try())
cancel:
lock_sys_t::cancel(trx, lock);
else
{ {
LockMutexGuard g{SRW_LOCK_CALL}; mysql_mutex_unlock(&lock_sys.wait_mutex);
lock_sys.rd_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex); mysql_mutex_lock(&lock_sys.wait_mutex);
if (lock_t *lock= trx->lock.wait_lock) lock= trx->lock.wait_lock;
{ if (lock)
trx->mutex_lock(); goto cancel;
lock_cancel_waiting_and_release(lock);
trx->mutex_unlock();
} }
lock_sys.deadlock_check(true); lock_sys.rd_unlock();
lock_sys.deadlock_check();
} }
mysql_mutex_unlock(&lock_sys.wait_mutex); mysql_mutex_unlock(&lock_sys.wait_mutex);
} thd_wait_end(trx->mysql_thd);
if (had_dict_lock) if (had_dict_lock)
row_mysql_freeze_data_dictionary(trx); row_mysql_freeze_data_dictionary(trx);
...@@ -3840,9 +3842,10 @@ void lock_release(trx_t *trx) ...@@ -3840,9 +3842,10 @@ void lock_release(trx_t *trx)
if (UNIV_UNLIKELY(Deadlock::to_be_checked)) if (UNIV_UNLIKELY(Deadlock::to_be_checked))
{ {
mysql_mutex_lock(&lock_sys.wait_mutex); mysql_mutex_lock(&lock_sys.wait_mutex);
lock_sys.deadlock_check(false); lock_sys.deadlock_check();
mysql_mutex_unlock(&lock_sys.wait_mutex); mysql_mutex_unlock(&lock_sys.wait_mutex);
} }
trx->lock.was_chosen_as_deadlock_victim= false; trx->lock.was_chosen_as_deadlock_victim= false;
trx->lock.n_rec_locks= 0; trx->lock.n_rec_locks= 0;
} }
...@@ -4863,17 +4866,15 @@ static void lock_rec_other_trx_holds_expl(trx_t *caller_trx, trx_t *trx, ...@@ -4863,17 +4866,15 @@ static void lock_rec_other_trx_holds_expl(trx_t *caller_trx, trx_t *trx,
if (trx) if (trx)
{ {
ut_ad(!page_rec_is_metadata(rec)); ut_ad(!page_rec_is_metadata(rec));
const auto fold= id.fold(); LockGuard g{lock_sys.rec_hash, id};
LockMutexGuard g{SRW_LOCK_CALL};
ut_ad(trx->is_referenced()); ut_ad(trx->is_referenced());
const trx_state_t state{trx->state}; const trx_state_t state{trx->state};
ut_ad(state != TRX_STATE_NOT_STARTED); ut_ad(state != TRX_STATE_NOT_STARTED);
if (state == TRX_STATE_COMMITTED_IN_MEMORY) if (state == TRX_STATE_COMMITTED_IN_MEMORY)
/* The transaction was committed before we acquired LockMutexGuard. */ /* The transaction was committed before we acquired LockGuard. */
return; return;
;
lock_rec_other_trx_holds_expl_arg arg= lock_rec_other_trx_holds_expl_arg arg=
{ page_rec_get_heap_no(rec), *lock_sys.rec_hash.cell_get(fold), id, *trx }; { page_rec_get_heap_no(rec), g.cell(), id, *trx };
trx_sys.rw_trx_hash.iterate(caller_trx, trx_sys.rw_trx_hash.iterate(caller_trx,
lock_rec_other_trx_holds_expl_callback, &arg); lock_rec_other_trx_holds_expl_callback, &arg);
} }
...@@ -5328,11 +5329,9 @@ lock_trx_holds_autoinc_locks( ...@@ -5328,11 +5329,9 @@ lock_trx_holds_autoinc_locks(
return(!ib_vector_is_empty(trx->autoinc_locks)); return(!ib_vector_is_empty(trx->autoinc_locks));
} }
/*******************************************************************//** /** Release all AUTO_INCREMENT locks of the transaction. */
Release all the transaction's autoinc locks. */
static void lock_release_autoinc_locks(trx_t *trx, bool owns_wait_mutex) static void lock_release_autoinc_locks(trx_t *trx, bool owns_wait_mutex)
{ {
lock_sys.assert_locked();
#ifdef SAFE_MUTEX #ifdef SAFE_MUTEX
ut_ad(owns_wait_mutex == mysql_mutex_is_owner(&lock_sys.wait_mutex)); ut_ad(owns_wait_mutex == mysql_mutex_is_owner(&lock_sys.wait_mutex));
#endif /* SAFE_MUTEX */ #endif /* SAFE_MUTEX */
...@@ -5350,9 +5349,13 @@ static void lock_release_autoinc_locks(trx_t *trx, bool owns_wait_mutex) ...@@ -5350,9 +5349,13 @@ static void lock_release_autoinc_locks(trx_t *trx, bool owns_wait_mutex)
lock_t *lock= *static_cast<lock_t**> lock_t *lock= *static_cast<lock_t**>
(ib_vector_get(autoinc_locks, size - 1)); (ib_vector_get(autoinc_locks, size - 1));
ut_ad(lock->type_mode == (LOCK_AUTO_INC | LOCK_TABLE)); ut_ad(lock->type_mode == (LOCK_AUTO_INC | LOCK_TABLE));
ut_ad(lock->un_member.tab_lock.table); dict_table_t *table= lock->un_member.tab_lock.table;
if (!owns_wait_mutex)
table->lock_mutex_lock();
lock_table_dequeue(lock, owns_wait_mutex); lock_table_dequeue(lock, owns_wait_mutex);
lock_trx_table_locks_remove(lock); lock_trx_table_locks_remove(lock);
if (!owns_wait_mutex)
table->lock_mutex_unlock();
} }
} }
...@@ -5411,11 +5414,12 @@ lock_rec_get_index( ...@@ -5411,11 +5414,12 @@ lock_rec_get_index(
} }
/** Cancel a waiting lock request and release possibly waiting transactions */ /** Cancel a waiting lock request and release possibly waiting transactions */
void lock_cancel_waiting_and_release(lock_t *lock) static void lock_cancel_waiting_and_release(lock_t *lock)
{ {
lock_sys.assert_locked(*lock); lock_sys.assert_locked(*lock);
mysql_mutex_assert_owner(&lock_sys.wait_mutex); mysql_mutex_assert_owner(&lock_sys.wait_mutex);
trx_t *trx= lock->trx; trx_t *trx= lock->trx;
trx->mutex_lock();
ut_ad(trx->state == TRX_STATE_ACTIVE); ut_ad(trx->state == TRX_STATE_ACTIVE);
if (!lock->is_table()) if (!lock->is_table())
...@@ -5433,6 +5437,75 @@ void lock_cancel_waiting_and_release(lock_t *lock) ...@@ -5433,6 +5437,75 @@ void lock_cancel_waiting_and_release(lock_t *lock)
lock_reset_lock_and_trx_wait(lock); lock_reset_lock_and_trx_wait(lock);
lock_wait_end(trx); lock_wait_end(trx);
trx->mutex_unlock();
}
/** Cancel a waiting lock request.
@param lock waiting lock request
@param trx active transaction */
void lock_sys_t::cancel(trx_t *trx, lock_t *lock)
{
mysql_mutex_assert_owner(&lock_sys.wait_mutex);
ut_ad(trx->lock.wait_lock == lock);
ut_ad(trx->state == TRX_STATE_ACTIVE);
if (lock->is_table())
{
dict_table_t *table= lock->un_member.tab_lock.table;
table->lock_mutex_lock();
if (lock->is_waiting())
lock_cancel_waiting_and_release(lock);
table->lock_mutex_unlock();
}
else
{
auto latch= lock_sys_t::hash_table::latch
(lock_sys.hash_get(lock->type_mode).cell_get
(lock->un_member.rec_lock.page_id.fold()));
latch->acquire();
if (lock->is_waiting())
lock_cancel_waiting_and_release(lock);
latch->release();
}
}
/** Cancel a waiting lock request (if any) when killing a transaction */
void lock_sys_t::cancel(trx_t *trx)
{
#ifdef HAVE_REPLICATION /* Work around MDEV-25016 (FIXME: remove this!) */
/* Parallel replication tests would occasionally hang if we did not
acquire exclusive lock_sys.latch here. This is not a real fix, but a
work-around!
It would be nice if thd_need_wait_reports() did not hold when no
parallel replication is in use, and only the binlog is enabled. */
if (innodb_deadlock_detect && thd_need_wait_reports(trx->mysql_thd))
{
lock_sys.wr_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex);
if (lock_t *lock= trx->lock.wait_lock)
{
trx->error_state= DB_INTERRUPTED;
cancel(trx, lock);
}
lock_sys.wr_unlock();
goto func_exit;
}
#endif
lock_sys.rd_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex);
if (lock_t *lock= trx->lock.wait_lock)
{
trx->error_state= DB_INTERRUPTED;
cancel(trx, lock);
}
lock_sys.rd_unlock();
#ifdef HAVE_REPLICATION
func_exit:
#endif
lock_sys.deadlock_check();
mysql_mutex_unlock(&lock_sys.wait_mutex);
} }
/*********************************************************************//** /*********************************************************************//**
...@@ -5459,52 +5532,55 @@ lock_unlock_table_autoinc( ...@@ -5459,52 +5532,55 @@ lock_unlock_table_autoinc(
necessary to hold trx->mutex here. */ necessary to hold trx->mutex here. */
if (lock_trx_holds_autoinc_locks(trx)) { if (lock_trx_holds_autoinc_locks(trx)) {
LockMutexGuard g{SRW_LOCK_CALL}; lock_sys.rd_lock(SRW_LOCK_CALL);
trx->mutex_lock(); trx->mutex_lock();
lock_release_autoinc_locks(trx, false); lock_release_autoinc_locks(trx, false);
trx->mutex_unlock(); trx->mutex_unlock();
lock_sys.rd_unlock();
} }
} }
static inline dberr_t lock_trx_handle_wait_low(trx_t* trx) /** Handle a pending lock wait (DB_LOCK_WAIT) in a semi-consistent read
while holding a clustered index leaf page latch.
@param trx transaction that is or was waiting for a lock
@retval DB_SUCCESS if the lock was granted
@retval DB_DEADLOCK if the transaction must be aborted due to a deadlock
@retval DB_LOCK_WAIT if a lock wait would be necessary; the pending
lock request was released */
dberr_t lock_trx_handle_wait(trx_t *trx)
{ {
lock_sys.assert_locked();
mysql_mutex_assert_owner(&lock_sys.wait_mutex);
ut_ad(trx->mutex_is_owner());
if (trx->lock.was_chosen_as_deadlock_victim) if (trx->lock.was_chosen_as_deadlock_victim)
return DB_DEADLOCK; return DB_DEADLOCK;
if (!trx->lock.wait_lock) if (!trx->lock.wait_lock)
/* The lock was probably granted before we got here. */
return DB_SUCCESS; return DB_SUCCESS;
dberr_t err= DB_SUCCESS;
lock_cancel_waiting_and_release(trx->lock.wait_lock); mysql_mutex_lock(&lock_sys.wait_mutex);
return DB_LOCK_WAIT; if (trx->lock.was_chosen_as_deadlock_victim)
} err= DB_DEADLOCK;
else if (lock_t *wait_lock= trx->lock.wait_lock)
/*********************************************************************//**
Check whether the transaction has already been rolled back because it
was selected as a deadlock victim, or if it has to wait then cancel
the wait lock.
@return DB_DEADLOCK, DB_LOCK_WAIT or DB_SUCCESS */
dberr_t
lock_trx_handle_wait(
/*=================*/
trx_t* trx) /*!< in/out: trx lock state */
{
#ifdef WITH_WSREP
if (UNIV_UNLIKELY(trx->lock.was_chosen_as_deadlock_victim & 2))
return lock_trx_handle_wait_low(trx);
#endif /* WITH_WSREP */
dberr_t err;
{ {
LockMutexGuard g{SRW_LOCK_CALL}; if (!lock_sys.rd_lock_try())
{
mysql_mutex_unlock(&lock_sys.wait_mutex);
lock_sys.rd_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex); mysql_mutex_lock(&lock_sys.wait_mutex);
trx->mutex_lock();
err= lock_trx_handle_wait_low(trx); if (trx->lock.was_chosen_as_deadlock_victim)
trx->mutex_unlock(); {
lock_sys.deadlock_check(true); err= DB_DEADLOCK;
goto release;
}
wait_lock= trx->lock.wait_lock;
if (!wait_lock)
goto release;
} }
err= DB_LOCK_WAIT;
lock_sys_t::cancel(trx, wait_lock);
release:
lock_sys.rd_unlock();
}
lock_sys.deadlock_check();
mysql_mutex_unlock(&lock_sys.wait_mutex); mysql_mutex_unlock(&lock_sys.wait_mutex);
return err; return err;
} }
...@@ -5550,23 +5626,25 @@ static my_bool lock_table_locks_lookup(rw_trx_hash_element_t *element, ...@@ -5550,23 +5626,25 @@ static my_bool lock_table_locks_lookup(rw_trx_hash_element_t *element,
} }
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
/*******************************************************************//** /** Check if there are any locks on a table.
Check if there are any locks (table or rec) against table.
@return true if table has either table or record locks. */ @return true if table has either table or record locks. */
bool bool lock_table_has_locks(dict_table_t *table)
lock_table_has_locks(
/*=================*/
const dict_table_t* table) /*!< in: check if there are any locks
held on records in this table or on the
table itself */
{ {
LockMutexGuard g{SRW_LOCK_CALL}; if (table->n_rec_locks)
bool has_locks= UT_LIST_GET_LEN(table->locks) > 0 || table->n_rec_locks > 0; return true;
table->lock_mutex_lock();
auto len= UT_LIST_GET_LEN(table->locks);
table->lock_mutex_unlock();
if (len)
return true;
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
if (!has_locks) {
trx_sys.rw_trx_hash.iterate(lock_table_locks_lookup, table); LockMutexGuard g{SRW_LOCK_CALL};
trx_sys.rw_trx_hash.iterate(lock_table_locks_lookup,
const_cast<const dict_table_t*>(table));
}
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
return has_locks; return false;
} }
/*******************************************************************//** /*******************************************************************//**
...@@ -5868,9 +5946,7 @@ namespace Deadlock ...@@ -5868,9 +5946,7 @@ namespace Deadlock
ut_ad(victim->state == TRX_STATE_ACTIVE); ut_ad(victim->state == TRX_STATE_ACTIVE);
victim->lock.was_chosen_as_deadlock_victim= true; victim->lock.was_chosen_as_deadlock_victim= true;
victim->mutex_lock();
lock_cancel_waiting_and_release(victim->lock.wait_lock); lock_cancel_waiting_and_release(victim->lock.wait_lock);
victim->mutex_unlock();
#ifdef WITH_WSREP #ifdef WITH_WSREP
if (victim->is_wsrep() && wsrep_thd_is_SR(victim->mysql_thd)) if (victim->is_wsrep() && wsrep_thd_is_SR(victim->mysql_thd))
wsrep_handle_SR_rollback(trx->mysql_thd, victim->mysql_thd); wsrep_handle_SR_rollback(trx->mysql_thd, victim->mysql_thd);
...@@ -5905,31 +5981,31 @@ static bool Deadlock::check_and_resolve(trx_t *trx) ...@@ -5905,31 +5981,31 @@ static bool Deadlock::check_and_resolve(trx_t *trx)
if (UNIV_LIKELY(!trx->lock.was_chosen_as_deadlock_victim)) if (UNIV_LIKELY(!trx->lock.was_chosen_as_deadlock_victim))
return false; return false;
if (!lock_sys.wr_lock_try()) if (lock_t *wait_lock= trx->lock.wait_lock)
{
if (!lock_sys.rd_lock_try())
{ {
mysql_mutex_unlock(&lock_sys.wait_mutex); mysql_mutex_unlock(&lock_sys.wait_mutex);
lock_sys.wr_lock(SRW_LOCK_CALL); lock_sys.rd_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex); mysql_mutex_lock(&lock_sys.wait_mutex);
wait_lock= trx->lock.wait_lock;
if (!wait_lock)
goto resolved;
} }
lock_sys_t::cancel(trx, wait_lock);
if (lock_t *lock= trx->lock.wait_lock) resolved:
{ lock_sys.rd_unlock();
trx->mutex_lock();
lock_cancel_waiting_and_release(lock);
trx->mutex_unlock();
} }
lock_sys.deadlock_check(true); lock_sys.deadlock_check();
lock_sys.wr_unlock();
return true; return true;
} }
/** Check for deadlocks while holding only lock_sys.wait_mutex. */
/** Check for deadlocks */ void lock_sys_t::deadlock_check()
void lock_sys_t::deadlock_check(bool locked)
{ {
ut_ad(lock_sys.is_writer() == locked); ut_ad(!is_writer());
mysql_mutex_assert_owner(&lock_sys.wait_mutex); mysql_mutex_assert_owner(&wait_mutex);
bool acquired= false; bool acquired= false;
if (Deadlock::to_be_checked) if (Deadlock::to_be_checked)
...@@ -5939,15 +6015,15 @@ void lock_sys_t::deadlock_check(bool locked) ...@@ -5939,15 +6015,15 @@ void lock_sys_t::deadlock_check(bool locked)
auto i= Deadlock::to_check.begin(); auto i= Deadlock::to_check.begin();
if (i == Deadlock::to_check.end()) if (i == Deadlock::to_check.end())
break; break;
if (!locked) if (!acquired)
{ {
acquired= locked= lock_sys.wr_lock_try(); acquired= wr_lock_try();
if (!locked) if (!acquired)
{ {
acquired= locked= true; acquired= true;
mysql_mutex_unlock(&lock_sys.wait_mutex); mysql_mutex_unlock(&wait_mutex);
lock_sys.wr_lock(SRW_LOCK_CALL); lock_sys.wr_lock(SRW_LOCK_CALL);
mysql_mutex_lock(&lock_sys.wait_mutex); mysql_mutex_lock(&wait_mutex);
continue; continue;
} }
} }
...@@ -5960,7 +6036,7 @@ void lock_sys_t::deadlock_check(bool locked) ...@@ -5960,7 +6036,7 @@ void lock_sys_t::deadlock_check(bool locked)
} }
ut_ad(Deadlock::to_check.empty()); ut_ad(Deadlock::to_check.empty());
if (acquired) if (acquired)
lock_sys.wr_unlock(); wr_unlock();
} }
......
...@@ -2613,10 +2613,9 @@ row_drop_tables_for_mysql_in_background(void) ...@@ -2613,10 +2613,9 @@ row_drop_tables_for_mysql_in_background(void)
} }
if (!srv_fast_shutdown && !trx_sys.any_active_transactions()) { if (!srv_fast_shutdown && !trx_sys.any_active_transactions()) {
{ table->lock_mutex_lock();
LockMutexGuard g{SRW_LOCK_CALL};
skip = UT_LIST_GET_LEN(table->locks) != 0; skip = UT_LIST_GET_LEN(table->locks) != 0;
} table->lock_mutex_unlock();
if (skip) { if (skip) {
/* We cannot drop tables that are locked by XA /* We cannot drop tables that are locked by XA
PREPARE transactions. */ PREPARE transactions. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment