Commit c561f9e6 authored by Marko Mäkelä's avatar Marko Mäkelä

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.
parent f87e4b4e
...@@ -7,7 +7,6 @@ TRUNCATE TABLE performance_schema.events_waits_current; ...@@ -7,7 +7,6 @@ TRUNCATE TABLE performance_schema.events_waits_current;
select name from performance_schema.setup_instruments select name from performance_schema.setup_instruments
where name like "wait/synch/sxlock/%" order by name; where name like "wait/synch/sxlock/%" order by name;
name name
wait/synch/sxlock/innodb/btr_search_latch
wait/synch/sxlock/innodb/dict_operation_lock wait/synch/sxlock/innodb/dict_operation_lock
wait/synch/sxlock/innodb/fil_space_latch wait/synch/sxlock/innodb/fil_space_latch
wait/synch/sxlock/innodb/fts_cache_init_rw_lock wait/synch/sxlock/innodb/fts_cache_init_rw_lock
...@@ -26,14 +25,6 @@ where name in ...@@ -26,14 +25,6 @@ where name in
) )
order by name; order by name;
name name
wait/synch/sxlock/innodb/btr_search_latch
wait/synch/sxlock/innodb/btr_search_latch
wait/synch/sxlock/innodb/btr_search_latch
wait/synch/sxlock/innodb/btr_search_latch
wait/synch/sxlock/innodb/btr_search_latch
wait/synch/sxlock/innodb/btr_search_latch
wait/synch/sxlock/innodb/btr_search_latch
wait/synch/sxlock/innodb/btr_search_latch
wait/synch/sxlock/innodb/dict_operation_lock wait/synch/sxlock/innodb/dict_operation_lock
wait/synch/sxlock/innodb/trx_i_s_cache_lock wait/synch/sxlock/innodb/trx_i_s_cache_lock
wait/synch/sxlock/innodb/trx_purge_latch wait/synch/sxlock/innodb/trx_purge_latch
......
...@@ -350,6 +350,12 @@ SET(INNOBASE_SOURCES ...@@ -350,6 +350,12 @@ 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
......
...@@ -1245,9 +1245,8 @@ btr_cur_search_to_nth_level_func( ...@@ -1245,9 +1245,8 @@ btr_cur_search_to_nth_level_func(
btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is
s- or x-latched, but see also above! */ s- or x-latched, but see also above! */
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
rw_lock_t* ahi_latch, srw_lock* ahi_latch,
/*!< in: currently held btr_search_latch /*!< in: currently held AHI rdlock, or NULL */
(in RW_S_LATCH mode), or NULL */
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
const char* file, /*!< in: file name */ const char* file, /*!< in: file name */
unsigned line, /*!< in: line where called */ unsigned line, /*!< in: line where called */
...@@ -1449,7 +1448,7 @@ btr_cur_search_to_nth_level_func( ...@@ -1449,7 +1448,7 @@ btr_cur_search_to_nth_level_func(
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
if (ahi_latch) { if (ahi_latch) {
/* Release possible search latch to obey latching order */ /* Release possible search latch to obey latching order */
rw_lock_s_unlock(ahi_latch); ahi_latch->rd_unlock();
} }
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
...@@ -2502,7 +2501,7 @@ btr_cur_search_to_nth_level_func( ...@@ -2502,7 +2501,7 @@ btr_cur_search_to_nth_level_func(
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
if (ahi_latch) { if (ahi_latch) {
rw_lock_s_lock(ahi_latch); ahi_latch->rd_lock();
} }
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
...@@ -3584,7 +3583,7 @@ btr_cur_optimistic_insert( ...@@ -3584,7 +3583,7 @@ btr_cur_optimistic_insert(
ut_ad(index->is_instant()); ut_ad(index->is_instant());
ut_ad(flags == BTR_NO_LOCKING_FLAG); ut_ad(flags == BTR_NO_LOCKING_FLAG);
} else { } else {
rw_lock_t* ahi_latch = btr_search_sys.get_latch(*index); srw_lock* ahi_latch = btr_search_sys.get_latch(*index);
if (!reorg && cursor->flag == BTR_CUR_HASH) { if (!reorg && cursor->flag == BTR_CUR_HASH) {
btr_search_update_hash_node_on_insert( btr_search_update_hash_node_on_insert(
cursor, ahi_latch); cursor, ahi_latch);
...@@ -4296,7 +4295,7 @@ btr_cur_update_in_place( ...@@ -4296,7 +4295,7 @@ btr_cur_update_in_place(
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
{ {
rw_lock_t* ahi_latch = block->index srw_lock* ahi_latch = block->index
? btr_search_sys.get_latch(*index) : NULL; ? btr_search_sys.get_latch(*index) : NULL;
if (ahi_latch) { if (ahi_latch) {
/* TO DO: Can we skip this if none of the fields /* TO DO: Can we skip this if none of the fields
...@@ -4316,7 +4315,7 @@ btr_cur_update_in_place( ...@@ -4316,7 +4315,7 @@ btr_cur_update_in_place(
btr_search_update_hash_on_delete(cursor); btr_search_update_hash_on_delete(cursor);
} }
rw_lock_x_lock(ahi_latch); ahi_latch->wr_lock();
} }
assert_block_ahi_valid(block); assert_block_ahi_valid(block);
...@@ -4327,7 +4326,7 @@ btr_cur_update_in_place( ...@@ -4327,7 +4326,7 @@ btr_cur_update_in_place(
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
if (ahi_latch) { if (ahi_latch) {
rw_lock_x_unlock(ahi_latch); ahi_latch->wr_unlock();
} }
} }
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
......
This diff is collapsed.
...@@ -1980,16 +1980,11 @@ inline void buf_pool_t::resize() ...@@ -1980,16 +1980,11 @@ inline void buf_pool_t::resize()
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
/* disable AHI if needed */ /* disable AHI if needed */
const bool btr_search_disabled = btr_search_enabled;
buf_resize_status("Disabling adaptive hash index."); buf_resize_status("Disabling adaptive hash index.");
btr_search_s_lock_all(); btr_search_s_lock_all();
if (btr_search_disabled) { const bool btr_search_disabled = btr_search_enabled;
btr_search_s_unlock_all(); btr_search_s_unlock_all();
} else {
btr_search_s_unlock_all();
}
btr_search_disable(); btr_search_disable();
......
...@@ -561,7 +561,9 @@ static PSI_mutex_info all_innodb_mutexes[] = { ...@@ -561,7 +561,9 @@ static PSI_mutex_info all_innodb_mutexes[] = {
performance schema instrumented if "UNIV_PFS_RWLOCK" performance schema instrumented if "UNIV_PFS_RWLOCK"
is defined */ is defined */
static PSI_rwlock_info all_innodb_rwlocks[] = { static PSI_rwlock_info all_innodb_rwlocks[] = {
PSI_RWLOCK_KEY(btr_search_latch), # ifdef BTR_CUR_HASH_ADAPT
{ &btr_search_latch_key, "btr_search_latch", 0 },
# endif
PSI_RWLOCK_KEY(dict_operation_lock), PSI_RWLOCK_KEY(dict_operation_lock),
PSI_RWLOCK_KEY(fil_space_latch), PSI_RWLOCK_KEY(fil_space_latch),
PSI_RWLOCK_KEY(fts_cache_rw_lock), PSI_RWLOCK_KEY(fts_cache_rw_lock),
......
...@@ -33,6 +33,9 @@ Created 10/16/1994 Heikki Tuuri ...@@ -33,6 +33,9 @@ Created 10/16/1994 Heikki Tuuri
#include "rem0types.h" #include "rem0types.h"
#include "gis0type.h" #include "gis0type.h"
#include "my_base.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 */ /** Mode flags for btr_cur operations; these can be ORed */
enum { enum {
...@@ -202,9 +205,8 @@ btr_cur_search_to_nth_level_func( ...@@ -202,9 +205,8 @@ btr_cur_search_to_nth_level_func(
btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is btr_cur_t* cursor, /*!< in/out: tree cursor; the cursor page is
s- or x-latched, but see also above! */ s- or x-latched, but see also above! */
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
rw_lock_t* ahi_latch, srw_lock* ahi_latch,
/*!< in: currently held btr_search_latch /*!< in: currently held AHI rdlock, or NULL */
(in RW_S_LATCH mode), or NULL */
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
const char* file, /*!< in: file name */ const char* file, /*!< in: file name */
unsigned line, /*!< in: line where called */ unsigned line, /*!< in: line where called */
......
...@@ -137,9 +137,8 @@ btr_pcur_open_with_no_init_func( ...@@ -137,9 +137,8 @@ btr_pcur_open_with_no_init_func(
that the ahi_latch protects the record! */ that the ahi_latch protects the record! */
btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
rw_lock_t* ahi_latch, srw_lock* ahi_latch,
/*!< in: adaptive hash index latch held /*!< in: currently held AHI rdlock, or NULL */
by the caller, or NULL if none */
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
const char* file, /*!< in: file name */ const char* file, /*!< in: file name */
unsigned line, /*!< in: line where called */ unsigned line, /*!< in: line where called */
......
...@@ -483,9 +483,8 @@ btr_pcur_open_with_no_init_func( ...@@ -483,9 +483,8 @@ btr_pcur_open_with_no_init_func(
that the ahi_latch protects the record! */ that the ahi_latch protects the record! */
btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */ btr_pcur_t* cursor, /*!< in: memory buffer for persistent cursor */
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
rw_lock_t* ahi_latch, srw_lock* ahi_latch,
/*!< in: adaptive hash index latch held /*!< in: currently held AHI rdlock, or NULL */
by the caller, or NULL if none */
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
const char* file, /*!< in: file name */ const char* file, /*!< in: file name */
unsigned line, /*!< in: line where called */ unsigned line, /*!< in: line where called */
......
...@@ -30,7 +30,11 @@ Created 2/17/1996 Heikki Tuuri ...@@ -30,7 +30,11 @@ Created 2/17/1996 Heikki Tuuri
#include "dict0dict.h" #include "dict0dict.h"
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
#include "ha0ha.h" #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_create() btr_search_sys.create()
#define btr_search_sys_free() btr_search_sys.free() #define btr_search_sys_free() btr_search_sys.free()
...@@ -77,7 +81,7 @@ btr_search_guess_on_hash( ...@@ -77,7 +81,7 @@ btr_search_guess_on_hash(
ulint mode, ulint mode,
ulint latch_mode, ulint latch_mode,
btr_cur_t* cursor, btr_cur_t* cursor,
rw_lock_t* ahi_latch, srw_lock* ahi_latch,
mtr_t* mtr); mtr_t* mtr);
/** Move or delete hash entries for moved records, usually in a page split. /** 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); ...@@ -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 using btr_cur_search_, and the new record has been
inserted next to the cursor. inserted next to the cursor.
@param[in] ahi_latch the adaptive hash index latch */ @param[in] ahi_latch the adaptive hash index latch */
void void btr_search_update_hash_node_on_insert(btr_cur_t *cursor,
btr_search_update_hash_node_on_insert(btr_cur_t* cursor, rw_lock_t* ahi_latch); srw_lock *ahi_latch);
/** Updates the page hash index when a single record is inserted on a page. /** Updates the page hash index when a single record is inserted on a page.
@param[in,out] cursor cursor which was positioned to the @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); ...@@ -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 and the new record has been inserted next
to the cursor to the cursor
@param[in] ahi_latch the adaptive hash index latch */ @param[in] ahi_latch the adaptive hash index latch */
void void btr_search_update_hash_on_insert(btr_cur_t *cursor,
btr_search_update_hash_on_insert(btr_cur_t* cursor, rw_lock_t* ahi_latch); srw_lock *ahi_latch);
/** Updates the page hash index when a single record is deleted from a page. /** 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 @param[in] cursor cursor which was positioned on the record to delete
...@@ -139,23 +143,6 @@ static inline void btr_search_x_unlock_all(); ...@@ -139,23 +143,6 @@ static inline void btr_search_x_unlock_all();
/** Lock all search latches in shared mode. */ /** Lock all search latches in shared mode. */
static inline void btr_search_s_lock_all(); 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. */ /** Unlock all search latches from shared mode. */
static inline void btr_search_s_unlock_all(); static inline void btr_search_s_unlock_all();
...@@ -250,20 +237,20 @@ struct btr_search_sys_t ...@@ -250,20 +237,20 @@ struct btr_search_sys_t
struct partition struct partition
{ {
/** latches protecting hash_table */ /** latches protecting hash_table */
rw_lock_t latch; srw_lock latch;
/** mapping of dtuple_fold() to rec_t* in buf_block_t::frame */ /** mapping of dtuple_fold() to rec_t* in buf_block_t::frame */
hash_table_t table; hash_table_t table;
/** memory heap for table */ /** memory heap for table */
mem_heap_t *heap; 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)) & sizeof(hash_table_t) - sizeof(mem_heap_t)) &
(CPU_LEVEL1_DCACHE_LINESIZE - 1)]; (CPU_LEVEL1_DCACHE_LINESIZE - 1)];
void init() void init()
{ {
memset((void*) this, 0, sizeof *this); 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) void alloc(ulint hash_size)
...@@ -285,7 +272,7 @@ struct btr_search_sys_t ...@@ -285,7 +272,7 @@ struct btr_search_sys_t
void free() void free()
{ {
rw_lock_free(&latch); latch.destroy();
if (heap) if (heap)
clear(); clear();
} }
...@@ -309,7 +296,7 @@ struct btr_search_sys_t ...@@ -309,7 +296,7 @@ struct btr_search_sys_t
} }
/** Get the search latch for the adaptive hash index partition */ /** 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; } { return &get_part(index)->latch; }
/** Create and initialize at startup */ /** Create and initialize at startup */
...@@ -354,10 +341,10 @@ inline ulint dict_index_t::n_ahi_pages() const ...@@ -354,10 +341,10 @@ inline ulint dict_index_t::n_ahi_pages() const
{ {
if (!btr_search_enabled) if (!btr_search_enabled)
return 0; return 0;
rw_lock_t *latch = &btr_search_sys.get_part(*this)->latch; srw_lock *latch= &btr_search_sys.get_part(*this)->latch;
rw_lock_s_lock(latch); latch->rd_lock();
ulint ref_count= search_info->ref_count; ulint ref_count= search_info->ref_count;
rw_lock_s_unlock(latch); latch->rd_unlock();
return ref_count; return ref_count;
} }
......
...@@ -59,9 +59,6 @@ btr_search_info_update( ...@@ -59,9 +59,6 @@ btr_search_info_update(
dict_index_t* index, /*!< in: index of the cursor */ dict_index_t* index, /*!< in: index of the cursor */
btr_cur_t* cursor) /*!< in: cursor which was just positioned */ 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) { if (dict_index_is_spatial(index) || !btr_search_enabled) {
return; return;
} }
...@@ -88,7 +85,7 @@ btr_search_info_update( ...@@ -88,7 +85,7 @@ btr_search_info_update(
static inline void btr_search_x_lock_all() static inline void btr_search_x_lock_all()
{ {
for (ulint i = 0; i < btr_ahi_parts; ++i) { 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() ...@@ -96,7 +93,7 @@ static inline void btr_search_x_lock_all()
static inline void btr_search_x_unlock_all() static inline void btr_search_x_unlock_all()
{ {
for (ulint i = 0; i < btr_ahi_parts; ++i) { 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() ...@@ -104,7 +101,7 @@ static inline void btr_search_x_unlock_all()
static inline void btr_search_s_lock_all() static inline void btr_search_s_lock_all()
{ {
for (ulint i = 0; i < btr_ahi_parts; ++i) { 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() ...@@ -112,49 +109,7 @@ static inline void btr_search_s_lock_all()
static inline void btr_search_s_unlock_all() static inline void btr_search_s_unlock_all()
{ {
for (ulint i = 0; i < btr_ahi_parts; ++i) { for (ulint i = 0; i < btr_ahi_parts; ++i) {
rw_lock_s_unlock(&btr_search_sys.parts[i].latch); btr_search_sys.parts[i].latch.rd_unlock();
}
}
#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;
}
} }
return false;
} }
#endif /* UNIV_DEBUG */
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
...@@ -36,9 +36,19 @@ class rw_lock ...@@ -36,9 +36,19 @@ class rw_lock
/** Flag to indicate that write_lock() or write_lock_wait() is pending */ /** Flag to indicate that write_lock() or write_lock_wait() is pending */
static constexpr uint32_t WRITER_PENDING= WRITER | WRITER_WAITING; static constexpr uint32_t WRITER_PENDING= WRITER | WRITER_WAITING;
/** Start waiting for an exclusive lock. */ /** Start waiting for an exclusive lock.
void write_lock_wait_start() @return current value of the lock word */
{ lock.fetch_or(WRITER_WAITING, std::memory_order_relaxed); } 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. /** Try to acquire a shared lock.
@param l the value of the lock word @param l the value of the lock word
@return whether the lock was acquired */ @return whether the lock was acquired */
...@@ -58,9 +68,8 @@ class rw_lock ...@@ -58,9 +68,8 @@ class rw_lock
@return whether the exclusive lock was acquired */ @return whether the exclusive lock was acquired */
bool write_lock_poll() bool write_lock_poll()
{ {
auto l= WRITER_WAITING; uint32_t l;
if (lock.compare_exchange_strong(l, WRITER, std::memory_order_acquire, if (write_lock_wait_try(l))
std::memory_order_relaxed))
return true; return true;
if (!(l & WRITER_WAITING)) if (!(l & WRITER_WAITING))
/* write_lock() must have succeeded for another thread */ /* write_lock() must have succeeded for another thread */
...@@ -72,12 +81,14 @@ class rw_lock ...@@ -72,12 +81,14 @@ class rw_lock
/** Default constructor */ /** Default constructor */
rw_lock() : lock(UNLOCKED) {} rw_lock() : lock(UNLOCKED) {}
/** Release a shared lock */ /** Release a shared lock.
void read_unlock() @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); auto l= lock.fetch_sub(1, std::memory_order_release);
DBUG_ASSERT(l & ~WRITER_PENDING); /* at least one read lock */ DBUG_ASSERT(~WRITER_PENDING & l); /* at least one read lock */
DBUG_ASSERT(!(l & WRITER)); /* no write lock must have existed */ DBUG_ASSERT(!(l & WRITER)); /* no write lock must have existed */
return (~WRITER_PENDING & l) == 1;
} }
/** Release an exclusive lock */ /** Release an exclusive lock */
void write_unlock() void write_unlock()
...@@ -106,6 +117,9 @@ class rw_lock ...@@ -106,6 +117,9 @@ class rw_lock
auto l= lock.load(std::memory_order_relaxed); auto l= lock.load(std::memory_order_relaxed);
return (l & ~WRITER_PENDING) && !(l & WRITER); 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 */ /** @return whether any lock is being held by any thread */
bool is_locked() const bool is_locked() const
{ return (lock.load(std::memory_order_relaxed) & ~WRITER_WAITING) != 0; } { return (lock.load(std::memory_order_relaxed) & ~WRITER_WAITING) != 0; }
......
/*****************************************************************************
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
};
...@@ -87,7 +87,6 @@ extern mysql_pfs_key_t read_view_mutex_key; ...@@ -87,7 +87,6 @@ extern mysql_pfs_key_t read_view_mutex_key;
#ifdef UNIV_PFS_RWLOCK #ifdef UNIV_PFS_RWLOCK
/* Following are rwlock keys used to register with MySQL /* Following are rwlock keys used to register with MySQL
performance schema */ 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 dict_operation_lock_key;
extern mysql_pfs_key_t fil_space_latch_key; extern mysql_pfs_key_t fil_space_latch_key;
extern mysql_pfs_key_t fts_cache_rw_lock_key; extern mysql_pfs_key_t fts_cache_rw_lock_key;
......
...@@ -191,8 +191,6 @@ enum latch_level_t { ...@@ -191,8 +191,6 @@ enum latch_level_t {
SYNC_POOL, SYNC_POOL,
SYNC_POOL_MANAGER, SYNC_POOL_MANAGER,
SYNC_SEARCH_SYS,
SYNC_WORK_QUEUE, SYNC_WORK_QUEUE,
SYNC_FTS_TOKENIZE, SYNC_FTS_TOKENIZE,
...@@ -294,7 +292,6 @@ enum latch_id_t { ...@@ -294,7 +292,6 @@ enum latch_id_t {
LATCH_ID_ROW_DROP_LIST, LATCH_ID_ROW_DROP_LIST,
LATCH_ID_INDEX_ONLINE_LOG, LATCH_ID_INDEX_ONLINE_LOG,
LATCH_ID_WORK_QUEUE, LATCH_ID_WORK_QUEUE,
LATCH_ID_BTR_SEARCH,
LATCH_ID_BUF_BLOCK_LOCK, LATCH_ID_BUF_BLOCK_LOCK,
LATCH_ID_BUF_BLOCK_DEBUG, LATCH_ID_BUF_BLOCK_DEBUG,
LATCH_ID_DICT_OPERATION, LATCH_ID_DICT_OPERATION,
......
...@@ -3857,15 +3857,15 @@ row_sel_try_search_shortcut_for_mysql( ...@@ -3857,15 +3857,15 @@ row_sel_try_search_shortcut_for_mysql(
ut_ad(dict_index_is_clust(index)); ut_ad(dict_index_is_clust(index));
ut_ad(!prebuilt->templ_contains_blob); ut_ad(!prebuilt->templ_contains_blob);
rw_lock_t* ahi_latch = btr_search_sys.get_latch(*index); srw_lock* ahi_latch = btr_search_sys.get_latch(*index);
rw_lock_s_lock(ahi_latch); ahi_latch->rd_lock();
btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE, btr_pcur_open_with_no_init(index, search_tuple, PAGE_CUR_GE,
BTR_SEARCH_LEAF, pcur, ahi_latch, mtr); BTR_SEARCH_LEAF, pcur, ahi_latch, mtr);
rec = btr_pcur_get_rec(pcur); rec = btr_pcur_get_rec(pcur);
if (!page_rec_is_user_rec(rec) || rec_is_metadata(rec, *index)) { if (!page_rec_is_user_rec(rec) || rec_is_metadata(rec, *index)) {
retry: retry:
rw_lock_s_unlock(ahi_latch); ahi_latch->rd_unlock();
return(SEL_RETRY); return(SEL_RETRY);
} }
...@@ -3875,7 +3875,7 @@ row_sel_try_search_shortcut_for_mysql( ...@@ -3875,7 +3875,7 @@ row_sel_try_search_shortcut_for_mysql(
if (btr_pcur_get_up_match(pcur) < dtuple_get_n_fields(search_tuple)) { if (btr_pcur_get_up_match(pcur) < dtuple_get_n_fields(search_tuple)) {
exhausted: exhausted:
rw_lock_s_unlock(ahi_latch); ahi_latch->rd_unlock();
return(SEL_EXHAUSTED); return(SEL_EXHAUSTED);
} }
...@@ -3899,7 +3899,7 @@ row_sel_try_search_shortcut_for_mysql( ...@@ -3899,7 +3899,7 @@ row_sel_try_search_shortcut_for_mysql(
*out_rec = rec; *out_rec = rec;
rw_lock_s_unlock(ahi_latch); ahi_latch->rd_unlock();
return(SEL_FOUND); return(SEL_FOUND);
} }
#endif /* BTR_CUR_HASH_ADAPT */ #endif /* BTR_CUR_HASH_ADAPT */
......
...@@ -924,13 +924,13 @@ srv_printf_innodb_monitor( ...@@ -924,13 +924,13 @@ srv_printf_innodb_monitor(
#ifdef BTR_CUR_HASH_ADAPT #ifdef BTR_CUR_HASH_ADAPT
for (ulint i = 0; i < btr_ahi_parts && btr_search_enabled; ++i) { for (ulint i = 0; i < btr_ahi_parts && btr_search_enabled; ++i) {
const auto part= &btr_search_sys.parts[i]; const auto part= &btr_search_sys.parts[i];
rw_lock_s_lock(&part->latch); part->latch.rd_lock();
ut_ad(part->heap->type == MEM_HEAP_FOR_BTR_SEARCH); ut_ad(part->heap->type == MEM_HEAP_FOR_BTR_SEARCH);
fprintf(file, "Hash table size " ULINTPF fprintf(file, "Hash table size " ULINTPF
", node heap has " ULINTPF " buffer(s)\n", ", node heap has " ULINTPF " buffer(s)\n",
part->table.n_cells, part->table.n_cells,
part->heap->base.count - !part->heap->free_block); part->heap->base.count - !part->heap->free_block);
rw_lock_s_unlock(&part->latch); part->latch.rd_unlock();
} }
fprintf(file, fprintf(file,
...@@ -1065,14 +1065,14 @@ srv_export_innodb_status(void) ...@@ -1065,14 +1065,14 @@ srv_export_innodb_status(void)
ulint mem_adaptive_hash = 0; ulint mem_adaptive_hash = 0;
for (ulong i = 0; i < btr_ahi_parts; i++) { for (ulong i = 0; i < btr_ahi_parts; i++) {
const auto part= &btr_search_sys.parts[i]; const auto part= &btr_search_sys.parts[i];
rw_lock_s_lock(&part->latch); part->latch.rd_lock();
if (part->heap) { if (part->heap) {
ut_ad(part->heap->type == MEM_HEAP_FOR_BTR_SEARCH); ut_ad(part->heap->type == MEM_HEAP_FOR_BTR_SEARCH);
mem_adaptive_hash += mem_heap_get_size(part->heap) mem_adaptive_hash += mem_heap_get_size(part->heap)
+ part->table.n_cells * sizeof(hash_cell_t); + part->table.n_cells * sizeof(hash_cell_t);
} }
rw_lock_s_unlock(&part->latch); part->latch.rd_unlock();
} }
export_vars.innodb_mem_adaptive_hash = mem_adaptive_hash; export_vars.innodb_mem_adaptive_hash = mem_adaptive_hash;
#endif #endif
......
/*****************************************************************************
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
*****************************************************************************/
#ifndef __linux__
# error "This file is for Linux only"
#endif
#include "srw_lock.h"
#ifdef SRW_LOCK_DUMMY
/* Work around a potential build failure by preventing an empty .o file */
int srw_lock_dummy_function() { return 0; }
#else
# include <linux/futex.h>
# include <sys/syscall.h>
# include "srv0srv.h"
/** Wait for a read lock.
@param lock word value from a failed read_trylock() */
void srw_lock::read_lock(uint32_t l)
{
do
{
if (l == WRITER_WAITING)
wake_writer:
syscall(SYS_futex, word(), FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
else
for (auto spin= srv_n_spin_wait_rounds; spin; spin--)
{
ut_delay(srv_spin_wait_delay);
if (read_trylock(l))
return;
else if (l == WRITER_WAITING)
goto wake_writer;
}
syscall(SYS_futex, word(), FUTEX_WAIT_PRIVATE, l, nullptr, nullptr, 0);
}
while (!read_trylock(l));
}
/** Wait for a write lock after a failed write_trylock() */
void srw_lock::write_lock()
{
for (;;)
{
uint32_t l= write_lock_wait_start();
/* We are the first writer to be granted the lock. Spin for a while. */
for (auto spin= srv_n_spin_wait_rounds; spin; spin--)
{
if (write_lock_wait_try(l))
return;
if (!(l & WRITER_WAITING))
l= write_lock_wait_start();
ut_delay(srv_spin_wait_delay);
}
if (write_lock_wait_try(l))
return;
if (!(l & WRITER_WAITING))
{
if (l == UNLOCKED && write_trylock())
return;
l= write_lock_wait_start() | WRITER_WAITING;
}
else
DBUG_ASSERT(~WRITER_WAITING & l);
syscall(SYS_futex, word(), FUTEX_WAIT_PRIVATE, l, nullptr, nullptr, 0);
}
}
void srw_lock::rd_unlock()
{
#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()
{
#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
...@@ -455,7 +455,6 @@ LatchDebug::LatchDebug() ...@@ -455,7 +455,6 @@ LatchDebug::LatchDebug()
LEVEL_MAP_INSERT(SYNC_ANY_LATCH); LEVEL_MAP_INSERT(SYNC_ANY_LATCH);
LEVEL_MAP_INSERT(SYNC_POOL); LEVEL_MAP_INSERT(SYNC_POOL);
LEVEL_MAP_INSERT(SYNC_POOL_MANAGER); LEVEL_MAP_INSERT(SYNC_POOL_MANAGER);
LEVEL_MAP_INSERT(SYNC_SEARCH_SYS);
LEVEL_MAP_INSERT(SYNC_WORK_QUEUE); LEVEL_MAP_INSERT(SYNC_WORK_QUEUE);
LEVEL_MAP_INSERT(SYNC_FTS_TOKENIZE); LEVEL_MAP_INSERT(SYNC_FTS_TOKENIZE);
LEVEL_MAP_INSERT(SYNC_FTS_OPTIMIZE); LEVEL_MAP_INSERT(SYNC_FTS_OPTIMIZE);
...@@ -732,7 +731,6 @@ LatchDebug::check_order( ...@@ -732,7 +731,6 @@ LatchDebug::check_order(
case SYNC_FTS_OPTIMIZE: case SYNC_FTS_OPTIMIZE:
case SYNC_FTS_CACHE: case SYNC_FTS_CACHE:
case SYNC_FTS_CACHE_INIT: case SYNC_FTS_CACHE_INIT:
case SYNC_SEARCH_SYS:
case SYNC_LOCK_SYS: case SYNC_LOCK_SYS:
case SYNC_LOCK_WAIT_SYS: case SYNC_LOCK_WAIT_SYS:
case SYNC_RW_TRX_HASH_ELEMENT: case SYNC_RW_TRX_HASH_ELEMENT:
...@@ -1304,8 +1302,6 @@ sync_latch_meta_init() ...@@ -1304,8 +1302,6 @@ sync_latch_meta_init()
LATCH_ADD_MUTEX(WORK_QUEUE, SYNC_WORK_QUEUE, PFS_NOT_INSTRUMENTED); LATCH_ADD_MUTEX(WORK_QUEUE, SYNC_WORK_QUEUE, PFS_NOT_INSTRUMENTED);
// Add the RW locks // Add the RW locks
LATCH_ADD_RWLOCK(BTR_SEARCH, SYNC_SEARCH_SYS, btr_search_latch_key);
LATCH_ADD_RWLOCK(BUF_BLOCK_LOCK, SYNC_LEVEL_VARYING, LATCH_ADD_RWLOCK(BUF_BLOCK_LOCK, SYNC_LEVEL_VARYING,
PFS_NOT_INSTRUMENTED); PFS_NOT_INSTRUMENTED);
......
...@@ -81,7 +81,6 @@ mysql_pfs_key_t rw_trx_hash_element_mutex_key; ...@@ -81,7 +81,6 @@ mysql_pfs_key_t rw_trx_hash_element_mutex_key;
mysql_pfs_key_t read_view_mutex_key; mysql_pfs_key_t read_view_mutex_key;
#endif /* UNIV_PFS_MUTEX */ #endif /* UNIV_PFS_MUTEX */
#ifdef UNIV_PFS_RWLOCK #ifdef UNIV_PFS_RWLOCK
mysql_pfs_key_t btr_search_latch_key;
mysql_pfs_key_t dict_operation_lock_key; mysql_pfs_key_t dict_operation_lock_key;
mysql_pfs_key_t index_tree_rw_lock_key; mysql_pfs_key_t index_tree_rw_lock_key;
mysql_pfs_key_t index_online_log_key; mysql_pfs_key_t index_online_log_key;
......
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