Skip to content

Commit 1fdc161

Browse files
committed
MDEV-24167 fixup: Always derive srw_lock from rw_lock
Let us always base srw_lock on our own std::atomic<uint32_t> based rw_lock. In this way, we can extend the locks in a portable way across all platforms. We will use futex system calls where available: Linux, OpenBSD, and Microsoft Windows. Elsewhere, we will emulate futex with a mutex and a condition variable. Thanks to Daniel Black for testing this on OpenBSD.
1 parent 565b0dd commit 1fdc161

File tree

5 files changed

+233
-187
lines changed

5 files changed

+233
-187
lines changed

storage/innobase/CMakeLists.txt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ SET(INNOBASE_SOURCES
327327
srv/srv0mon.cc
328328
srv/srv0srv.cc
329329
srv/srv0start.cc
330+
sync/srw_lock.cc
330331
sync/sync0arr.cc
331332
sync/sync0rw.cc
332333
sync/sync0debug.cc
@@ -349,12 +350,6 @@ SET(INNOBASE_SOURCES
349350
ut/ut0vec.cc
350351
ut/ut0wqueue.cc)
351352

352-
IF (UNIX)
353-
IF(CMAKE_SYSTEM_NAME STREQUAL "Linux")
354-
SET(INNOBASE_SOURCES ${INNOBASE_SOURCES} "sync/srw_lock_futex.cc")
355-
ENDIF()
356-
ENDIF()
357-
358353
MYSQL_ADD_PLUGIN(innobase ${INNOBASE_SOURCES} STORAGE_ENGINE
359354
MODULE_OUTPUT_NAME ha_innodb
360355
DEFAULT RECOMPILE_FOR_EMBEDDED

storage/innobase/include/rw_lock.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ class rw_lock
7676
write_lock_wait_start();
7777
return false;
7878
}
79+
/** @return the lock word value */
80+
uint32_t value() const { return lock.load(std::memory_order_acquire); }
7981

8082
public:
8183
/** Default constructor */

storage/innobase/include/srw_lock.h

Lines changed: 68 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -19,133 +19,127 @@ this program; if not, write to the Free Software Foundation, Inc.,
1919
#pragma once
2020
#include "univ.i"
2121

22-
#if 0 // defined SAFE_MUTEX
23-
# define SRW_LOCK_DUMMY /* Use mysql_rwlock_t for debugging purposes */
22+
#if !(defined __linux__ || defined _WIN32 || defined __OpenBSD__)
23+
# define SRW_LOCK_DUMMY
24+
#elif 0 // defined SAFE_MUTEX
25+
# define SRW_LOCK_DUMMY /* Use dummy implementation for debugging purposes */
2426
#endif
2527

26-
#if defined SRW_LOCK_DUMMY || (!defined _WIN32 && !defined __linux__)
27-
#else
28-
# ifdef _WIN32
29-
# include <windows.h>
30-
# else
31-
# include "rw_lock.h"
32-
# endif
33-
#endif
28+
#include "rw_lock.h"
3429

30+
/** Slim reader-writer lock with no recursion */
31+
class srw_lock_low final : private rw_lock
32+
{
3533
#ifdef UNIV_PFS_RWLOCK
36-
# define SRW_LOCK_INIT(key) init(key)
37-
#else
38-
# define SRW_LOCK_INIT(key) init()
34+
friend class srw_lock;
3935
#endif
40-
41-
class srw_lock final
42-
#if defined __linux__ && !defined SRW_LOCK_DUMMY
43-
: protected rw_lock
36+
#ifdef SRW_LOCK_DUMMY
37+
pthread_mutex_t mutex;
38+
pthread_cond_t cond;
4439
#endif
45-
{
46-
#if defined SRW_LOCK_DUMMY || (!defined _WIN32 && !defined __linux__)
47-
mysql_rwlock_t lock;
48-
public:
49-
void SRW_LOCK_INIT(mysql_pfs_key_t key) { mysql_rwlock_init(key, &lock); }
50-
void destroy() { mysql_rwlock_destroy(&lock); }
51-
void rd_lock() { mysql_rwlock_rdlock(&lock); }
52-
void rd_unlock() { mysql_rwlock_unlock(&lock); }
53-
void wr_lock() { mysql_rwlock_wrlock(&lock); }
54-
void wr_unlock() { mysql_rwlock_unlock(&lock); }
55-
#else
56-
# ifdef UNIV_PFS_RWLOCK
57-
PSI_rwlock *pfs_psi;
58-
# endif
59-
# ifdef _WIN32
60-
SRWLOCK lock;
61-
bool read_trylock() { return TryAcquireSRWLockShared(&lock); }
62-
bool write_trylock() { return TryAcquireSRWLockExclusive(&lock); }
63-
void read_lock() { AcquireSRWLockShared(&lock); }
64-
void write_lock() { AcquireSRWLockExclusive(&lock); }
65-
# else
6640
/** @return pointer to the lock word */
6741
rw_lock *word() { return static_cast<rw_lock*>(this); }
6842
/** Wait for a read lock.
6943
@param l lock word from a failed read_trylock() */
7044
void read_lock(uint32_t l);
7145
/** Wait for a write lock after a failed write_trylock() */
7246
void write_lock();
73-
# endif
47+
/** Wait for signal
48+
@param l lock word from a failed acquisition */
49+
inline void wait(uint32_t l);
50+
/** Send signal to one waiter */
51+
inline void wake_one();
52+
/** Send signal to all waiters */
53+
inline void wake_all();
54+
public:
55+
#ifdef SRW_LOCK_DUMMY
56+
void init();
57+
void destroy();
58+
#else
59+
void init() { DBUG_ASSERT(!is_locked_or_waiting()); }
60+
void destroy() { DBUG_ASSERT(!is_locked_or_waiting()); }
61+
#endif
62+
bool rd_lock_try() { uint32_t l; return read_trylock(l); }
63+
bool wr_lock_try() { return write_trylock(); }
64+
void rd_lock() { uint32_t l; if (!read_trylock(l)) read_lock(l); }
65+
void wr_lock() { if (!write_trylock()) write_lock(); }
66+
void rd_unlock();
67+
void wr_unlock();
68+
};
69+
70+
#ifndef UNIV_PFS_RWLOCK
71+
# define SRW_LOCK_INIT(key) init()
72+
typedef srw_lock_low srw_lock;
73+
#else
74+
# define SRW_LOCK_INIT(key) init(key)
75+
76+
/** Slim reader-writer lock with PERFORMANCE_SCHEMA instrumentation */
77+
class srw_lock
78+
{
79+
srw_lock_low lock;
80+
PSI_rwlock *pfs_psi;
7481

7582
public:
76-
void SRW_LOCK_INIT(mysql_pfs_key_t key)
83+
void init(mysql_pfs_key_t key)
7784
{
78-
# ifdef UNIV_PFS_RWLOCK
85+
lock.init();
7986
pfs_psi= PSI_RWLOCK_CALL(init_rwlock)(key, this);
80-
# endif
81-
IF_WIN(lock= SRWLOCK_INIT, static_assert(4 == sizeof(rw_lock), "ABI"));
8287
}
8388
void destroy()
8489
{
85-
# ifdef UNIV_PFS_RWLOCK
8690
if (pfs_psi)
8791
{
8892
PSI_RWLOCK_CALL(destroy_rwlock)(pfs_psi);
8993
pfs_psi= nullptr;
9094
}
91-
# endif
92-
IF_WIN(, DBUG_ASSERT(!is_locked_or_waiting()));
95+
lock.destroy();
9396
}
9497
void rd_lock()
9598
{
96-
IF_WIN(, uint32_t l);
97-
# ifdef UNIV_PFS_RWLOCK
98-
if (read_trylock(IF_WIN(, l)))
99+
uint32_t l;
100+
if (lock.read_trylock(l))
99101
return;
100102
if (pfs_psi)
101103
{
102104
PSI_rwlock_locker_state state;
103105
PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_rdwait)
104106
(&state, pfs_psi, PSI_RWLOCK_READLOCK, __FILE__, __LINE__);
105-
read_lock(IF_WIN(, l));
107+
lock.read_lock(l);
106108
if (locker)
107109
PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, 0);
108110
return;
109111
}
110-
# endif /* UNIV_PFS_RWLOCK */
111-
IF_WIN(read_lock(), if (!read_trylock(l)) read_lock(l));
112+
lock.read_lock(l);
113+
}
114+
void rd_unlock()
115+
{
116+
if (pfs_psi)
117+
PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
118+
lock.rd_unlock();
112119
}
113120
void wr_lock()
114121
{
115-
# ifdef UNIV_PFS_RWLOCK
116-
if (write_trylock())
122+
if (lock.write_trylock())
117123
return;
118124
if (pfs_psi)
119125
{
120126
PSI_rwlock_locker_state state;
121127
PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_wrwait)
122128
(&state, pfs_psi, PSI_RWLOCK_WRITELOCK, __FILE__, __LINE__);
123-
write_lock();
129+
lock.write_lock();
124130
if (locker)
125131
PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, 0);
126132
return;
127133
}
128-
# endif /* UNIV_PFS_RWLOCK */
129-
IF_WIN(, if (!write_trylock())) write_lock();
130-
}
131-
#ifdef _WIN32
132-
void rd_unlock()
133-
{
134-
#ifdef UNIV_PFS_RWLOCK
135-
if (pfs_psi) PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
136-
#endif
137-
ReleaseSRWLockShared(&lock);
134+
lock.write_lock();
138135
}
139136
void wr_unlock()
140137
{
141-
#ifdef UNIV_PFS_RWLOCK
142-
if (pfs_psi) PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
143-
#endif
144-
ReleaseSRWLockExclusive(&lock);
138+
if (pfs_psi)
139+
PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
140+
lock.wr_unlock();
145141
}
146-
#else
147-
void rd_unlock();
148-
void wr_unlock();
149-
#endif
150-
#endif
142+
bool rd_lock_try() { return lock.rd_lock_try(); }
143+
bool wr_lock_try() { return lock.wr_lock_try(); }
151144
};
145+
#endif

storage/innobase/sync/srw_lock.cc

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*****************************************************************************
2+
3+
Copyright (c) 2020, MariaDB Corporation.
4+
5+
This program is free software; you can redistribute it and/or modify it under
6+
the terms of the GNU General Public License as published by the Free Software
7+
Foundation; version 2 of the License.
8+
9+
This program is distributed in the hope that it will be useful, but WITHOUT
10+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11+
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12+
13+
You should have received a copy of the GNU General Public License along with
14+
this program; if not, write to the Free Software Foundation, Inc.,
15+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
16+
17+
*****************************************************************************/
18+
19+
#include "srw_lock.h"
20+
#include "srv0srv.h"
21+
22+
#ifdef SRW_LOCK_DUMMY
23+
void srw_lock_low::init()
24+
{
25+
DBUG_ASSERT(!is_locked_or_waiting());
26+
pthread_mutex_init(&mutex, nullptr);
27+
pthread_cond_init(&cond, nullptr);
28+
}
29+
30+
void srw_lock_low::destroy()
31+
{
32+
DBUG_ASSERT(!is_locked_or_waiting());
33+
pthread_mutex_destroy(&mutex);
34+
pthread_cond_destroy(&cond);
35+
}
36+
37+
inline void srw_lock_low::wait(uint32_t l)
38+
{
39+
pthread_mutex_lock(&mutex);
40+
if (value() == l)
41+
pthread_cond_wait(&cond, &mutex);
42+
pthread_mutex_unlock(&mutex);
43+
}
44+
45+
inline void srw_lock_low::wake_one()
46+
{
47+
pthread_mutex_lock(&mutex);
48+
pthread_cond_signal(&cond);
49+
pthread_mutex_unlock(&mutex);
50+
}
51+
52+
inline void srw_lock_low::wake_all()
53+
{
54+
pthread_mutex_lock(&mutex);
55+
pthread_cond_broadcast(&cond);
56+
pthread_mutex_unlock(&mutex);
57+
}
58+
#else
59+
static_assert(4 == sizeof(rw_lock), "ABI");
60+
# ifdef _WIN32
61+
# include <synchapi.h>
62+
63+
inline void srw_lock_low::wait(uint32_t l)
64+
{
65+
WaitOnAddress(word(), &l, 4, INFINITE);
66+
}
67+
inline void srw_lock_low::wake_one() { WakeByAddressSingle(word()); }
68+
inline void srw_lock_low::wake_all() { WakeByAddressAll(word()); }
69+
# else
70+
# ifdef __linux__
71+
# include <linux/futex.h>
72+
# include <sys/syscall.h>
73+
# define SRW_FUTEX(a,op,n) \
74+
syscall(SYS_futex, a, FUTEX_ ## op ## _PRIVATE, n, nullptr, nullptr, 0)
75+
# elif defined __OpenBSD__
76+
# include <sys/time.h>
77+
# include <sys/futex.h>
78+
# define SRW_FUTEX(a,op,n) \
79+
futex((volatile uint32_t*) a, FUTEX_ ## op, n, nullptr, nullptr)
80+
# else
81+
# error "no futex support"
82+
# endif
83+
84+
inline void srw_lock_low::wait(uint32_t l) { SRW_FUTEX(word(), WAIT, l); }
85+
inline void srw_lock_low::wake_one() { SRW_FUTEX(word(), WAKE, 1); }
86+
inline void srw_lock_low::wake_all() { SRW_FUTEX(word(), WAKE, INT_MAX); }
87+
# endif
88+
#endif
89+
90+
/** Wait for a read lock.
91+
@param lock word value from a failed read_trylock() */
92+
void srw_lock_low::read_lock(uint32_t l)
93+
{
94+
do
95+
{
96+
if (l == WRITER_WAITING)
97+
{
98+
wake_writer:
99+
#ifdef SRW_LOCK_DUMMY
100+
pthread_mutex_lock(&mutex);
101+
{
102+
pthread_cond_signal(&cond);
103+
pthread_cond_wait(&cond, &mutex);
104+
l= value();
105+
}
106+
while (l == WRITER_WAITING);
107+
pthread_mutex_unlock(&mutex);
108+
continue;
109+
#else
110+
wake_one();
111+
#endif
112+
}
113+
else
114+
for (auto spin= srv_n_spin_wait_rounds; spin; spin--)
115+
{
116+
ut_delay(srv_spin_wait_delay);
117+
if (read_trylock(l))
118+
return;
119+
else if (l == WRITER_WAITING)
120+
goto wake_writer;
121+
}
122+
123+
wait(l);
124+
}
125+
while (!read_trylock(l));
126+
}
127+
128+
/** Wait for a write lock after a failed write_trylock() */
129+
void srw_lock_low::write_lock()
130+
{
131+
for (;;)
132+
{
133+
uint32_t l= write_lock_wait_start();
134+
/* We are the first writer to be granted the lock. Spin for a while. */
135+
for (auto spin= srv_n_spin_wait_rounds; spin; spin--)
136+
{
137+
if (write_lock_wait_try(l))
138+
return;
139+
if (!(l & WRITER_WAITING))
140+
l= write_lock_wait_start();
141+
ut_delay(srv_spin_wait_delay);
142+
}
143+
144+
if (write_lock_wait_try(l))
145+
return;
146+
147+
if (!(l & WRITER_WAITING))
148+
{
149+
if (l == UNLOCKED && write_trylock())
150+
return;
151+
l= write_lock_wait_start() | WRITER_WAITING;
152+
}
153+
else
154+
DBUG_ASSERT(~WRITER_WAITING & l);
155+
156+
wait(l);
157+
}
158+
}
159+
160+
void srw_lock_low::rd_unlock() { if (read_unlock()) wake_one(); }
161+
162+
void srw_lock_low::wr_unlock() { write_unlock(); wake_all(); }

0 commit comments

Comments
 (0)