diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2021-02-11 15:18:23 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2021-02-11 15:18:23 +0200 |
commit | 27facc0d7a592c5cf80a153686e09d8430a20ad4 (patch) | |
tree | b603b09517b27b5ed6eb8f4f04d37e9bc57d6880 | |
parent | f98795a8749b8053dca2b742938a8d289c7b515a (diff) | |
download | mariadb-git-27facc0d7a592c5cf80a153686e09d8430a20ad4.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 | 106 |
3 files changed, 100 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 69d68875270..f64d3159d3a 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 1fcfc112603..d456201227c 100644 --- a/storage/innobase/lock/lock0lock.cc +++ b/storage/innobase/lock/lock0lock.cc @@ -3929,12 +3929,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 @@ -3946,45 +3944,115 @@ 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_hash.calc_hash(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(); + ut_ad(!all_released || prev == UT_LIST_GET_LAST(trx->lock.trx_locks)); + } } 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(); + ut_ad(!all_released || prev == UT_LIST_GET_LAST(trx->lock.trx_locks)); + } } - if (count == 1000) + lock= 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; } |