Commit 1fdc161d authored by Marko Mäkelä's avatar Marko Mäkelä

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.
parent 565b0dd1
...@@ -327,6 +327,7 @@ SET(INNOBASE_SOURCES ...@@ -327,6 +327,7 @@ SET(INNOBASE_SOURCES
srv/srv0mon.cc srv/srv0mon.cc
srv/srv0srv.cc srv/srv0srv.cc
srv/srv0start.cc srv/srv0start.cc
sync/srw_lock.cc
sync/sync0arr.cc sync/sync0arr.cc
sync/sync0rw.cc sync/sync0rw.cc
sync/sync0debug.cc sync/sync0debug.cc
...@@ -349,12 +350,6 @@ SET(INNOBASE_SOURCES ...@@ -349,12 +350,6 @@ SET(INNOBASE_SOURCES
ut/ut0vec.cc ut/ut0vec.cc
ut/ut0wqueue.cc) ut/ut0wqueue.cc)
IF (UNIX)
IF(CMAKE_SYSTEM_NAME STREQUAL "Linux")
SET(INNOBASE_SOURCES ${INNOBASE_SOURCES} "sync/srw_lock_futex.cc")
ENDIF()
ENDIF()
MYSQL_ADD_PLUGIN(innobase ${INNOBASE_SOURCES} STORAGE_ENGINE MYSQL_ADD_PLUGIN(innobase ${INNOBASE_SOURCES} STORAGE_ENGINE
MODULE_OUTPUT_NAME ha_innodb MODULE_OUTPUT_NAME ha_innodb
DEFAULT RECOMPILE_FOR_EMBEDDED DEFAULT RECOMPILE_FOR_EMBEDDED
......
...@@ -76,6 +76,8 @@ class rw_lock ...@@ -76,6 +76,8 @@ class rw_lock
write_lock_wait_start(); write_lock_wait_start();
return false; return false;
} }
/** @return the lock word value */
uint32_t value() const { return lock.load(std::memory_order_acquire); }
public: public:
/** Default constructor */ /** Default constructor */
......
...@@ -19,50 +19,24 @@ this program; if not, write to the Free Software Foundation, Inc., ...@@ -19,50 +19,24 @@ this program; if not, write to the Free Software Foundation, Inc.,
#pragma once #pragma once
#include "univ.i" #include "univ.i"
#if 0 // defined SAFE_MUTEX #if !(defined __linux__ || defined _WIN32 || defined __OpenBSD__)
# define SRW_LOCK_DUMMY /* Use mysql_rwlock_t for debugging purposes */ # define SRW_LOCK_DUMMY
#elif 0 // defined SAFE_MUTEX
# define SRW_LOCK_DUMMY /* Use dummy implementation for debugging purposes */
#endif #endif
#if defined SRW_LOCK_DUMMY || (!defined _WIN32 && !defined __linux__) #include "rw_lock.h"
#else
# ifdef _WIN32
# include <windows.h>
# else
# include "rw_lock.h"
# endif
#endif
/** Slim reader-writer lock with no recursion */
class srw_lock_low final : private rw_lock
{
#ifdef UNIV_PFS_RWLOCK #ifdef UNIV_PFS_RWLOCK
# define SRW_LOCK_INIT(key) init(key) friend class srw_lock;
#else
# define SRW_LOCK_INIT(key) init()
#endif #endif
#ifdef SRW_LOCK_DUMMY
class srw_lock final pthread_mutex_t mutex;
#if defined __linux__ && !defined SRW_LOCK_DUMMY pthread_cond_t cond;
: protected rw_lock
#endif #endif
{
#if defined SRW_LOCK_DUMMY || (!defined _WIN32 && !defined __linux__)
mysql_rwlock_t lock;
public:
void SRW_LOCK_INIT(mysql_pfs_key_t key) { mysql_rwlock_init(key, &lock); }
void destroy() { mysql_rwlock_destroy(&lock); }
void rd_lock() { mysql_rwlock_rdlock(&lock); }
void rd_unlock() { mysql_rwlock_unlock(&lock); }
void wr_lock() { mysql_rwlock_wrlock(&lock); }
void wr_unlock() { mysql_rwlock_unlock(&lock); }
#else
# ifdef UNIV_PFS_RWLOCK
PSI_rwlock *pfs_psi;
# endif
# ifdef _WIN32
SRWLOCK lock;
bool read_trylock() { return TryAcquireSRWLockShared(&lock); }
bool write_trylock() { return TryAcquireSRWLockExclusive(&lock); }
void read_lock() { AcquireSRWLockShared(&lock); }
void write_lock() { AcquireSRWLockExclusive(&lock); }
# else
/** @return pointer to the lock word */ /** @return pointer to the lock word */
rw_lock *word() { return static_cast<rw_lock*>(this); } rw_lock *word() { return static_cast<rw_lock*>(this); }
/** Wait for a read lock. /** Wait for a read lock.
...@@ -70,82 +44,102 @@ class srw_lock final ...@@ -70,82 +44,102 @@ class srw_lock final
void read_lock(uint32_t l); void read_lock(uint32_t l);
/** Wait for a write lock after a failed write_trylock() */ /** Wait for a write lock after a failed write_trylock() */
void write_lock(); void write_lock();
# endif /** Wait for signal
@param l lock word from a failed acquisition */
inline void wait(uint32_t l);
/** Send signal to one waiter */
inline void wake_one();
/** Send signal to all waiters */
inline void wake_all();
public:
#ifdef SRW_LOCK_DUMMY
void init();
void destroy();
#else
void init() { DBUG_ASSERT(!is_locked_or_waiting()); }
void destroy() { DBUG_ASSERT(!is_locked_or_waiting()); }
#endif
bool rd_lock_try() { uint32_t l; return read_trylock(l); }
bool wr_lock_try() { return write_trylock(); }
void rd_lock() { uint32_t l; if (!read_trylock(l)) read_lock(l); }
void wr_lock() { if (!write_trylock()) write_lock(); }
void rd_unlock();
void wr_unlock();
};
#ifndef UNIV_PFS_RWLOCK
# define SRW_LOCK_INIT(key) init()
typedef srw_lock_low srw_lock;
#else
# define SRW_LOCK_INIT(key) init(key)
/** Slim reader-writer lock with PERFORMANCE_SCHEMA instrumentation */
class srw_lock
{
srw_lock_low lock;
PSI_rwlock *pfs_psi;
public: public:
void SRW_LOCK_INIT(mysql_pfs_key_t key) void init(mysql_pfs_key_t key)
{ {
# ifdef UNIV_PFS_RWLOCK lock.init();
pfs_psi= PSI_RWLOCK_CALL(init_rwlock)(key, this); pfs_psi= PSI_RWLOCK_CALL(init_rwlock)(key, this);
# endif
IF_WIN(lock= SRWLOCK_INIT, static_assert(4 == sizeof(rw_lock), "ABI"));
} }
void destroy() void destroy()
{ {
# ifdef UNIV_PFS_RWLOCK
if (pfs_psi) if (pfs_psi)
{ {
PSI_RWLOCK_CALL(destroy_rwlock)(pfs_psi); PSI_RWLOCK_CALL(destroy_rwlock)(pfs_psi);
pfs_psi= nullptr; pfs_psi= nullptr;
} }
# endif lock.destroy();
IF_WIN(, DBUG_ASSERT(!is_locked_or_waiting()));
} }
void rd_lock() void rd_lock()
{ {
IF_WIN(, uint32_t l); uint32_t l;
# ifdef UNIV_PFS_RWLOCK if (lock.read_trylock(l))
if (read_trylock(IF_WIN(, l)))
return; return;
if (pfs_psi) if (pfs_psi)
{ {
PSI_rwlock_locker_state state; PSI_rwlock_locker_state state;
PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_rdwait) PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_rdwait)
(&state, pfs_psi, PSI_RWLOCK_READLOCK, __FILE__, __LINE__); (&state, pfs_psi, PSI_RWLOCK_READLOCK, __FILE__, __LINE__);
read_lock(IF_WIN(, l)); lock.read_lock(l);
if (locker) if (locker)
PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, 0); PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, 0);
return; return;
} }
# endif /* UNIV_PFS_RWLOCK */ lock.read_lock(l);
IF_WIN(read_lock(), if (!read_trylock(l)) read_lock(l)); }
void rd_unlock()
{
if (pfs_psi)
PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
lock.rd_unlock();
} }
void wr_lock() void wr_lock()
{ {
# ifdef UNIV_PFS_RWLOCK if (lock.write_trylock())
if (write_trylock())
return; return;
if (pfs_psi) if (pfs_psi)
{ {
PSI_rwlock_locker_state state; PSI_rwlock_locker_state state;
PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_wrwait) PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_wrwait)
(&state, pfs_psi, PSI_RWLOCK_WRITELOCK, __FILE__, __LINE__); (&state, pfs_psi, PSI_RWLOCK_WRITELOCK, __FILE__, __LINE__);
write_lock(); lock.write_lock();
if (locker) if (locker)
PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, 0); PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, 0);
return; return;
} }
# endif /* UNIV_PFS_RWLOCK */ lock.write_lock();
IF_WIN(, if (!write_trylock())) write_lock();
}
#ifdef _WIN32
void rd_unlock()
{
#ifdef UNIV_PFS_RWLOCK
if (pfs_psi) PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
#endif
ReleaseSRWLockShared(&lock);
} }
void wr_unlock() void wr_unlock()
{ {
#ifdef UNIV_PFS_RWLOCK if (pfs_psi)
if (pfs_psi) PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi); PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
#endif lock.wr_unlock();
ReleaseSRWLockExclusive(&lock);
} }
#else bool rd_lock_try() { return lock.rd_lock_try(); }
void rd_unlock(); bool wr_lock_try() { return lock.wr_lock_try(); }
void wr_unlock();
#endif
#endif
}; };
#endif
...@@ -16,30 +16,100 @@ this program; if not, write to the Free Software Foundation, Inc., ...@@ -16,30 +16,100 @@ this program; if not, write to the Free Software Foundation, Inc.,
*****************************************************************************/ *****************************************************************************/
#ifndef __linux__
# error "This file is for Linux only"
#endif
#include "srw_lock.h" #include "srw_lock.h"
#include "srv0srv.h"
#ifdef SRW_LOCK_DUMMY #ifdef SRW_LOCK_DUMMY
/* Work around a potential build failure by preventing an empty .o file */ void srw_lock_low::init()
int srw_lock_dummy_function() { return 0; } {
DBUG_ASSERT(!is_locked_or_waiting());
pthread_mutex_init(&mutex, nullptr);
pthread_cond_init(&cond, nullptr);
}
void srw_lock_low::destroy()
{
DBUG_ASSERT(!is_locked_or_waiting());
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}
inline void srw_lock_low::wait(uint32_t l)
{
pthread_mutex_lock(&mutex);
if (value() == l)
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
}
inline void srw_lock_low::wake_one()
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
inline void srw_lock_low::wake_all()
{
pthread_mutex_lock(&mutex);
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
#else #else
# include <linux/futex.h> static_assert(4 == sizeof(rw_lock), "ABI");
# include <sys/syscall.h> # ifdef _WIN32
# include <synchapi.h>
# include "srv0srv.h" inline void srw_lock_low::wait(uint32_t l)
{
WaitOnAddress(word(), &l, 4, INFINITE);
}
inline void srw_lock_low::wake_one() { WakeByAddressSingle(word()); }
inline void srw_lock_low::wake_all() { WakeByAddressAll(word()); }
# else
# ifdef __linux__
# include <linux/futex.h>
# include <sys/syscall.h>
# define SRW_FUTEX(a,op,n) \
syscall(SYS_futex, a, FUTEX_ ## op ## _PRIVATE, n, nullptr, nullptr, 0)
# elif defined __OpenBSD__
# include <sys/time.h>
# include <sys/futex.h>
# define SRW_FUTEX(a,op,n) \
futex((volatile uint32_t*) a, FUTEX_ ## op, n, nullptr, nullptr)
# else
# error "no futex support"
# endif
inline void srw_lock_low::wait(uint32_t l) { SRW_FUTEX(word(), WAIT, l); }
inline void srw_lock_low::wake_one() { SRW_FUTEX(word(), WAKE, 1); }
inline void srw_lock_low::wake_all() { SRW_FUTEX(word(), WAKE, INT_MAX); }
# endif
#endif
/** Wait for a read lock. /** Wait for a read lock.
@param lock word value from a failed read_trylock() */ @param lock word value from a failed read_trylock() */
void srw_lock::read_lock(uint32_t l) void srw_lock_low::read_lock(uint32_t l)
{ {
do do
{ {
if (l == WRITER_WAITING) if (l == WRITER_WAITING)
{
wake_writer: wake_writer:
syscall(SYS_futex, word(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0); #ifdef SRW_LOCK_DUMMY
pthread_mutex_lock(&mutex);
{
pthread_cond_signal(&cond);
pthread_cond_wait(&cond, &mutex);
l= value();
}
while (l == WRITER_WAITING);
pthread_mutex_unlock(&mutex);
continue;
#else
wake_one();
#endif
}
else else
for (auto spin= srv_n_spin_wait_rounds; spin; spin--) for (auto spin= srv_n_spin_wait_rounds; spin; spin--)
{ {
...@@ -50,13 +120,13 @@ void srw_lock::read_lock(uint32_t l) ...@@ -50,13 +120,13 @@ void srw_lock::read_lock(uint32_t l)
goto wake_writer; goto wake_writer;
} }
syscall(SYS_futex, word(), FUTEX_WAIT_PRIVATE, l, nullptr, nullptr, 0); wait(l);
} }
while (!read_trylock(l)); while (!read_trylock(l));
} }
/** Wait for a write lock after a failed write_trylock() */ /** Wait for a write lock after a failed write_trylock() */
void srw_lock::write_lock() void srw_lock_low::write_lock()
{ {
for (;;) for (;;)
{ {
...@@ -83,25 +153,10 @@ void srw_lock::write_lock() ...@@ -83,25 +153,10 @@ void srw_lock::write_lock()
else else
DBUG_ASSERT(~WRITER_WAITING & l); DBUG_ASSERT(~WRITER_WAITING & l);
syscall(SYS_futex, word(), FUTEX_WAIT_PRIVATE, l, nullptr, nullptr, 0); wait(l);
} }
} }
void srw_lock::rd_unlock() void srw_lock_low::rd_unlock() { if (read_unlock()) wake_one(); }
{
#ifdef UNIV_PFS_RWLOCK
if (pfs_psi) PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
#endif
if (read_unlock())
syscall(SYS_futex, word(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
}
void srw_lock::wr_unlock() void srw_lock_low::wr_unlock() { write_unlock(); wake_all(); }
{
#ifdef UNIV_PFS_RWLOCK
if (pfs_psi) PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
#endif
write_unlock();
syscall(SYS_futex, word(), FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment