Skip to content

Commit 040c16a

Browse files
committed
MDEV-25404: Optimize srw_mutex on Linux, OpenBSD, Windows
On Linux, OpenBSD and Microsoft Windows, srw_mutex was an alias for a rw-lock while we only need mutex functionality. Let us implement a futex-based mutex with one bit for HOLDER and 31 bits for counting waiting requests. srw_lock::wr_unlock() can avoid waking up a waiter when no waiting requests exist. (Previously, we only had 1-bit rw_lock::WRITER_WAITING flag that could be wrongly cleared if multiple waiting wr_lock() exist. Now we have no problem with up to 2,147,483,648 conflicting threads.) On 64-bit Microsoft Windows, the advantage is that sizeof(srw_mutex) is 4, while sizeof(SRWLOCK) would be 8. Reviewed by: Vladislav Vaintroub
1 parent af418bb commit 040c16a

File tree

2 files changed

+93
-7
lines changed

2 files changed

+93
-7
lines changed

storage/innobase/include/srw_lock.h

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*****************************************************************************
22
3-
Copyright (c) 2020, MariaDB Corporation.
3+
Copyright (c) 2020, 2021, MariaDB Corporation.
44
55
This program is free software; you can redistribute it and/or modify it under
66
the terms of the GNU General Public License as published by the Free Software
@@ -25,9 +25,9 @@ this program; if not, write to the Free Software Foundation, Inc.,
2525
# define SRW_LOCK_DUMMY /* Use dummy implementation for debugging purposes */
2626
#endif
2727

28-
#if defined SRW_LOCK_DUMMY && !(defined _WIN32)
28+
#if defined SRW_LOCK_DUMMY
2929
/** An exclusive-only variant of srw_lock */
30-
class srw_mutex
30+
class srw_mutex final
3131
{
3232
pthread_mutex_t lock;
3333
public:
@@ -38,7 +38,51 @@ class srw_mutex
3838
bool wr_lock_try() { return !pthread_mutex_trylock(&lock); }
3939
};
4040
#else
41-
# define srw_mutex srw_lock_low
41+
/** Futex-based mutex */
42+
class srw_mutex final
43+
{
44+
/** The lock word, containing HOLDER and a count of waiters */
45+
std::atomic<uint32_t> lock;
46+
/** Identifies that the lock is being held */
47+
static constexpr uint32_t HOLDER= 1U << 31;
48+
49+
/** Wait until the mutex has been acquired */
50+
void wait_and_lock();
51+
/** Wait for lock!=lk */
52+
inline void wait(uint32_t lk);
53+
/** Wake up one wait() thread */
54+
void wake();
55+
public:
56+
/** @return whether the mutex is being held or waited for */
57+
bool is_locked_or_waiting() const
58+
{ return lock.load(std::memory_order_relaxed) != 0; }
59+
/** @return whether the mutex is being held by any thread */
60+
bool is_locked() const
61+
{ return (lock.load(std::memory_order_relaxed) & HOLDER) != 0; }
62+
63+
void init() { DBUG_ASSERT(!is_locked_or_waiting()); }
64+
void destroy() { DBUG_ASSERT(!is_locked_or_waiting()); }
65+
66+
/** @return whether the mutex was acquired */
67+
bool wr_lock_try()
68+
{
69+
uint32_t lk= 0;
70+
return lock.compare_exchange_strong(lk, HOLDER,
71+
std::memory_order_acquire,
72+
std::memory_order_relaxed);
73+
}
74+
75+
void wr_lock() { if (!wr_lock_try()) wait_and_lock(); }
76+
void wr_unlock()
77+
{
78+
const uint32_t lk= lock.fetch_and(~HOLDER, std::memory_order_release);
79+
if (lk != HOLDER)
80+
{
81+
DBUG_ASSERT(lk & HOLDER);
82+
wake();
83+
}
84+
}
85+
};
4286
#endif
4387

4488
#include "rw_lock.h"

storage/innobase/sync/srw_lock.cc

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*****************************************************************************
22
3-
Copyright (c) 2020, MariaDB Corporation.
3+
Copyright (c) 2020, 2021, MariaDB Corporation.
44
55
This program is free software; you can redistribute it and/or modify it under
66
the terms of the GNU General Public License as published by the Free Software
@@ -73,6 +73,10 @@ static_assert(4 == sizeof(rw_lock), "ABI");
7373
# ifdef _WIN32
7474
# include <synchapi.h>
7575

76+
inline void srw_mutex::wait(uint32_t lk)
77+
{ WaitOnAddress(&lock, &lk, 4, INFINITE); }
78+
void srw_mutex::wake() { WakeByAddressSingle(&lock); }
79+
7680
inline void ssux_lock_low::writer_wait(uint32_t l)
7781
{
7882
WaitOnAddress(word(), &l, 4, INFINITE);
@@ -86,14 +90,17 @@ inline void ssux_lock_low::readers_wake() { WakeByAddressAll(word()); }
8690
# define SRW_FUTEX(a,op,n) \
8791
syscall(SYS_futex, a, FUTEX_ ## op ## _PRIVATE, n, nullptr, nullptr, 0)
8892
# elif defined __OpenBSD__
89-
# include <sys/time.h>
90-
# include <sys/futex.h>
93+
# include <sys/time.h>
94+
# include <sys/futex.h>
9195
# define SRW_FUTEX(a,op,n) \
9296
futex((volatile uint32_t*) a, FUTEX_ ## op, n, nullptr, nullptr)
9397
# else
9498
# error "no futex support"
9599
# endif
96100

101+
inline void srw_mutex::wait(uint32_t lk) { SRW_FUTEX(&lock, WAIT, lk); }
102+
void srw_mutex::wake() { SRW_FUTEX(&lock, WAKE, 1); }
103+
97104
inline void ssux_lock_low::writer_wait(uint32_t l)
98105
{
99106
SRW_FUTEX(word(), WAIT, l);
@@ -102,6 +109,41 @@ inline void ssux_lock_low::writer_wake() { SRW_FUTEX(word(), WAKE, 1); }
102109
inline void ssux_lock_low::readers_wake() { SRW_FUTEX(word(), WAKE, INT_MAX); }
103110
# endif
104111
# define readers_wait writer_wait
112+
113+
114+
void srw_mutex::wait_and_lock()
115+
{
116+
uint32_t lk= 1 + lock.fetch_add(1, std::memory_order_relaxed);
117+
for (auto spin= srv_n_spin_wait_rounds; spin; spin--)
118+
{
119+
lk&= ~HOLDER;
120+
DBUG_ASSERT(lk);
121+
while (!lock.compare_exchange_weak(lk, HOLDER | (lk - 1),
122+
std::memory_order_acquire,
123+
std::memory_order_relaxed))
124+
if (lk & HOLDER)
125+
goto occupied;
126+
return;
127+
occupied:
128+
ut_delay(srv_spin_wait_delay);
129+
}
130+
131+
for (;;)
132+
{
133+
lk= lock.load(std::memory_order_relaxed);
134+
while (!(lk & HOLDER))
135+
{
136+
DBUG_ASSERT(lk);
137+
if (lock.compare_exchange_weak(lk, HOLDER | (lk - 1),
138+
std::memory_order_acquire,
139+
std::memory_order_relaxed))
140+
return;
141+
}
142+
DBUG_ASSERT(lk > HOLDER);
143+
wait(lk);
144+
}
145+
}
146+
105147
#endif
106148

107149
/** Wait for a read lock.

0 commit comments

Comments
 (0)