1/*****************************************************************************
2
3Copyright (c) 2007, 2020, Oracle and/or its affiliates. All Rights Reserved.
4
5This program is free software; you can redistribute it and/or modify it under
6the terms of the GNU General Public License, version 2.0, as published by the
7Free Software Foundation.
8
9This program is also distributed with certain software (including but not
10limited to OpenSSL) that is licensed under separate terms, as designated in a
11particular file or component or in included license documentation. The authors
12of MySQL hereby grant you an additional permission to link the program and
13your derivative works with the separately licensed software that they have
14included with MySQL.
15
16This program is distributed in the hope that it will be useful, but WITHOUT
17ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19for more details.
20
21You should have received a copy of the GNU General Public License along with
22this program; if not, write to the Free Software Foundation, Inc.,
2351 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24
25*****************************************************************************/
26
27/** @file include/lock0priv.ic
28 Lock module internal inline methods.
29
30 Created July 16, 2007 Vasil Dimov
31 *******************************************************/
32
33/* This file contains only methods which are used in
34lock/lock0* files, other than lock/lock0lock.cc.
35I.e. lock/lock0lock.cc contains more internal inline
36methods but they are used only in that file. */
37
38#ifndef LOCK_MODULE_IMPLEMENTATION
39#error Do not include lock0priv.ic outside of the lock/ module
40#endif
41
42/** Gets the type of a lock.
43 @return LOCK_TABLE or LOCK_REC */
44UNIV_INLINE
45uint32_t lock_get_type_low(const lock_t *lock) /*!< in: lock */
46{
47  ut_ad(lock);
48
49  return (lock->type_mode & LOCK_TYPE_MASK);
50}
51
52/** Checks if some transaction has an implicit x-lock on a record in a clustered
53 index.
54 @return transaction id of the transaction which has the x-lock, or 0 */
55UNIV_INLINE
56trx_id_t lock_clust_rec_some_has_impl(
57    const rec_t *rec,          /*!< in: user record */
58    const dict_index_t *index, /*!< in: clustered index */
59    const ulint *offsets)      /*!< in: rec_get_offsets(rec, index) */
60{
61  ut_ad(index->is_clustered());
62  ut_ad(page_rec_is_user_rec(rec));
63
64  return (row_get_rec_trx_id(rec, index, offsets));
65}
66
67/** Gets the number of bits in a record lock bitmap.
68 @return	number of bits */
69UNIV_INLINE
70ulint lock_rec_get_n_bits(const lock_t *lock) /*!< in: record lock */
71{
72  return (lock->rec_lock.n_bits);
73}
74
75/** Sets the nth bit of a record lock to TRUE. */
76UNIV_INLINE
77void lock_rec_set_nth_bit(lock_t *lock, /*!< in: record lock */
78                          ulint i)      /*!< in: index of the bit */
79{
80  ulint byte_index;
81  ulint bit_index;
82
83  ut_ad(lock);
84  ut_ad(lock_get_type_low(lock) == LOCK_REC);
85  ut_ad(i < lock->rec_lock.n_bits);
86
87  byte_index = i / 8;
88  bit_index = i % 8;
89
90  ((byte *)&lock[1])[byte_index] |= 1 << bit_index;
91
92  lock->trx->lock.n_rec_locks.fetch_add(1, std::memory_order_relaxed);
93}
94
95/** Gets the first or next record lock on a page.
96 @return	next lock, NULL if none exists */
97UNIV_INLINE
98lock_t *lock_rec_get_next_on_page(lock_t *lock) /*!< in: a record lock */
99{
100  return ((lock_t *)lock_rec_get_next_on_page_const(lock));
101}
102
103UNIV_INLINE
104lock_t *lock_rec_get_first_on_page_addr(hash_table_t *lock_hash,
105                                        const page_id_t &page_id) {
106  ut_ad(locksys::owns_page_shard(page_id));
107
108  for (lock_t *lock = static_cast<lock_t *>(
109           HASH_GET_FIRST(lock_hash, lock_rec_hash(page_id)));
110       lock != nullptr;
111       lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, lock))) {
112    if (page_id == lock->rec_lock.page_id) {
113      return (lock);
114    }
115  }
116
117  return (nullptr);
118}
119
120/** Gets the first record lock on a page, where the page is identified by a
121 pointer to it.
122 @return	first lock, NULL if none exists */
123UNIV_INLINE
124lock_t *lock_rec_get_first_on_page(
125    hash_table_t *lock_hash,  /*!< in: lock hash table */
126    const buf_block_t *block) /*!< in: buffer block */
127{
128  ut_ad(locksys::owns_page_shard(block->get_page_id()));
129
130  const page_id_t page_id = block->page.id;
131  ulint hash = buf_block_get_lock_hash_val(block);
132
133  for (lock_t *lock = static_cast<lock_t *>(HASH_GET_FIRST(lock_hash, hash));
134       lock != nullptr;
135       lock = static_cast<lock_t *>(HASH_GET_NEXT(hash, lock))) {
136    if (page_id == lock->rec_lock.page_id) {
137      return (lock);
138    }
139  }
140
141  return (nullptr);
142}
143
144/** Gets the next explicit lock request on a record.
145 @return	next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED
146 */
147UNIV_INLINE
148lock_t *lock_rec_get_next(ulint heap_no, /*!< in: heap number of the record */
149                          lock_t *lock)  /*!< in: lock */
150{
151  ut_ad(locksys::owns_page_shard(lock->rec_lock.page_id));
152
153  do {
154    ut_ad(lock_get_type_low(lock) == LOCK_REC);
155    lock = lock_rec_get_next_on_page(lock);
156  } while (lock != nullptr && !lock_rec_get_nth_bit(lock, heap_no));
157
158  return (lock);
159}
160
161/** Gets the next explicit lock request on a record.
162 @return	next lock, NULL if none exists or if heap_no == ULINT_UNDEFINED
163 */
164UNIV_INLINE
165const lock_t *lock_rec_get_next_const(
166    ulint heap_no,      /*!< in: heap number of the record */
167    const lock_t *lock) /*!< in: lock */
168{
169  return (lock_rec_get_next(heap_no, (lock_t *)lock));
170}
171
172/** Gets the first explicit lock request on a record.
173 @return	first lock, NULL if none exists */
174UNIV_INLINE
175lock_t *lock_rec_get_first(
176    hash_table_t *hash,       /*!< in: hash chain the lock on */
177    const buf_block_t *block, /*!< in: block containing the record */
178    ulint heap_no)            /*!< in: heap number of the record */
179{
180  ut_ad(locksys::owns_page_shard(block->get_page_id()));
181
182  for (lock_t *lock = lock_rec_get_first_on_page(hash, block); lock;
183       lock = lock_rec_get_next_on_page(lock)) {
184    if (lock_rec_get_nth_bit(lock, heap_no)) {
185      return (lock);
186    }
187  }
188
189  return (nullptr);
190}
191
192/** Gets the nth bit of a record lock.
193@param[in]	lock		Record lock
194@param[in]	i		Index of the bit to check
195@return true if bit set also if i == ULINT_UNDEFINED return false */
196UNIV_INLINE
197bool lock_rec_get_nth_bit(const lock_t *lock, ulint i) {
198  const byte *b;
199
200  ut_ad(lock);
201  ut_ad(lock_get_type_low(lock) == LOCK_REC);
202
203  if (i >= lock->rec_lock.n_bits) {
204    return (false);
205  }
206
207  b = ((const byte *)&lock[1]) + (i / 8);
208
209  return (true & *b >> (i % 8));
210}
211
212/** Gets the first or next record lock on a page.
213 @return next lock, NULL if none exists */
214UNIV_INLINE
215const lock_t *lock_rec_get_next_on_page_const(
216    const lock_t *lock) /*!< in: a record lock */
217{
218  ut_ad(lock_get_type_low(lock) == LOCK_REC);
219  const auto page_id = lock->rec_lock.page_id;
220  ut_ad(locksys::owns_page_shard(page_id));
221
222  while ((lock = static_cast<const lock_t *>(HASH_GET_NEXT(hash, lock))) !=
223         nullptr) {
224    if (page_id == lock->rec_lock.page_id) {
225      return (lock);
226    }
227  }
228
229  return (nullptr);
230}
231
232/** Gets the mode of a lock.
233 @return mode */
234UNIV_INLINE
235enum lock_mode lock_get_mode(const lock_t *lock) /*!< in: lock */
236{
237  ut_ad(lock);
238
239  return (static_cast<enum lock_mode>(lock->type_mode & LOCK_MODE_MASK));
240}
241
242/** Calculates if lock mode 1 is compatible with lock mode 2.
243 @return nonzero if mode1 compatible with mode2 */
244UNIV_INLINE
245ulint lock_mode_compatible(enum lock_mode mode1, /*!< in: lock mode */
246                           enum lock_mode mode2) /*!< in: lock mode */
247{
248  ut_ad((ulint)mode1 < lock_types);
249  ut_ad((ulint)mode2 < lock_types);
250
251  return (lock_compatibility_matrix[mode1][mode2]);
252}
253
254UNIV_INLINE
255bool lock_mode_stronger_or_eq(enum lock_mode mode1, enum lock_mode mode2) {
256  ut_ad((ulint)mode1 < lock_types);
257  ut_ad((ulint)mode2 < lock_types);
258
259  return (lock_strength_matrix[mode1][mode2] != 0);
260}
261
262/** Gets the wait flag of a lock.
263 @return LOCK_WAIT if waiting, 0 if not */
264UNIV_INLINE
265ulint lock_get_wait(const lock_t *lock) /*!< in: lock */
266{
267  ut_ad(lock);
268
269  return (lock->type_mode & LOCK_WAIT);
270}
271
272/** The back pointer to a waiting lock request in the transaction is set to NULL
273 and the wait bit in lock type_mode is reset. */
274UNIV_INLINE
275void lock_reset_lock_and_trx_wait(lock_t *lock) /*!< in/out: record lock */
276{
277  ut_ad(locksys::owns_lock_shard(lock));
278  ut_ad(lock_get_wait(lock));
279  ut_ad(lock->trx->lock.wait_lock == lock);
280  /* We intentionally don't clear trx->lock.blocking_trx here, as
281  lock_reset_lock_and_trx_wait() is called also during movements of locks from
282  one page to another, which does not really change the structure of the
283  wait-for graph. Instead the lock_reset_wait_and_release_thread_if_suspended()
284  is responsible for clearing the blocking_trx field once it is sure that
285  we really want to remove the edge from the wait-for graph.*/
286  lock->trx->lock.wait_lock = nullptr;
287
288  /* We intentionally don't clear lock->trx->lock.wait_lock_type here, to make
289  it easier to obtain stats about the last wait in lock_wait_suspend_thread().
290  @see trx_lock_t::wait_lock_type for more detailed explanation. */
291  lock->type_mode &= ~LOCK_WAIT;
292}
293
294UNIV_INLINE
295bool lock_table_has(const trx_t *trx, const dict_table_t *table,
296                    lock_mode in_mode) {
297  ut_ad(!trx_mutex_own(trx));
298  trx_mutex_enter(trx);
299  ut_ad(trx_can_be_handled_by_current_thread(trx));
300
301  if (trx->lock.table_locks.empty()) {
302    trx_mutex_exit(trx);
303    return (false);
304  }
305
306  typedef lock_pool_t::const_reverse_iterator iterator;
307
308  iterator end = trx->lock.table_locks.rend();
309
310  /* Look for stronger locks the same trx already has on the table */
311
312  for (iterator it = trx->lock.table_locks.rbegin(); it != end; ++it) {
313    const lock_t *lock = *it;
314
315    ut_ad(lock != nullptr);
316
317    lock_mode mode = lock_get_mode(lock);
318
319    ut_ad(trx == lock->trx);
320    ut_ad(lock_get_type_low(lock) & LOCK_TABLE);
321    ut_ad(lock->tab_lock.table != nullptr);
322
323    if (table == lock->tab_lock.table &&
324        lock_mode_stronger_or_eq(mode, in_mode)) {
325      ut_ad(!lock_get_wait(lock));
326
327      trx_mutex_exit(trx);
328      return (true);
329    }
330  }
331
332  trx_mutex_exit(trx);
333  return (false);
334}
335
336/* Check if the rec id matches the lock instance.
337@param[i]	lock		Lock to compare with
338@return true if <space, page_no, heap_no> matches the lock. */
339bool RecID::matches(const lock_t *lock) const {
340  return (lock->rec_lock.page_id == get_page_id() &&
341          lock_rec_get_nth_bit(lock, m_heap_no));
342}
343
344/* vim: set filetype=c: */
345