diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2020-11-05 18:17:47 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2020-11-05 18:45:35 +0200 |
commit | 70cdb84101c96849a05b49edb841348e83cc4927 (patch) | |
tree | 2c461b03435acd44588edc0dcaf36b0311d10b8e | |
parent | 4b3343200e6d2041385cf14f96fbcd2238261df4 (diff) | |
download | mariadb-git-bb-10.5-MDEV-21452.tar.gz |
amend 88722a7c2082e1af7b0f76fe106a61b95390ca56bb-10.5-MDEV-21452
This will be pushed separately as MDEV-24142 once it works.
rw_lock_sx_unlock_func(): Wake up an X-latch waiter if needed.
sync_array_wait_event(): Wake up even if a pending SX-latch
request exists.
rw_lock_x_unlock_func(): Clean up.
FIXME: rw_lock_x_lock_wait_func() can still get stuck in
sync_array_wait_event() with lock_word==-X_LOCK_HALF_DECR.
-rw-r--r-- | storage/innobase/include/sync0rw.ic | 69 | ||||
-rw-r--r-- | storage/innobase/sync/sync0arr.cc | 11 | ||||
-rw-r--r-- | storage/innobase/sync/sync0rw.cc | 3 |
3 files changed, 45 insertions, 38 deletions
diff --git a/storage/innobase/include/sync0rw.ic b/storage/innobase/include/sync0rw.ic index 9fc1de8f901..b2dc7ab6454 100644 --- a/storage/innobase/include/sync0rw.ic +++ b/storage/innobase/include/sync0rw.ic @@ -390,36 +390,34 @@ rw_lock_x_unlock_func( #endif /* UNIV_DEBUG */ rw_lock_t* lock) /*!< in/out: rw-lock */ { - int32_t lock_word = lock->lock_word; - - if (lock_word == 0) { - /* Last caller in a possible recursive chain. */ - lock->writer_thread = 0; - } - - ut_d(rw_lock_remove_debug_info(lock, pass, RW_LOCK_X)); - - if (lock_word == 0 || lock_word == -X_LOCK_HALF_DECR) { - /* Last X-lock owned by this thread, it may still hold SX-locks. - ACQ_REL due to... - RELEASE: we release rw-lock - ACQUIRE: we want waiters to be loaded after lock_word is stored */ - lock->lock_word.fetch_add(X_LOCK_DECR, - std::memory_order_acq_rel); - if (lock->waiters) { - lock->wakeup_waiters(); - } - } else if (lock_word == -X_LOCK_DECR - || lock_word == -(X_LOCK_DECR + X_LOCK_HALF_DECR)) { - /* There are 2 x-locks */ - lock->lock_word.fetch_add(X_LOCK_DECR); - } else { - /* There are more than 2 x-locks. */ - ut_ad(lock_word < -X_LOCK_DECR); - lock->lock_word.fetch_add(1); - } - - ut_ad(rw_lock_validate(lock)); + ut_d(rw_lock_remove_debug_info(lock, pass, RW_LOCK_X)); + + switch (int32_t lock_word= lock->lock_word) { + case 0: + /* Last caller in a possible recursive chain. */ + lock->writer_thread= 0; + /* fall through */ + case -X_LOCK_HALF_DECR: + /* Last X-lock owned by this thread, it may still hold SX-locks. + ACQ_REL due to... + RELEASE: we release rw-lock + ACQUIRE: we want waiters to be loaded after lock_word is stored */ + lock->lock_word.fetch_add(X_LOCK_DECR, std::memory_order_acq_rel); + if (lock->waiters) + lock->wakeup_waiters(); + break; + case -X_LOCK_DECR: + case -X_LOCK_DECR - X_LOCK_HALF_DECR: + /* There are 2 x-locks */ + lock->lock_word.fetch_add(X_LOCK_DECR); + break; + default: + /* There are more than 2 x-locks. */ + ut_ad(lock_word < -X_LOCK_DECR); + lock->lock_word.fetch_add(1); + } + + ut_ad(rw_lock_validate(lock)); } /******************************************************************//** @@ -436,12 +434,9 @@ rw_lock_sx_unlock_func( { ut_ad(rw_lock_get_sx_lock_count(lock)); ut_ad(lock->sx_recursive > 0); - - --lock->sx_recursive; - ut_d(rw_lock_remove_debug_info(lock, pass, RW_LOCK_SX)); - if (lock->sx_recursive == 0) { + if (--lock->sx_recursive == 0) { int32_t lock_word = lock->lock_word; /* Last caller in a possible recursive chain. */ if (lock_word > 0) { @@ -465,7 +460,11 @@ rw_lock_sx_unlock_func( /* still has x-lock */ ut_ad(lock_word == -X_LOCK_HALF_DECR || lock_word <= -(X_LOCK_DECR + X_LOCK_HALF_DECR)); - lock->lock_word.fetch_add(X_LOCK_HALF_DECR); + switch (lock->lock_word.fetch_add(X_LOCK_HALF_DECR)) { + case -X_LOCK_HALF_DECR * 2: + case -X_LOCK_HALF_DECR: + lock->wakeup_wait_ex(); + } } } diff --git a/storage/innobase/sync/sync0arr.cc b/storage/innobase/sync/sync0arr.cc index 53aa717a73e..cbf2715b7d5 100644 --- a/storage/innobase/sync/sync0arr.cc +++ b/storage/innobase/sync/sync0arr.cc @@ -414,9 +414,16 @@ sync_array_wait_event( rw_lock_t *lock= cell->latch; const int32_t lock_word = lock->lock_word; if (cell->request_type == RW_LOCK_X_WAIT) { - if (lock_word) { + switch (lock_word) { + case 0: + case -X_LOCK_DECR: + break; + default: mysql_mutex_lock(&lock->wait_mutex); - while (lock->lock_word) { + while (const int32_t l = lock->lock_word) { + if (l == -X_LOCK_DECR) { + break; + } mysql_cond_wait(&lock->wait_ex_cond, &lock->wait_mutex); } diff --git a/storage/innobase/sync/sync0rw.cc b/storage/innobase/sync/sync0rw.cc index c5a1aedf3e1..2da15645537 100644 --- a/storage/innobase/sync/sync0rw.cc +++ b/storage/innobase/sync/sync0rw.cc @@ -129,7 +129,8 @@ wait_ex_cond: A thread may only wait on the wait_ex_cond after it has (2) Verify that lock_word < 0. These restrictions force the above ordering. Immediately before sending the wake-up signal, we should: - Verify lock_word == 0 (waiting thread holds x_lock) + Verify lock_word == 0 || lock_word == -X_LOCK_HALF_DECR + (waiting thread holds x_lock) */ rw_lock_stats_t rw_lock_stats; |