summaryrefslogtreecommitdiff
path: root/storage/innobase/include
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2020-11-24 15:41:03 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2020-11-24 15:41:03 +0200
commitc561f9e6e857dbae905c3d01db9ffa5b2ae2e6db (patch)
tree508626dbb5380f3b1a31191e17bc710cd392ce7b /storage/innobase/include
parentf87e4b4e4d2ef25a8f98b4ad8ce1ce2407262940 (diff)
downloadmariadb-git-c561f9e6e857dbae905c3d01db9ffa5b2ae2e6db.tar.gz
MDEV-24167: Use lightweight srw_lock for btr_search_latch
Many InnoDB rw-locks unnecessarily depend on the complex InnoDB rw_lock_t implementation that support the SX lock mode as well as recursive acquisition of X or SX locks. One of them is the bunch of adaptive hash index search latches, instrumented as btr_search_latch in PERFORMANCE_SCHEMA. Let us introduce a simpler lock for those in order to reduce overhead. srw_lock: A simple read-write lock that does not support recursion. On Microsoft Windows, this wraps SRWLOCK, only adding runtime overhead if PERFORMANCE_SCHEMA is enabled. On Linux (all architectures), this is implemented with std::atomic<uint32_t> and the futex system call. On other platforms, we will wrap mysql_rwlock_t with zero runtime overhead. The PERFORMANCE_SCHEMA instrumentation differs from InnoDB rw_lock_t in that we will only invoke PSI_RWLOCK_CALL(start_rwlock_wrwait) or PSI_RWLOCK_CALL(start_rwlock_rdwait) if there is an actual conflict.
Diffstat (limited to 'storage/innobase/include')
-rw-r--r--storage/innobase/include/btr0cur.h8
-rw-r--r--storage/innobase/include/btr0pcur.h5
-rw-r--r--storage/innobase/include/btr0pcur.ic5
-rw-r--r--storage/innobase/include/btr0sea.h49
-rw-r--r--storage/innobase/include/btr0sea.ic53
-rw-r--r--storage/innobase/include/rw_lock.h34
-rw-r--r--storage/innobase/include/srw_lock.h145
-rw-r--r--storage/innobase/include/sync0sync.h1
-rw-r--r--storage/innobase/include/sync0types.h3
9 files changed, 200 insertions, 103 deletions
diff --git a/storage/innobase/include/btr0cur.h b/storage/innobase/include/btr0cur.h
index 7136d726db0..46a5bf397d2 100644
--- a/storage/innobase/include/btr0cur.h
+++ b/storage/innobase/include/btr0cur.h
@@ -33,6 +33,9 @@ Created 10/16/1994 Heikki Tuuri
#include "rem0types.h"
#include "gis0type.h"
#include "my_base.h"
+#ifdef BTR_CUR_HASH_ADAPT
+# include "srw_lock.h"
+#endif
/** Mode flags for btr_cur operations; these can be ORed */
enum {
@@ -202,9 +205,8 @@ btr_cur_search_to_nth_level_func(
btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is
s- or x-latched, but see also above! */
#ifdef BTR_CUR_HASH_ADAPT
- rw_lock_t* ahi_latch,
- /*!< in: currently held btr_search_latch
- (in RW_S_LATCH mode), or NULL */
+ srw_lock* ahi_latch,
+ /*!< in: currently held AHI rdlock, or NULL */
#endif /* BTR_CUR_HASH_ADAPT */
const char* file, /*!< in: file name */
unsigned line, /*!< in: line where called */
diff --git a/storage/innobase/include/btr0pcur.h b/storage/innobase/include/btr0pcur.h
index bc7afbf3b67..a1f6034345d 100644
--- a/storage/innobase/include/btr0pcur.h
+++ b/storage/innobase/include/btr0pcur.h
@@ -137,9 +137,8 @@ btr_pcur_open_with_no_init_func(
that the ahi_latch protects the record! */
btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */
#ifdef BTR_CUR_HASH_ADAPT
- rw_lock_t* ahi_latch,
- /*!< in: adaptive hash index latch held
- by the caller, or NULL if none */
+ srw_lock* ahi_latch,
+ /*!< in: currently held AHI rdlock, or NULL */
#endif /* BTR_CUR_HASH_ADAPT */
const char* file, /*!< in: file name */
unsigned line, /*!< in: line where called */
diff --git a/storage/innobase/include/btr0pcur.ic b/storage/innobase/include/btr0pcur.ic
index d93da475a1f..c436a110757 100644
--- a/storage/innobase/include/btr0pcur.ic
+++ b/storage/innobase/include/btr0pcur.ic
@@ -483,9 +483,8 @@ btr_pcur_open_with_no_init_func(
that the ahi_latch protects the record! */
btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */
#ifdef BTR_CUR_HASH_ADAPT
- rw_lock_t* ahi_latch,
- /*!< in: adaptive hash index latch held
- by the caller, or NULL if none */
+ srw_lock* ahi_latch,
+ /*!< in: currently held AHI rdlock, or NULL */
#endif /* BTR_CUR_HASH_ADAPT */
const char* file, /*!< in: file name */
unsigned line, /*!< in: line where called */
diff --git a/storage/innobase/include/btr0sea.h b/storage/innobase/include/btr0sea.h
index 1e6b667c324..c7cfb1f259c 100644
--- a/storage/innobase/include/btr0sea.h
+++ b/storage/innobase/include/btr0sea.h
@@ -30,7 +30,11 @@ Created 2/17/1996 Heikki Tuuri
#include "dict0dict.h"
#ifdef BTR_CUR_HASH_ADAPT
#include "ha0ha.h"
-#include "sync0sync.h"
+#include "srw_lock.h"
+
+#ifdef UNIV_PFS_RWLOCK
+extern mysql_pfs_key_t btr_search_latch_key;
+#endif /* UNIV_PFS_RWLOCK */
#define btr_search_sys_create() btr_search_sys.create()
#define btr_search_sys_free() btr_search_sys.free()
@@ -77,7 +81,7 @@ btr_search_guess_on_hash(
ulint mode,
ulint latch_mode,
btr_cur_t* cursor,
- rw_lock_t* ahi_latch,
+ srw_lock* ahi_latch,
mtr_t* mtr);
/** Move or delete hash entries for moved records, usually in a page split.
@@ -109,8 +113,8 @@ void btr_search_drop_page_hash_when_freed(const page_id_t page_id);
using btr_cur_search_, and the new record has been
inserted next to the cursor.
@param[in] ahi_latch the adaptive hash index latch */
-void
-btr_search_update_hash_node_on_insert(btr_cur_t* cursor, rw_lock_t* ahi_latch);
+void btr_search_update_hash_node_on_insert(btr_cur_t *cursor,
+ srw_lock *ahi_latch);
/** Updates the page hash index when a single record is inserted on a page.
@param[in,out] cursor cursor which was positioned to the
@@ -118,8 +122,8 @@ btr_search_update_hash_node_on_insert(btr_cur_t* cursor, rw_lock_t* ahi_latch);
and the new record has been inserted next
to the cursor
@param[in] ahi_latch the adaptive hash index latch */
-void
-btr_search_update_hash_on_insert(btr_cur_t* cursor, rw_lock_t* ahi_latch);
+void btr_search_update_hash_on_insert(btr_cur_t *cursor,
+ srw_lock *ahi_latch);
/** Updates the page hash index when a single record is deleted from a page.
@param[in] cursor cursor which was positioned on the record to delete
@@ -139,23 +143,6 @@ static inline void btr_search_x_unlock_all();
/** Lock all search latches in shared mode. */
static inline void btr_search_s_lock_all();
-#ifdef UNIV_DEBUG
-/** Check if thread owns all the search latches.
-@param[in] mode lock mode check
-@retval true if owns all of them
-@retval false if does not own some of them */
-static inline bool btr_search_own_all(ulint mode);
-
-/** Check if thread owns any of the search latches.
-@param[in] mode lock mode check
-@retval true if owns any of them
-@retval false if owns no search latch */
-static inline bool btr_search_own_any(ulint mode);
-
-/** @return whether this thread holds any of the search latches */
-static inline bool btr_search_own_any();
-#endif /* UNIV_DEBUG */
-
/** Unlock all search latches from shared mode. */
static inline void btr_search_s_unlock_all();
@@ -250,20 +237,20 @@ struct btr_search_sys_t
struct partition
{
/** latches protecting hash_table */
- rw_lock_t latch;
+ srw_lock latch;
/** mapping of dtuple_fold() to rec_t* in buf_block_t::frame */
hash_table_t table;
/** memory heap for table */
mem_heap_t *heap;
- char pad[(CPU_LEVEL1_DCACHE_LINESIZE - sizeof(rw_lock_t) -
+ char pad[(CPU_LEVEL1_DCACHE_LINESIZE - sizeof(srw_lock) -
sizeof(hash_table_t) - sizeof(mem_heap_t)) &
(CPU_LEVEL1_DCACHE_LINESIZE - 1)];
void init()
{
memset((void*) this, 0, sizeof *this);
- rw_lock_create(btr_search_latch_key, &latch, SYNC_SEARCH_SYS);
+ latch.init(btr_search_latch_key);
}
void alloc(ulint hash_size)
@@ -285,7 +272,7 @@ struct btr_search_sys_t
void free()
{
- rw_lock_free(&latch);
+ latch.destroy();
if (heap)
clear();
}
@@ -309,7 +296,7 @@ struct btr_search_sys_t
}
/** Get the search latch for the adaptive hash index partition */
- rw_lock_t *get_latch(const dict_index_t &index) const
+ srw_lock *get_latch(const dict_index_t &index) const
{ return &get_part(index)->latch; }
/** Create and initialize at startup */
@@ -354,10 +341,10 @@ inline ulint dict_index_t::n_ahi_pages() const
{
if (!btr_search_enabled)
return 0;
- rw_lock_t *latch = &btr_search_sys.get_part(*this)->latch;
- rw_lock_s_lock(latch);
+ srw_lock *latch= &btr_search_sys.get_part(*this)->latch;
+ latch->rd_lock();
ulint ref_count= search_info->ref_count;
- rw_lock_s_unlock(latch);
+ latch->rd_unlock();
return ref_count;
}
diff --git a/storage/innobase/include/btr0sea.ic b/storage/innobase/include/btr0sea.ic
index 40eb5d86ead..f0d35203c3a 100644
--- a/storage/innobase/include/btr0sea.ic
+++ b/storage/innobase/include/btr0sea.ic
@@ -59,9 +59,6 @@ btr_search_info_update(
dict_index_t* index, /*!< in: index of the cursor */
btr_cur_t* cursor) /*!< in: cursor which was just positioned */
{
- ut_ad(!btr_search_own_any(RW_LOCK_S));
- ut_ad(!btr_search_own_any(RW_LOCK_X));
-
if (dict_index_is_spatial(index) || !btr_search_enabled) {
return;
}
@@ -88,7 +85,7 @@ btr_search_info_update(
static inline void btr_search_x_lock_all()
{
for (ulint i = 0; i < btr_ahi_parts; ++i) {
- rw_lock_x_lock(&btr_search_sys.parts[i].latch);
+ btr_search_sys.parts[i].latch.wr_lock();
}
}
@@ -96,7 +93,7 @@ static inline void btr_search_x_lock_all()
static inline void btr_search_x_unlock_all()
{
for (ulint i = 0; i < btr_ahi_parts; ++i) {
- rw_lock_x_unlock(&btr_search_sys.parts[i].latch);
+ btr_search_sys.parts[i].latch.wr_unlock();
}
}
@@ -104,7 +101,7 @@ static inline void btr_search_x_unlock_all()
static inline void btr_search_s_lock_all()
{
for (ulint i = 0; i < btr_ahi_parts; ++i) {
- rw_lock_s_lock(&btr_search_sys.parts[i].latch);
+ btr_search_sys.parts[i].latch.rd_lock();
}
}
@@ -112,49 +109,7 @@ static inline void btr_search_s_lock_all()
static inline void btr_search_s_unlock_all()
{
for (ulint i = 0; i < btr_ahi_parts; ++i) {
- rw_lock_s_unlock(&btr_search_sys.parts[i].latch);
- }
-}
-
-#ifdef UNIV_DEBUG
-/** Check if thread owns all the search latches.
-@param[in] mode lock mode check
-@retval true if owns all of them
-@retval false if does not own some of them */
-static inline bool btr_search_own_all(ulint mode)
-{
- for (ulint i = 0; i < btr_ahi_parts; ++i) {
- if (!rw_lock_own(&btr_search_sys.parts[i].latch, mode)) {
- return(false);
- }
- }
- return(true);
-}
-
-/** Check if thread owns any of the search latches.
-@param[in] mode lock mode check
-@retval true if owns any of them
-@retval false if owns no search latch */
-static inline bool btr_search_own_any(ulint mode)
-{
- for (ulint i = 0; i < btr_ahi_parts; ++i) {
- if (rw_lock_own(&btr_search_sys.parts[i].latch, mode)) {
- return(true);
- }
- }
- return(false);
-}
-
-/** @return whether this thread holds any of the search latches */
-static inline bool btr_search_own_any()
-{
- for (ulint i = btr_ahi_parts; i--; ) {
- if (rw_lock_own_flagged(&btr_search_sys.parts[i].latch,
- RW_LOCK_FLAG_X | RW_LOCK_FLAG_S)) {
- return true;
- }
+ btr_search_sys.parts[i].latch.rd_unlock();
}
- return false;
}
-#endif /* UNIV_DEBUG */
#endif /* BTR_CUR_HASH_ADAPT */
diff --git a/storage/innobase/include/rw_lock.h b/storage/innobase/include/rw_lock.h
index 9fcafacc426..1388093dc25 100644
--- a/storage/innobase/include/rw_lock.h
+++ b/storage/innobase/include/rw_lock.h
@@ -36,9 +36,19 @@ protected:
/** Flag to indicate that write_lock() or write_lock_wait() is pending */
static constexpr uint32_t WRITER_PENDING= WRITER | WRITER_WAITING;
- /** Start waiting for an exclusive lock. */
- void write_lock_wait_start()
- { lock.fetch_or(WRITER_WAITING, std::memory_order_relaxed); }
+ /** Start waiting for an exclusive lock.
+ @return current value of the lock word */
+ uint32_t write_lock_wait_start()
+ { return lock.fetch_or(WRITER_WAITING, std::memory_order_relaxed); }
+ /** Wait for an exclusive lock.
+ @param l the value of the lock word
+ @return whether the exclusive lock was acquired */
+ bool write_lock_wait_try(uint32_t &l)
+ {
+ l= WRITER_WAITING;
+ return lock.compare_exchange_strong(l, WRITER, std::memory_order_acquire,
+ std::memory_order_relaxed);
+ }
/** Try to acquire a shared lock.
@param l the value of the lock word
@return whether the lock was acquired */
@@ -58,9 +68,8 @@ protected:
@return whether the exclusive lock was acquired */
bool write_lock_poll()
{
- auto l= WRITER_WAITING;
- if (lock.compare_exchange_strong(l, WRITER, std::memory_order_acquire,
- std::memory_order_relaxed))
+ uint32_t l;
+ if (write_lock_wait_try(l))
return true;
if (!(l & WRITER_WAITING))
/* write_lock() must have succeeded for another thread */
@@ -72,12 +81,14 @@ public:
/** Default constructor */
rw_lock() : lock(UNLOCKED) {}
- /** Release a shared lock */
- void read_unlock()
+ /** Release a shared lock.
+ @return whether any writers may have to be woken up */
+ bool read_unlock()
{
- IF_DBUG_ASSERT(auto l=,) lock.fetch_sub(1, std::memory_order_release);
- DBUG_ASSERT(l & ~WRITER_PENDING); /* at least one read lock */
+ auto l= lock.fetch_sub(1, std::memory_order_release);
+ DBUG_ASSERT(~WRITER_PENDING & l); /* at least one read lock */
DBUG_ASSERT(!(l & WRITER)); /* no write lock must have existed */
+ return (~WRITER_PENDING & l) == 1;
}
/** Release an exclusive lock */
void write_unlock()
@@ -106,6 +117,9 @@ public:
auto l= lock.load(std::memory_order_relaxed);
return (l & ~WRITER_PENDING) && !(l & WRITER);
}
+ /** @return whether any lock is being held or waited for by any thread */
+ bool is_locked_or_waiting() const
+ { return lock.load(std::memory_order_relaxed) != 0; }
/** @return whether any lock is being held by any thread */
bool is_locked() const
{ return (lock.load(std::memory_order_relaxed) & ~WRITER_WAITING) != 0; }
diff --git a/storage/innobase/include/srw_lock.h b/storage/innobase/include/srw_lock.h
new file mode 100644
index 00000000000..cf7f9c50a21
--- /dev/null
+++ b/storage/innobase/include/srw_lock.h
@@ -0,0 +1,145 @@
+/*****************************************************************************
+
+Copyright (c) 2020, MariaDB Corporation.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; version 2 of the License.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+*****************************************************************************/
+
+#pragma once
+#include "univ.i"
+
+#if 0 // defined SAFE_MUTEX
+# define SRW_LOCK_DUMMY /* Use mysql_rwlock_t for debugging purposes */
+#endif
+
+#if defined SRW_LOCK_DUMMY || (!defined _WIN32 && !defined __linux__)
+#else
+# ifdef _WIN32
+# include <windows.h>
+# else
+# include "rw_lock.h"
+# endif
+#endif
+
+class srw_lock final
+#if defined __linux__ && !defined SRW_LOCK_DUMMY
+ : protected rw_lock
+#endif
+{
+#if defined SRW_LOCK_DUMMY || (!defined _WIN32 && !defined __linux__)
+ mysql_rwlock_t lock;
+public:
+ void 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 */
+ rw_lock *word() { return static_cast<rw_lock*>(this); }
+ /** Wait for a read lock.
+ @param l lock word from a failed read_trylock() */
+ void read_lock(uint32_t l);
+ /** Wait for a write lock after a failed write_trylock() */
+ void write_lock();
+# endif
+
+public:
+ void init(mysql_pfs_key_t key)
+ {
+# ifdef UNIV_PFS_RWLOCK
+ pfs_psi= PSI_RWLOCK_CALL(init_rwlock)(key, this);
+# endif
+ IF_WIN(lock= SRWLOCK_INIT, static_assert(4 == sizeof(rw_lock), "ABI"));
+ }
+ void destroy()
+ {
+# ifdef UNIV_PFS_RWLOCK
+ if (pfs_psi)
+ {
+ PSI_RWLOCK_CALL(destroy_rwlock)(pfs_psi);
+ pfs_psi= nullptr;
+ }
+# endif
+ DBUG_ASSERT(!is_locked_or_waiting());
+ }
+ void rd_lock()
+ {
+ IF_WIN(, uint32_t l);
+# ifdef UNIV_PFS_RWLOCK
+ if (read_trylock(IF_WIN(, l)))
+ return;
+ if (pfs_psi)
+ {
+ PSI_rwlock_locker_state state;
+ PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_rdwait)
+ (&state, pfs_psi, PSI_RWLOCK_READLOCK, __FILE__, __LINE__);
+ read_lock(IF_WIN(, l));
+ if (locker)
+ PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, 0);
+ return;
+ }
+# endif /* UNIV_PFS_RWLOCK */
+ IF_WIN(read_lock(), if (!read_trylock(l)) read_lock(l));
+ }
+ void wr_lock()
+ {
+# ifdef UNIV_PFS_RWLOCK
+ if (write_trylock())
+ return;
+ if (pfs_psi)
+ {
+ PSI_rwlock_locker_state state;
+ PSI_rwlock_locker *locker= PSI_RWLOCK_CALL(start_rwlock_wrwait)
+ (&state, pfs_psi, PSI_RWLOCK_WRITELOCK, __FILE__, __LINE__);
+ write_lock();
+ if (locker)
+ PSI_RWLOCK_CALL(end_rwlock_rdwait)(locker, 0);
+ return;
+ }
+# endif /* UNIV_PFS_RWLOCK */
+ 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()
+ {
+#ifdef UNIV_PFS_RWLOCK
+ if (pfs_psi) PSI_RWLOCK_CALL(unlock_rwlock)(pfs_psi);
+#endif
+ ReleaseSRWLockExclusive(&lock);
+ }
+#else
+ void rd_unlock();
+ void wr_unlock();
+#endif
+#endif
+};
diff --git a/storage/innobase/include/sync0sync.h b/storage/innobase/include/sync0sync.h
index b7f3cff2925..4ba3ceb437c 100644
--- a/storage/innobase/include/sync0sync.h
+++ b/storage/innobase/include/sync0sync.h
@@ -87,7 +87,6 @@ extern mysql_pfs_key_t read_view_mutex_key;
#ifdef UNIV_PFS_RWLOCK
/* Following are rwlock keys used to register with MySQL
performance schema */
-extern mysql_pfs_key_t btr_search_latch_key;
extern mysql_pfs_key_t dict_operation_lock_key;
extern mysql_pfs_key_t fil_space_latch_key;
extern mysql_pfs_key_t fts_cache_rw_lock_key;
diff --git a/storage/innobase/include/sync0types.h b/storage/innobase/include/sync0types.h
index feb1e3b45ef..b3b67b106ef 100644
--- a/storage/innobase/include/sync0types.h
+++ b/storage/innobase/include/sync0types.h
@@ -191,8 +191,6 @@ enum latch_level_t {
SYNC_POOL,
SYNC_POOL_MANAGER,
- SYNC_SEARCH_SYS,
-
SYNC_WORK_QUEUE,
SYNC_FTS_TOKENIZE,
@@ -294,7 +292,6 @@ enum latch_id_t {
LATCH_ID_ROW_DROP_LIST,
LATCH_ID_INDEX_ONLINE_LOG,
LATCH_ID_WORK_QUEUE,
- LATCH_ID_BTR_SEARCH,
LATCH_ID_BUF_BLOCK_LOCK,
LATCH_ID_BUF_BLOCK_DEBUG,
LATCH_ID_DICT_OPERATION,