📄 lock0lock.c
字号:
lock->trx = trx; lock->type_mode = (type_mode & ~LOCK_TYPE_MASK) | LOCK_REC; lock->index = index; lock->un_member.rec_lock.space = space; lock->un_member.rec_lock.page_no = page_no; lock->un_member.rec_lock.n_bits = n_bytes * 8; /* Reset to zero the bitmap which resides immediately after the lock struct */ lock_rec_bitmap_reset(lock); /* Set the bit corresponding to rec */ lock_rec_set_nth_bit(lock, heap_no); HASH_INSERT(lock_t, hash, lock_sys->rec_hash, lock_rec_fold(space, page_no), lock); if (type_mode & LOCK_WAIT) { lock_set_lock_and_trx_wait(lock, trx); } return(lock);}/*************************************************************************Enqueues a waiting request for a lock which cannot be granted immediately.Checks for deadlocks. */staticulintlock_rec_enqueue_waiting(/*=====================*/ /* out: DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED, or DB_SUCCESS; DB_SUCCESS means that there was a deadlock, but another transaction was chosen as a victim, and we got the lock immediately: no need to wait then */ ulint type_mode,/* in: lock mode this transaction is requesting: LOCK_S or LOCK_X, possibly ORed with LOCK_GAP or LOCK_REC_NOT_GAP, ORed with LOCK_INSERT_INTENTION if this waiting lock request is set when performing an insert of an index record */ rec_t* rec, /* in: record */ dict_index_t* index, /* in: index of record */ que_thr_t* thr) /* in: query thread */{ lock_t* lock; trx_t* trx; #ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex));#endif /* UNIV_SYNC_DEBUG */ /* Test if there already is some other reason to suspend thread: we do not enqueue a lock request if the query thread should be stopped anyway */ if (que_thr_stop(thr)) { ut_error; return(DB_QUE_THR_SUSPENDED); } trx = thr_get_trx(thr); if (trx->dict_operation) { ut_print_timestamp(stderr); fputs(" InnoDB: Error: a record lock wait happens in a dictionary operation!\n""InnoDB: Table name ", stderr); ut_print_name(stderr, trx, index->table_name); fputs(".\n""InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n", stderr); } /* Enqueue the lock request that will wait to be granted */ lock = lock_rec_create(type_mode | LOCK_WAIT, rec, index, trx); /* Check if a deadlock occurs: if yes, remove the lock request and return an error code */ if (lock_deadlock_occurs(lock, trx)) { lock_reset_lock_and_trx_wait(lock); lock_rec_reset_nth_bit(lock, rec_get_heap_no(rec, page_rec_is_comp(rec))); return(DB_DEADLOCK); } /* If there was a deadlock but we chose another transaction as a victim, it is possible that we already have the lock now granted! */ if (trx->wait_lock == NULL) { return(DB_SUCCESS); } trx->que_state = TRX_QUE_LOCK_WAIT; trx->was_chosen_as_deadlock_victim = FALSE; trx->wait_started = time(NULL); ut_a(que_thr_stop(thr));#ifdef UNIV_DEBUG if (lock_print_waits) { fprintf(stderr, "Lock wait for trx %lu in index ", (ulong) ut_dulint_get_low(trx->id)); ut_print_name(stderr, trx, index->name); }#endif /* UNIV_DEBUG */ return(DB_LOCK_WAIT); }/*************************************************************************Adds a record lock request in the record queue. The request is normallyadded as the last in the queue, but if there are no waiting lock requestson the record, and the request to be added is not a waiting request, wecan reuse a suitable record lock object already existing on the same page,just setting the appropriate bit in its bitmap. This is a low-level functionwhich does NOT check for deadlocks or lock compatibility! */staticlock_t*lock_rec_add_to_queue(/*==================*/ /* out: lock where the bit was set, NULL if out of memory */ ulint type_mode,/* in: lock mode, wait, gap etc. flags; type is ignored and replaced by LOCK_REC */ rec_t* rec, /* in: record on page */ dict_index_t* index, /* in: index of record */ trx_t* trx) /* in: transaction */{ lock_t* lock; lock_t* similar_lock = NULL; ulint heap_no; ibool somebody_waits = FALSE; #ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex));#endif /* UNIV_SYNC_DEBUG */ ut_ad((type_mode & (LOCK_WAIT | LOCK_GAP)) || ((type_mode & LOCK_MODE_MASK) != LOCK_S) || !lock_rec_other_has_expl_req(LOCK_X, 0, LOCK_WAIT, rec, trx)); ut_ad((type_mode & (LOCK_WAIT | LOCK_GAP)) || ((type_mode & LOCK_MODE_MASK) != LOCK_X) || !lock_rec_other_has_expl_req(LOCK_S, 0, LOCK_WAIT, rec, trx)); type_mode = type_mode | LOCK_REC; /* If rec is the supremum record, then we can reset the gap bit, as all locks on the supremum are automatically of the gap type, and we try to avoid unnecessary memory consumption of a new record lock struct for a gap type lock */ if (page_rec_is_supremum(rec)) { ut_ad(!(type_mode & LOCK_REC_NOT_GAP)); /* There should never be LOCK_REC_NOT_GAP on a supremum record, but let us play safe */ type_mode = type_mode & ~(LOCK_GAP | LOCK_REC_NOT_GAP); } /* Look for a waiting lock request on the same record or on a gap */ heap_no = rec_get_heap_no(rec, page_rec_is_comp(rec)); lock = lock_rec_get_first_on_page(rec); while (lock != NULL) { if (lock_get_wait(lock) && (lock_rec_get_nth_bit(lock, heap_no))) { somebody_waits = TRUE; } lock = lock_rec_get_next_on_page(lock); } /* Look for a similar record lock on the same page: if one is found and there are no waiting lock requests, we can just set the bit */ similar_lock = lock_rec_find_similar_on_page(type_mode, rec, trx); if (similar_lock && !somebody_waits && !(type_mode & LOCK_WAIT)) { lock_rec_set_nth_bit(similar_lock, heap_no); return(similar_lock); } return(lock_rec_create(type_mode, rec, index, trx));}/*************************************************************************This is a fast routine for locking a record in the most common cases:there are no explicit locks on the page, or there is just one lock, ownedby this transaction, and of the right type_mode. This is a low-level functionwhich does NOT look at implicit locks! Checks lock compatibility withinexplicit locks. This function sets a normal next-key lock, or in the case ofa page supremum record, a gap type lock. */UNIV_INLINEiboollock_rec_lock_fast(/*===============*/ /* out: TRUE if locking succeeded */ ibool impl, /* in: if TRUE, no lock is set if no wait is necessary: we assume that the caller will set an implicit lock */ ulint mode, /* in: lock mode: LOCK_X or LOCK_S possibly ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */ rec_t* rec, /* in: record */ dict_index_t* index, /* in: index of record */ que_thr_t* thr) /* in: query thread */{ lock_t* lock; ulint heap_no; trx_t* trx;#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex));#endif /* UNIV_SYNC_DEBUG */ ut_ad((LOCK_MODE_MASK & mode) != LOCK_S || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); ut_ad((LOCK_MODE_MASK & mode) != LOCK_X || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); ut_ad((LOCK_MODE_MASK & mode) == LOCK_S || (LOCK_MODE_MASK & mode) == LOCK_X); ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP || mode - (LOCK_MODE_MASK & mode) == 0 || mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP); heap_no = rec_get_heap_no(rec, page_rec_is_comp(rec)); lock = lock_rec_get_first_on_page(rec); trx = thr_get_trx(thr); if (lock == NULL) { if (!impl) { lock_rec_create(mode, rec, index, trx); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } return(TRUE); } if (lock_rec_get_next_on_page(lock)) { return(FALSE); } if (lock->trx != trx || lock->type_mode != (mode | LOCK_REC) || lock_rec_get_n_bits(lock) <= heap_no) { return(FALSE); } if (!impl) { /* If the nth bit of the record lock is already set then we do not set a new lock bit, otherwise we do set */ if (!lock_rec_get_nth_bit(lock, heap_no)) { lock_rec_set_nth_bit(lock, heap_no); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } } return(TRUE);}/*************************************************************************This is the general, and slower, routine for locking a record. This is alow-level function which does NOT look at implicit locks! Checks lockcompatibility within explicit locks. This function sets a normal next-keylock, or in the case of a page supremum record, a gap type lock. */staticulintlock_rec_lock_slow(/*===============*/ /* out: DB_SUCCESS, DB_LOCK_WAIT, or error code */ ibool impl, /* in: if TRUE, no lock is set if no wait is necessary: we assume that the caller will set an implicit lock */ ulint mode, /* in: lock mode: LOCK_X or LOCK_S possibly ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */ rec_t* rec, /* in: record */ dict_index_t* index, /* in: index of record */ que_thr_t* thr) /* in: query thread */{ trx_t* trx; ulint err;#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex));#endif /* UNIV_SYNC_DEBUG */ ut_ad((LOCK_MODE_MASK & mode) != LOCK_S || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); ut_ad((LOCK_MODE_MASK & mode) != LOCK_X || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); ut_ad((LOCK_MODE_MASK & mode) == LOCK_S || (LOCK_MODE_MASK & mode) == LOCK_X); ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP || mode - (LOCK_MODE_MASK & mode) == 0 || mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP); trx = thr_get_trx(thr); if (lock_rec_has_expl(mode, rec, trx)) { /* The trx already has a strong enough lock on rec: do nothing */ err = DB_SUCCESS; } else if (lock_rec_other_has_conflicting(mode, rec, trx)) { /* If another transaction has a non-gap conflicting request in the queue, as this transaction does not have a lock strong enough already granted on the record, we have to wait. */ err = lock_rec_enqueue_waiting(mode, rec, index, thr); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } else { if (!impl) { /* Set the requested lock on the record */ lock_rec_add_to_queue(LOCK_REC | mode, rec, index, trx); if (srv_locks_unsafe_for_binlog) { trx_register_new_rec_lock(trx, index); } } err = DB_SUCCESS; } return(err);}/*************************************************************************Tries to lock the specified record in the mode requested. If not immediatelypossible, enqueues a waiting lock request. This is a low-level functionwhich does NOT look at implicit locks! Checks lock compatibility withinexplicit locks. This function sets a normal next-key lock, or in the caseof a page supremum record, a gap type lock. */staticulintlock_rec_lock(/*==========*/ /* out: DB_SUCCESS, DB_LOCK_WAIT, or error code */ ibool impl, /* in: if TRUE, no lock is set if no wait is necessary: we assume that the caller will set an implicit lock */ ulint mode, /* in: lock mode: LOCK_X or LOCK_S possibly ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */ rec_t* rec, /* in: record */ dict_index_t* index, /* in: index of record */ que_thr_t* thr) /* in: query thread */{ ulint err;#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex));#endif /* UNIV_SYNC_DEBUG */ ut_ad((LOCK_MODE_MASK & mode) != LOCK_S || lock_table_has(thr_get_trx(thr), index->table, LOCK_IS)); ut_ad((LOCK_MODE_MASK & mode) != LOCK_X || lock_table_has(thr_get_trx(thr), index->table, LOCK_IX)); ut_ad((LOCK_MODE_MASK & mode) == LOCK_S || (LOCK_MODE_MASK & mode) == LOCK_X); ut_ad(mode - (LOCK_MODE_MASK & mode) == LOCK_GAP || mode - (LOCK_MODE_MASK & mode) == LOCK_REC_NOT_GAP || mode - (LOCK_MODE_MASK & mode) == 0); if (lock_rec_lock_fast(impl, mode, rec, index, thr)) { /* We try a simplified and faster subroutine for the most common cases */ err = DB_SUCCESS; } else { err = lock_rec_lock_slow(impl, mode, rec, index, thr); } return(err);}/*************************************************************************Checks if a waiting record lock request still has to wait in a queue. */staticiboollock_rec_has_to_wait_in_queue(/*==========================*/ /* out: TRUE if still has to wait */ lock_t* wait_lock) /* in: waiting record lock */{ lock_t* lock; ulint space; ulint page_no; ulint heap_no;#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&kernel_mutex));#endif /* UNIV_SYNC_DEBUG */ ut_ad(lock_get_wait(wait_lock)); ut_ad(lock_get_type(wait_lock) == LOCK_REC); space = wait_lock->un_member.rec_lock.space; page_no = wait_lock->un_member.rec_lock.page_no; heap_no = lock_rec_find_set_bit(wait_lock); lock = lock_rec_get_first_on_page_addr(space, page_no); while (lock != wait_lock) { if (lock_rec_get_nth_bit(lock, heap_no) && lock_has_to_wait(wait_lock, lock))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -