diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2021-02-12 17:42:18 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2021-02-12 17:44:58 +0200 |
commit | 26d6224dd64435a74273867c59dbfba802d8ecd5 (patch) | |
tree | 92f6af4ec32103726ffdb770382788f1ead6a59c | |
parent | b08448de64f2af9c849154647bdd61d1725f8928 (diff) | |
download | mariadb-git-26d6224dd64435a74273867c59dbfba802d8ecd5.tar.gz |
MDEV-20612: Enable concurrent lock_release()
lock_release_try(): Try to release locks while only holding
shared lock_sys.latch.
lock_release(): If 5 attempts of lock_release_try() fail,
proceed to acquire exclusive lock_sys.latch.
-rw-r--r-- | storage/innobase/include/dict0mem.h | 8 | ||||
-rw-r--r-- | storage/innobase/include/lock0lock.h | 6 | ||||
-rw-r--r-- | storage/innobase/lock/lock0lock.cc | 103 |
3 files changed, 97 insertions, 20 deletions
diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index b613e6e46e4..d0d82bf7e56 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -1972,6 +1972,14 @@ struct dict_table_t { lock_mutex.wr_lock(); ut_ad(!lock_mutex_owner.exchange(os_thread_get_curr_id())); } + /** Try to acquire lock_mutex */ + bool lock_mutex_trylock() + { + ut_ad(!lock_mutex_is_owner()); + bool acquired= lock_mutex.wr_lock_try(); + ut_ad(!acquired || !lock_mutex_owner.exchange(os_thread_get_curr_id())); + return acquired; + } /** Release lock_mutex */ void lock_mutex_unlock() { diff --git a/storage/innobase/include/lock0lock.h b/storage/innobase/include/lock0lock.h index 4b5654a3b27..f81d7c65b84 100644 --- a/storage/innobase/include/lock0lock.h +++ b/storage/innobase/include/lock0lock.h @@ -581,8 +581,10 @@ class lock_sys_t { /** Wait for an exclusive lock */ void wait(); + /** Try to acquire a lock */ + bool try_acquire() { return write_trylock(); } /** Acquire a lock */ - void acquire() { if (!write_trylock()) wait(); } + void acquire() { if (!try_acquire()) wait(); } /** Release a lock */ void release(); #else @@ -590,6 +592,8 @@ class lock_sys_t private: srw_lock_low lock; public: + /** Try to acquire a lock */ + bool try_acquire() { return lock.wr_lock_try(); } /** Acquire a lock */ void acquire() { lock.wr_lock(); } /** Release a lock */ diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc index 7d3aadb2129..2e82efe52c9 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -3939,12 +3939,10 @@ released: } /** Release the explicit locks of a committing transaction, -and release possible other transactions waiting because of these locks. */ -void lock_release(trx_t *trx) +and release possible other transactions waiting because of these locks. +@return whether the operation succeeded */ +static bool lock_release_try(trx_t *trx) { - ulint count= 0; - - ut_ad(!trx->mutex_is_owner()); /* At this point, trx->lock.trx_locks cannot be modified by other threads, because our transaction has been committed. See the checks and assertions in lock_rec_create_low() and @@ -3956,45 +3954,112 @@ void lock_release(trx_t *trx) DBUG_ASSERT(trx->state == TRX_STATE_COMMITTED_IN_MEMORY); DBUG_ASSERT(!trx->is_referenced()); - LockMutexGuard g{SRW_LOCK_CALL}; + bool all_released= true; +restart: + ulint count= 1000; + lock_sys.rd_lock(SRW_LOCK_CALL); trx->mutex_lock(); - for (lock_t *lock= UT_LIST_GET_LAST(trx->lock.trx_locks); lock; - lock= UT_LIST_GET_LAST(trx->lock.trx_locks)) + /* Note: Anywhere else, trx->mutex is not held while acquiring + a lock table latch, but here we are following the opposite order. + To avoid deadlocks, we only try to acquire the lock table latches + but not keep waiting for them. */ + + for (lock_t *lock= UT_LIST_GET_LAST(trx->lock.trx_locks); lock; ) { ut_ad(lock->trx == trx); + lock_t *prev= UT_LIST_GET_PREV(trx_locks, lock); if (!lock->is_table()) { ut_ad(!lock->index->table->is_temporary()); ut_ad(lock->mode() != LOCK_X || lock->index->table->id >= DICT_HDR_FIRST_ID || trx->dict_operation); - lock_rec_dequeue_from_page(lock, false); + auto &lock_hash= lock_sys.hash_get(lock->type_mode); + auto latch= lock_hash.lock_get(lock->un_member.rec_lock.page_id.fold()); + if (!latch->try_acquire()) + all_released= false; + else + { + lock_rec_dequeue_from_page(lock, false); + latch->release(); + } } else { - ut_d(dict_table_t *table= lock->un_member.tab_lock.table); + dict_table_t *table= lock->un_member.tab_lock.table; ut_ad(!table->is_temporary()); ut_ad(table->id >= DICT_HDR_FIRST_ID || (lock->mode() != LOCK_IX && lock->mode() != LOCK_X) || trx->dict_operation); - lock_table_dequeue(lock, false); + if (!table->lock_mutex_trylock()) + all_released= false; + else + { + lock_table_dequeue(lock, false); + table->lock_mutex_unlock(); + } } - if (count == 1000) + lock= all_released ? UT_LIST_GET_LAST(trx->lock.trx_locks) : prev; + if (!--count) + break; + } + + lock_sys.rd_unlock(); + trx->mutex_unlock(); + if (all_released && !count) + goto restart; + return all_released; +} + +/** Release the explicit locks of a committing transaction, +and release possible other transactions waiting because of these locks. */ +void lock_release(trx_t *trx) +{ + ulint count; + + for (count= 5; count--; ) + if (lock_release_try(trx)) + goto released; + + /* Fall back to acquiring lock_sys.latch in exclusive mode */ +restart: + count= 1000; + lock_sys.wr_lock(SRW_LOCK_CALL); + trx->mutex_lock(); + + while (lock_t *lock= UT_LIST_GET_LAST(trx->lock.trx_locks)) + { + ut_ad(lock->trx == trx); + if (!lock->is_table()) { - /* Release the latch for a while, so that we do not monopolize it */ - lock_sys.wr_unlock(); - trx->mutex_unlock(); - count= 0; - lock_sys.wr_lock(SRW_LOCK_CALL); - trx->mutex_lock(); + ut_ad(!lock->index->table->is_temporary()); + ut_ad(lock->mode() != LOCK_X || + lock->index->table->id >= DICT_HDR_FIRST_ID || + trx->dict_operation); + lock_rec_dequeue_from_page(lock, false); + } + else + { + ut_d(dict_table_t *table= lock->un_member.tab_lock.table); + ut_ad(!table->is_temporary()); + ut_ad(table->id >= DICT_HDR_FIRST_ID || + (lock->mode() != LOCK_IX && lock->mode() != LOCK_X) || + trx->dict_operation); + lock_table_dequeue(lock, false); } - ++count; + if (!--count) + break; } + lock_sys.wr_unlock(); trx->mutex_unlock(); + if (!count) + goto restart; + +released: trx->lock.was_chosen_as_deadlock_victim= false; trx->lock.n_rec_locks= 0; } |