summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2020-11-05 18:17:47 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2020-11-05 18:45:35 +0200
commit70cdb84101c96849a05b49edb841348e83cc4927 (patch)
tree2c461b03435acd44588edc0dcaf36b0311d10b8e
parent4b3343200e6d2041385cf14f96fbcd2238261df4 (diff)
downloadmariadb-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.ic69
-rw-r--r--storage/innobase/sync/sync0arr.cc11
-rw-r--r--storage/innobase/sync/sync0rw.cc3
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;