Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 59 additions & 6 deletions storage/innobase/lock/lock0lock.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4529,6 +4529,58 @@ lock_rec_unlock(
g.cell(), first_lock, heap_no);
}

/** Spin budget for lock_release_try(), lock_release_on_prepare_try() and
lock_rec_unlock_unmodified() trylock attempts on per-cell and per-table
latches.

Those paths hold trx->mutex while attempting the trylock (the reverse of
the standard order used by lock_rec_convert_impl_to_expl()), so blocking
acquisition would risk deadlock. A single CAS attempt fails too readily
under contention: any one failure across the trx's record/table locks
marks the whole try-pass as unsuccessful, and after 5 such passes
lock_release() escalates to lock_sys.wr_lock for the entire trx, which
then blocks every concurrent lock_sys.rd_lock acquirer in lock_rec_lock()
and lock_table().

A bounded spin (CAS, PAUSE between attempts, no syscall, no blocking) lets a transient
holder release without changing the deadlock-avoidance guarantee. The
upper bound on extra trx->mutex hold time is LOCK_RELEASE_TRY_SPIN_BUDGET
* pause-cost (tens to ~100ns per iteration, microarchitecture-dependent). */
static constexpr unsigned LOCK_RELEASE_TRY_SPIN_BUDGET= 16;
Comment thread
iMineLink marked this conversation as resolved.

/** Try to acquire a lock_sys hash cell latch with a bounded spin.
The Latch template parameter avoids naming the type lock_sys_t::hash_latch,
which is private to lock_sys_t. Template deduction handles it.
@return whether the latch was acquired */
template <typename Latch>
static inline bool cell_latch_try_acquire_spin(Latch *latch)
{
for (unsigned spin= LOCK_RELEASE_TRY_SPIN_BUDGET;;)
{
if (latch->try_acquire())
return true;
if (spin <= 1)
return false;
--spin;
MY_RELAX_CPU();
}
}
Comment thread
iMineLink marked this conversation as resolved.

/** Try to acquire a dict_table_t::lock_latch with a bounded spin.
@return whether the latch was acquired */
static inline bool table_lock_mutex_try_acquire_spin(dict_table_t *table)
{
for (unsigned spin= LOCK_RELEASE_TRY_SPIN_BUDGET;;)
{
if (table->lock_mutex_trylock())
return true;
if (spin <= 1)
return false;
--spin;
MY_RELAX_CPU();
}
}
Comment thread
iMineLink marked this conversation as resolved.

/** Release the explicit locks of a committing transaction,
and release possible other transactions waiting because of these locks.
@return whether the operation succeeded */
Expand Down Expand Up @@ -4573,7 +4625,7 @@ static bool lock_release_try(trx_t *trx)
auto &lock_hash= lock_sys.hash_get(lock->type_mode);
auto cell= lock_hash.cell_get(lock->un_member.rec_lock.page_id.fold());
auto latch= lock_sys_t::hash_table::latch(cell);
if (!latch->try_acquire())
if (!cell_latch_try_acquire_spin(latch))
all_released= false;
else
{
Expand All @@ -4588,7 +4640,7 @@ static bool lock_release_try(trx_t *trx)
ut_ad(table->id >= DICT_HDR_FIRST_ID ||
(lock->mode() != LOCK_IX && lock->mode() != LOCK_X) ||
trx->dict_operation || trx->was_dict_operation);
if (!table->lock_mutex_trylock())
if (!table_lock_mutex_try_acquire_spin(table))
all_released= false;
else
{
Expand Down Expand Up @@ -4831,7 +4883,8 @@ bool lock_rec_unlock_unmodified(buf_block_t *block, hash_cell_t *cell,
computed cell address invalid */
cell= lock_sys.rec_hash.cell_get(
lock->un_member.rec_lock.page_id.fold());
if (!lock_sys_t::hash_table::latch(cell)->try_acquire())
auto latch= lock_sys_t::hash_table::latch(cell);
if (!cell_latch_try_acquire_spin(latch))
return false;
}
if (lock->trx != impl_trx)
Expand Down Expand Up @@ -4889,7 +4942,7 @@ static bool lock_release_on_prepare_try(trx_t *trx, bool unlock_unmodified)
const auto fold = lock->un_member.rec_lock.page_id.fold();
auto cell= lock_sys.rec_hash.cell_get(fold);
auto latch= lock_sys_t::hash_table::latch(cell);
if (latch->try_acquire())
if (cell_latch_try_acquire_spin(latch))
{
if (!rec_granted_exclusive_not_gap)
{
Expand Down Expand Up @@ -4926,7 +4979,7 @@ static bool lock_release_on_prepare_try(trx_t *trx, bool unlock_unmodified)
computed cell address invalid */
cell= lock_sys.rec_hash.cell_get(fold);
latch= lock_sys_t::hash_table::latch(cell);
if (latch->try_acquire())
if (cell_latch_try_acquire_spin(latch))
{
if (!lock_rec_unlock_unmodified<CELL>(block, cell, lock,
offsets))
Expand Down Expand Up @@ -4962,7 +5015,7 @@ static bool lock_release_on_prepare_try(trx_t *trx, bool unlock_unmodified)
switch (lock->mode()) {
case LOCK_IS:
case LOCK_S:
if (table->lock_mutex_trylock())
if (table_lock_mutex_try_acquire_spin(table))
{
lock_table_dequeue(lock, false);
table->lock_mutex_unlock();
Expand Down