summaryrefslogtreecommitdiff
path: root/storage/innobase/lock
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2019-09-06 11:53:10 +0200
committerSergei Golubchik <serg@mariadb.org>2019-09-06 11:53:10 +0200
commit244f0e6dd815b388282c15db4fe7f15533f4c8fc (patch)
treeaf138f2b3739a742c0c38173cdc86ec176fc0edd /storage/innobase/lock
parent18af13b88ba580562981a190c25da128a2e9db26 (diff)
parent2842c369851a8afc2a944ce6f4f60fa052f20969 (diff)
downloadmariadb-git-244f0e6dd815b388282c15db4fe7f15533f4c8fc.tar.gz
Merge branch '10.3' into 10.4
Diffstat (limited to 'storage/innobase/lock')
-rw-r--r--storage/innobase/lock/lock0lock.cc236
1 files changed, 69 insertions, 167 deletions
diff --git a/storage/innobase/lock/lock0lock.cc b/storage/innobase/lock/lock0lock.cc
index aebbf5c1cab..3bf7e33fe4e 100644
--- a/storage/innobase/lock/lock0lock.cc
+++ b/storage/innobase/lock/lock0lock.cc
@@ -4270,23 +4270,17 @@ lock_check_dict_lock(
}
#endif /* UNIV_DEBUG */
-/*********************************************************************//**
-Releases transaction locks, and releases possible other transactions waiting
-because of these locks. */
-static
-void
-lock_release(
-/*=========*/
- trx_t* trx) /*!< in/out: transaction */
+/** Release the explicit locks of a committing transaction,
+and release possible other transactions waiting because of these locks. */
+void lock_release(trx_t* trx)
{
- lock_t* lock;
ulint count = 0;
trx_id_t max_trx_id = trx_sys.get_max_trx_id();
- ut_ad(lock_mutex_own());
+ lock_mutex_enter();
ut_ad(!trx_mutex_own(trx));
- for (lock = UT_LIST_GET_LAST(trx->lock.trx_locks);
+ for (lock_t* lock = UT_LIST_GET_LAST(trx->lock.trx_locks);
lock != NULL;
lock = UT_LIST_GET_LAST(trx->lock.trx_locks)) {
@@ -4326,6 +4320,8 @@ lock_release(
++count;
}
+
+ lock_mutex_exit();
}
/* True if a lock mode is S or X */
@@ -4736,7 +4732,7 @@ lock_trx_table_locks_find(
{
bool found = false;
- trx_mutex_enter(trx);
+ ut_ad(trx_mutex_own(trx));
for (lock_list::const_iterator it = trx->lock.table_locks.begin(),
end = trx->lock.table_locks.end(); it != end; ++it) {
@@ -4759,8 +4755,6 @@ lock_trx_table_locks_find(
ut_a(lock->un_member.tab_lock.table != NULL);
}
- trx_mutex_exit(trx);
-
return(found);
}
@@ -4781,25 +4775,23 @@ lock_table_queue_validate(
lock != NULL;
lock = UT_LIST_GET_NEXT(un_member.tab_lock.locks, lock)) {
- /* Transaction state may change from ACTIVE to PREPARED.
- State change to COMMITTED is not possible while we are
- holding lock_sys.mutex: it is done by lock_trx_release_locks()
- under lock_sys.mutex protection.
- Transaction in NOT_STARTED state cannot hold locks, and
- lock->trx->state can only move to NOT_STARTED from COMMITTED. */
+ /* lock->trx->state cannot change from or to NOT_STARTED
+ while we are holding the lock_sys.mutex. It may change
+ from ACTIVE or PREPARED to PREPARED or COMMITTED. */
+ trx_mutex_enter(lock->trx);
check_trx_state(lock->trx);
- if (!lock_get_wait(lock)) {
-
+ if (lock->trx->state == TRX_STATE_COMMITTED_IN_MEMORY) {
+ } else if (!lock_get_wait(lock)) {
ut_a(!lock_table_other_has_incompatible(
lock->trx, 0, table,
lock_get_mode(lock)));
} else {
-
ut_a(lock_table_has_to_wait_in_queue(lock));
}
ut_a(lock_trx_table_locks_find(lock->trx, lock));
+ trx_mutex_exit(lock->trx);
}
return(TRUE);
@@ -4846,42 +4838,41 @@ lock_rec_queue_validate(
lock != NULL;
lock = lock_rec_get_next_const(heap_no, lock)) {
- ut_ad(!trx_is_ac_nl_ro(lock->trx));
+ ut_ad(!index || lock->index == index);
- if (lock_get_wait(lock)) {
- ut_a(lock_rec_has_to_wait_in_queue(lock));
- }
+ trx_mutex_enter(lock->trx);
+ ut_ad(!trx_is_ac_nl_ro(lock->trx));
+ ut_ad(trx_state_eq(lock->trx,
+ TRX_STATE_COMMITTED_IN_MEMORY)
+ || !lock_get_wait(lock)
+ || lock_rec_has_to_wait_in_queue(lock));
+ trx_mutex_exit(lock->trx);
+ }
- if (index != NULL) {
- ut_a(lock->index == index);
- }
+func_exit:
+ if (!locked_lock_trx_sys) {
+ lock_mutex_exit();
}
- goto func_exit;
+ return true;
}
ut_ad(page_rec_is_leaf(rec));
+ ut_ad(lock_mutex_own());
- if (index == NULL) {
-
- /* Nothing we can do */
-
- } else if (dict_index_is_clust(index)) {
- /* Unlike the non-debug code, this invariant can only succeed
- if the check and assertion are covered by the lock mutex. */
-
- const trx_id_t impl_trx_id = lock_clust_rec_some_has_impl(
- rec, index, offsets);
-
- const trx_t *impl_trx = impl_trx_id
- ? trx_sys.find(current_trx(), impl_trx_id, false)
- : 0;
+ const trx_id_t impl_trx_id = index && index->is_primary()
+ ? lock_clust_rec_some_has_impl(rec, index, offsets)
+ : 0;
- ut_ad(lock_mutex_own());
- /* impl_trx cannot be committed until lock_mutex_exit()
- because lock_trx_release_locks() acquires lock_sys.mutex */
+ if (trx_t *impl_trx = impl_trx_id
+ ? trx_sys.find(current_trx(), impl_trx_id, false)
+ : 0) {
+ /* impl_trx could have been committed before we
+ acquire its mutex, but not thereafter. */
- if (!impl_trx) {
+ mutex_enter(&impl_trx->mutex);
+ ut_ad(impl_trx->state != TRX_STATE_NOT_STARTED);
+ if (impl_trx->state == TRX_STATE_COMMITTED_IN_MEMORY) {
} else if (const lock_t* other_lock
= lock_rec_other_has_expl_req(
LOCK_S, block, true, heap_no,
@@ -4923,6 +4914,8 @@ lock_rec_queue_validate(
ut_ad(lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP,
block, heap_no, impl_trx));
}
+
+ mutex_exit(&impl_trx->mutex);
}
for (lock = lock_rec_get_first(lock_sys.rec_hash, block, heap_no);
@@ -4967,12 +4960,7 @@ lock_rec_queue_validate(
ut_ad(innodb_lock_schedule_algorithm == INNODB_LOCK_SCHEDULE_ALGORITHM_FCFS ||
lock_queue_validate(lock));
-func_exit:
- if (!locked_lock_trx_sys) {
- lock_mutex_exit();
- }
-
- return(TRUE);
+ goto func_exit;
}
/*********************************************************************//**
@@ -5399,25 +5387,19 @@ lock_rec_convert_impl_to_expl_for_trx(
ut_ad(!rec_is_metadata(rec, *index));
DEBUG_SYNC_C("before_lock_rec_convert_impl_to_expl_for_trx");
-
lock_mutex_enter();
-
+ trx_mutex_enter(trx);
ut_ad(!trx_state_eq(trx, TRX_STATE_NOT_STARTED));
if (!trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY)
&& !lock_rec_has_expl(LOCK_X | LOCK_REC_NOT_GAP,
block, heap_no, trx)) {
-
- ulint type_mode;
-
- type_mode = (LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP);
-
- lock_rec_add_to_queue(
- type_mode, block, heap_no, index, trx, FALSE);
+ lock_rec_add_to_queue(LOCK_REC | LOCK_X | LOCK_REC_NOT_GAP,
+ block, heap_no, index, trx, true);
}
lock_mutex_exit();
-
+ trx_mutex_exit(trx);
trx->release_reference();
DEBUG_SYNC_C("after_lock_rec_convert_impl_to_expl_for_trx");
@@ -5440,13 +5422,17 @@ static my_bool lock_rec_other_trx_holds_expl_callback(
mutex_enter(&element->mutex);
if (element->trx)
{
- lock_t *expl_lock= lock_rec_has_expl(LOCK_S | LOCK_REC_NOT_GAP, arg->block,
- arg->heap_no, element->trx);
+ trx_mutex_enter(element->trx);
+ ut_ad(element->trx->state != TRX_STATE_NOT_STARTED);
+ lock_t *expl_lock= element->trx->state == TRX_STATE_COMMITTED_IN_MEMORY
+ ? NULL : lock_rec_has_expl(LOCK_S | LOCK_REC_NOT_GAP, arg->block,
+ arg->heap_no, element->trx);
/*
An explicit lock is held by trx other than the trx holding the implicit
lock.
*/
ut_ad(!expl_lock || expl_lock->trx == arg->impl_trx);
+ trx_mutex_exit(element->trx);
}
mutex_exit(&element->mutex);
return 0;
@@ -5476,9 +5462,6 @@ static void lock_rec_other_trx_holds_expl(trx_t *caller_trx, trx_t *trx,
ut_ad(!page_rec_is_metadata(rec));
lock_mutex_enter();
ut_ad(trx->is_referenced());
- /* Prevent a data race with trx_prepare(), which could change the
- state from ACTIVE to PREPARED. Other state changes should be
- blocked by lock_mutex_own() and trx->is_referenced(). */
trx_mutex_enter(trx);
const trx_state_t state = trx->state;
trx_mutex_exit(trx);
@@ -6242,92 +6225,6 @@ lock_unlock_table_autoinc(
}
}
-/*********************************************************************//**
-Releases a transaction's locks, and releases possible other transactions
-waiting because of these locks. Change the state of the transaction to
-TRX_STATE_COMMITTED_IN_MEMORY. */
-void
-lock_trx_release_locks(
-/*===================*/
- trx_t* trx) /*!< in/out: transaction */
-{
- check_trx_state(trx);
- ut_ad(trx_state_eq(trx, TRX_STATE_PREPARED)
- || trx_state_eq(trx, TRX_STATE_PREPARED_RECOVERED)
- || trx_state_eq(trx, TRX_STATE_ACTIVE));
-
- bool release_lock = UT_LIST_GET_LEN(trx->lock.trx_locks) > 0;
-
- /* Don't take lock_sys.mutex if trx didn't acquire any lock. */
- if (release_lock) {
-
- /* The transition of trx->state to TRX_STATE_COMMITTED_IN_MEMORY
- is protected by both the lock_sys.mutex and the trx->mutex. */
- lock_mutex_enter();
- }
-
- /* The following assignment makes the transaction committed in memory
- and makes its changes to data visible to other transactions.
- NOTE that there is a small discrepancy from the strict formal
- visibility rules here: a human user of the database can see
- modifications made by another transaction T even before the necessary
- log segment has been flushed to the disk. If the database happens to
- crash before the flush, the user has seen modifications from T which
- will never be a committed transaction. However, any transaction T2
- which sees the modifications of the committing transaction T, and
- which also itself makes modifications to the database, will get an lsn
- larger than the committing transaction T. In the case where the log
- flush fails, and T never gets committed, also T2 will never get
- committed. */
-
- /*--------------------------------------*/
- trx_mutex_enter(trx);
- trx->state = TRX_STATE_COMMITTED_IN_MEMORY;
- trx_mutex_exit(trx);
- /*--------------------------------------*/
-
- if (trx->is_referenced()) {
-
- ut_a(release_lock);
-
- lock_mutex_exit();
-
- while (trx->is_referenced()) {
-
- DEBUG_SYNC_C("waiting_trx_is_not_referenced");
-
- /** Doing an implicit to explicit conversion
- should not be expensive. */
- ut_delay(srv_spin_wait_delay);
- }
-
- lock_mutex_enter();
- }
-
- ut_ad(!trx->is_referenced());
-
- if (release_lock) {
-
- lock_release(trx);
-
- lock_mutex_exit();
- }
-
- trx->lock.n_rec_locks = 0;
-
- /* We don't remove the locks one by one from the vector for
- efficiency reasons. We simply reset it because we would have
- released all the locks anyway. */
-
- trx->lock.table_locks.clear();
-
- ut_a(UT_LIST_GET_LEN(trx->lock.trx_locks) == 0);
- ut_a(ib_vector_is_empty(trx->autoinc_locks));
- ut_a(trx->lock.table_locks.empty());
-
- mem_heap_empty(trx->lock.lock_heap);
-}
-
static inline dberr_t lock_trx_handle_wait_low(trx_t* trx)
{
ut_ad(lock_mutex_own());
@@ -6403,21 +6300,26 @@ static my_bool lock_table_locks_lookup(rw_trx_hash_element_t *element,
mutex_enter(&element->mutex);
if (element->trx)
{
+ trx_mutex_enter(element->trx);
check_trx_state(element->trx);
- for (const lock_t *lock= UT_LIST_GET_FIRST(element->trx->lock.trx_locks);
- lock != NULL;
- lock= UT_LIST_GET_NEXT(trx_locks, lock))
+ if (element->trx->state != TRX_STATE_COMMITTED_IN_MEMORY)
{
- ut_ad(lock->trx == element->trx);
- if (lock_get_type_low(lock) == LOCK_REC)
+ for (const lock_t *lock= UT_LIST_GET_FIRST(element->trx->lock.trx_locks);
+ lock != NULL;
+ lock= UT_LIST_GET_NEXT(trx_locks, lock))
{
- ut_ad(!dict_index_is_online_ddl(lock->index) ||
- dict_index_is_clust(lock->index));
- ut_ad(lock->index->table != table);
+ ut_ad(lock->trx == element->trx);
+ if (lock_get_type_low(lock) == LOCK_REC)
+ {
+ ut_ad(!dict_index_is_online_ddl(lock->index) ||
+ lock->index->is_primary());
+ ut_ad(lock->index->table != table);
+ }
+ else
+ ut_ad(lock->un_member.tab_lock.table != table);
}
- else
- ut_ad(lock->un_member.tab_lock.table != table);
}
+ trx_mutex_exit(element->trx);
}
mutex_exit(&element->mutex);
return 0;
@@ -6585,7 +6487,7 @@ DeadlockChecker::start_print()
if (srv_print_all_deadlocks) {
ib::info() << "Transactions deadlock detected, dumping"
- << " detailed information.";
+ " detailed information.";
}
}