summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2021-02-11 15:18:23 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2021-02-11 15:18:23 +0200
commit27facc0d7a592c5cf80a153686e09d8430a20ad4 (patch)
treeb603b09517b27b5ed6eb8f4f04d37e9bc57d6880
parentf98795a8749b8053dca2b742938a8d289c7b515a (diff)
downloadmariadb-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.h8
-rw-r--r--storage/innobase/include/lock0lock.h6
-rw-r--r--storage/innobase/lock/lock0lock.cc106
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;
}