diff options
author | Marko Mäkelä <marko.makela@mariadb.com> | 2020-11-24 15:41:03 +0200 |
---|---|---|
committer | Marko Mäkelä <marko.makela@mariadb.com> | 2020-11-24 15:41:03 +0200 |
commit | c561f9e6e857dbae905c3d01db9ffa5b2ae2e6db (patch) | |
tree | 508626dbb5380f3b1a31191e17bc710cd392ce7b /storage/innobase/include | |
parent | f87e4b4e4d2ef25a8f98b4ad8ce1ce2407262940 (diff) | |
download | mariadb-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.h | 8 | ||||
-rw-r--r-- | storage/innobase/include/btr0pcur.h | 5 | ||||
-rw-r--r-- | storage/innobase/include/btr0pcur.ic | 5 | ||||
-rw-r--r-- | storage/innobase/include/btr0sea.h | 49 | ||||
-rw-r--r-- | storage/innobase/include/btr0sea.ic | 53 | ||||
-rw-r--r-- | storage/innobase/include/rw_lock.h | 34 | ||||
-rw-r--r-- | storage/innobase/include/srw_lock.h | 145 | ||||
-rw-r--r-- | storage/innobase/include/sync0sync.h | 1 | ||||
-rw-r--r-- | storage/innobase/include/sync0types.h | 3 |
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, |