diff options
author | Sergey Vojtovich <svoj@mariadb.org> | 2016-11-23 14:07:17 +0400 |
---|---|---|
committer | Sergey Vojtovich <svoj@mariadb.org> | 2016-11-25 12:41:35 +0400 |
commit | fb7caad72b5c37e96c69aad63f9589f8b56855d6 (patch) | |
tree | a7d3764837c0a27a370fdf058b5cbe9c07fe4f7f | |
parent | 68a853732750902fbd97e8ddd1a7f264f9b199c9 (diff) | |
download | mariadb-git-fb7caad72b5c37e96c69aad63f9589f8b56855d6.tar.gz |
MDEV-11296 - InnoDB stalls under OLTP RW on P8
Simplify away recursive flag: it is not necessary for rw-locks to operate
properly. Now writer_thread != 0 means recursive.
As we only need correct value of writer_thread only in writer_thread itself
it is rather safe to load and update it non-atomically.
This patch also fixes potential reorder of "writer_thread" and "recursive"
loads (aka MDEV-7148), which was reopened along with InnoDB thread fences
simplification. Previous versions are unaffected, because they have os_rmb
in rw_lock_lock_word_decr(). It wasn't observed at the moment of writing
though.
-rw-r--r-- | storage/innobase/include/sync0rw.h | 35 | ||||
-rw-r--r-- | storage/innobase/include/sync0rw.ic | 45 | ||||
-rw-r--r-- | storage/innobase/sync/sync0rw.cc | 92 |
3 files changed, 36 insertions, 136 deletions
diff --git a/storage/innobase/include/sync0rw.h b/storage/innobase/include/sync0rw.h index 7b41edb1492..396156e9518 100644 --- a/storage/innobase/include/sync0rw.h +++ b/storage/innobase/include/sync0rw.h @@ -510,22 +510,6 @@ rw_lock_lock_word_decr( rw_lock_t* lock, /*!< in/out: rw-lock */ ulint amount, /*!< in: amount to decrement */ lint threshold); /*!< in: threshold of judgement */ -/******************************************************************//** -This function sets the lock->writer_thread and lock->recursive fields. -For platforms where we are using atomic builtins instead of lock->mutex -it sets the lock->writer_thread field using atomics to ensure memory -ordering. Note that it is assumed that the caller of this function -effectively owns the lock i.e.: nobody else is allowed to modify -lock->writer_thread at this point in time. -The protocol is that lock->writer_thread MUST be updated BEFORE the -lock->recursive flag is set. */ -UNIV_INLINE -void -rw_lock_set_writer_id_and_recursion_flag( -/*=====================================*/ - rw_lock_t* lock, /*!< in/out: lock to work on */ - bool recursive); /*!< in: true if recursion - allowed */ #ifdef UNIV_DEBUG /******************************************************************//** Checks if the thread has locked the rw-lock in the specified mode, with @@ -612,17 +596,6 @@ struct rw_lock_t /** 1: there are waiters */ volatile uint32_t waiters; - /** Default value FALSE which means the lock is non-recursive. - The value is typically set to TRUE making normal rw_locks recursive. - In case of asynchronous IO, when a non-zero value of 'pass' is - passed then we keep the lock non-recursive. - - This flag also tells us about the state of writer_thread field. - If this flag is set then writer_thread MUST contain the thread - id of the current x-holder or wait-x thread. This flag must be - reset in x_unlock functions before incrementing the lock_word */ - volatile bool recursive; - /** number of granted SX locks. */ volatile ulint sx_recursive; @@ -632,8 +605,12 @@ struct rw_lock_t causing much memory bus traffic */ bool writer_is_wait_ex; - /** Thread id of writer thread. Is only guaranteed to have sane - and non-stale value iff recursive flag is set. */ + /** The value is typically set to thread id of a writer thread making + normal rw_locks recursive. In case of asynchronous IO, when a non-zero + value of 'pass' is passed then we keep the lock non-recursive. + + writer_thread must be reset in x_unlock functions before incrementing + the lock_word. */ volatile os_thread_id_t writer_thread; /** Used by sync0arr.cc for thread queueing */ diff --git a/storage/innobase/include/sync0rw.ic b/storage/innobase/include/sync0rw.ic index 355294c18a7..d67e26d961d 100644 --- a/storage/innobase/include/sync0rw.ic +++ b/storage/innobase/include/sync0rw.ic @@ -224,28 +224,6 @@ rw_lock_lock_word_decr( } /******************************************************************//** -This function sets the lock->writer_thread and lock->recursive fields. -For platforms where we are using atomic builtins instead of lock->mutex -it sets the lock->writer_thread field using atomics to ensure memory -ordering. Note that it is assumed that the caller of this function -effectively owns the lock i.e.: nobody else is allowed to modify -lock->writer_thread at this point in time. -The protocol is that lock->writer_thread MUST be updated BEFORE the -lock->recursive flag is set. */ -UNIV_INLINE -void -rw_lock_set_writer_id_and_recursion_flag( -/*=====================================*/ - rw_lock_t* lock, /*!< in/out: lock to work on */ - bool recursive) /*!< in: true if recursion - allowed */ -{ - os_thread_id_t curr_thread = os_thread_get_curr_id(); - my_atomic_storelong(&lock->writer_thread, (long) curr_thread); - lock->recursive = recursive; -} - -/******************************************************************//** Low-level function which tries to lock an rw-lock in s-mode. Performs no spinning. @return TRUE if success */ @@ -334,19 +312,12 @@ rw_lock_x_lock_func_nowait( const char* file_name,/*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { - ibool local_recursive= lock->recursive; lint oldval = X_LOCK_DECR; - /* Note: recursive must be loaded before writer_thread see - comment for rw_lock_set_writer_id_and_recursion_flag(). - To achieve this we load it before my_atomic_caslint(), - which implies full memory barrier in current implementation. */ if (my_atomic_caslint(&lock->lock_word, &oldval, 0)) { - rw_lock_set_writer_id_and_recursion_flag(lock, true); + lock->writer_thread = os_thread_get_curr_id(); - } else if (local_recursive - && os_thread_eq(lock->writer_thread, - os_thread_get_curr_id())) { + } else if (os_thread_eq(lock->writer_thread, os_thread_get_curr_id())) { /* Relock: this lock_word modification is safe since no other threads can modify (lock, unlock, or reserve) lock_word while there is an exclusive writer and this is the writer thread. */ @@ -435,15 +406,9 @@ rw_lock_x_unlock_func( ut_ad(lock->lock_word == 0 || lock->lock_word == -X_LOCK_HALF_DECR || lock->lock_word <= -X_LOCK_DECR); - /* lock->recursive flag also indicates if lock->writer_thread is - valid or stale. If we are the last of the recursive callers - then we must unset lock->recursive flag to indicate that the - lock->writer_thread is now stale. - Note that since we still hold the x-lock we can safely read the - lock_word. */ if (lock->lock_word == 0) { /* Last caller in a possible recursive chain. */ - lock->recursive = FALSE; + lock->writer_thread = 0; } ut_d(rw_lock_remove_debug_info(lock, pass, RW_LOCK_X)); @@ -500,9 +465,7 @@ rw_lock_sx_unlock_func( if (lock->sx_recursive == 0) { /* Last caller in a possible recursive chain. */ if (lock->lock_word > 0) { - lock->recursive = FALSE; - UNIV_MEM_INVALID(&lock->writer_thread, - sizeof lock->writer_thread); + lock->writer_thread = 0; if (my_atomic_addlint(&lock->lock_word, X_LOCK_HALF_DECR) <= 0) { ut_error; diff --git a/storage/innobase/sync/sync0rw.cc b/storage/innobase/sync/sync0rw.cc index e597df02f8f..77a9102112f 100644 --- a/storage/innobase/sync/sync0rw.cc +++ b/storage/innobase/sync/sync0rw.cc @@ -104,31 +104,12 @@ sx-lock holder. The other members of the lock obey the following rules to remain consistent: -recursive: This and the writer_thread field together control the - behaviour of recursive x-locking or sx-locking. - lock->recursive must be FALSE in following states: - 1) The writer_thread contains garbage i.e.: the - lock has just been initialized. - 2) The lock is not x-held and there is no - x-waiter waiting on WAIT_EX event. - 3) The lock is x-held or there is an x-waiter - waiting on WAIT_EX event but the 'pass' value - is non-zero. - lock->recursive is TRUE iff: - 1) The lock is x-held or there is an x-waiter - waiting on WAIT_EX event and the 'pass' value - is zero. - This flag must be set after the writer_thread field - has been updated with a memory ordering barrier. - It is unset before the lock_word has been incremented. -writer_thread: Is used only in recursive x-locking. Can only be safely - read iff lock->recursive flag is TRUE. - This field is uninitialized at lock creation time and - is updated atomically when x-lock is acquired or when - move_ownership is called. A thread is only allowed to - set the value of this field to it's thread_id i.e.: a - thread cannot set writer_thread to some other thread's - id. +writer_thread: Is used only in recursive x-locking or sx-locking. + This field is 0 at lock creation time and is updated + when x-lock is acquired or when move_ownership is called. + A thread is only allowed to set the value of this field to + it's thread_id i.e.: a thread cannot set writer_thread to + some other thread's id. waiters: May be set to 1 anytime, but to avoid unnecessary wake-up signals, it should only be set to 1 when there are threads waiting on event. Must be 1 when a writer starts waiting to @@ -236,14 +217,8 @@ rw_lock_create_func( lock->lock_word = X_LOCK_DECR; lock->waiters = 0; - /* We set this value to signify that lock->writer_thread - contains garbage at initialization and cannot be used for - recursive x-locking. */ - lock->recursive = FALSE; lock->sx_recursive = 0; - /* Silence Valgrind when UNIV_DEBUG_VALGRIND is not enabled. */ - memset((void*) &lock->writer_thread, 0, sizeof lock->writer_thread); - UNIV_MEM_INVALID(&lock->writer_thread, sizeof lock->writer_thread); + lock->writer_thread= 0; #ifdef UNIV_DEBUG lock->m_rw_lock = true; @@ -437,7 +412,7 @@ rw_lock_x_lock_move_ownership( { ut_ad(rw_lock_is_locked(lock, RW_LOCK_X)); - rw_lock_set_writer_id_and_recursion_flag(lock, true); + lock->writer_thread = os_thread_get_curr_id(); } /******************************************************************//** @@ -557,20 +532,17 @@ rw_lock_x_lock_low( const char* file_name,/*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { - ibool local_recursive= lock->recursive; - if (rw_lock_lock_word_decr(lock, X_LOCK_DECR, X_LOCK_HALF_DECR)) { - - /* lock->recursive also tells us if the writer_thread - field is stale or active. As we are going to write - our own thread id in that field it must be that the - current writer_thread value is not active. */ - ut_a(!lock->recursive); + /* As we are going to write our own thread id in that field it + must be that the current writer_thread value is not active. */ + ut_a(!lock->writer_thread); /* Decrement occurred: we are writer or next-writer. */ - rw_lock_set_writer_id_and_recursion_flag( - lock, !pass); + if (!pass) + { + lock->writer_thread = os_thread_get_curr_id(); + } rw_lock_x_lock_wait(lock, pass, 0, file_name, line); @@ -578,13 +550,8 @@ rw_lock_x_lock_low( os_thread_id_t thread_id = os_thread_get_curr_id(); /* Decrement failed: An X or SX lock is held by either - this thread or another. Try to relock. - Note: recursive must be loaded before writer_thread see - comment for rw_lock_set_writer_id_and_recursion_flag(). - To achieve this we load it before rw_lock_lock_word_decr(), - which implies full memory barrier in current implementation. */ - if (!pass && local_recursive - && os_thread_eq(lock->writer_thread, thread_id)) { + this thread or another. Try to relock. */ + if (!pass && os_thread_eq(lock->writer_thread, thread_id)) { /* Other s-locks can be allowed. If it is request x recursively while holding sx lock, this x lock should be along with the latching-order. */ @@ -645,19 +612,17 @@ rw_lock_sx_lock_low( const char* file_name,/*!< in: file name where lock requested */ ulint line) /*!< in: line where requested */ { - ibool local_recursive= lock->recursive; - if (rw_lock_lock_word_decr(lock, X_LOCK_HALF_DECR, X_LOCK_HALF_DECR)) { - /* lock->recursive also tells us if the writer_thread - field is stale or active. As we are going to write - our own thread id in that field it must be that the - current writer_thread value is not active. */ - ut_a(!lock->recursive); + /* As we are going to write our own thread id in that field it + must be that the current writer_thread value is not active. */ + ut_a(!lock->writer_thread); /* Decrement occurred: we are the SX lock owner. */ - rw_lock_set_writer_id_and_recursion_flag( - lock, !pass); + if (!pass) + { + lock->writer_thread = os_thread_get_curr_id(); + } lock->sx_recursive = 1; } else { @@ -665,13 +630,8 @@ rw_lock_sx_lock_low( /* Decrement failed: It already has an X or SX lock by this thread or another thread. If it is this thread, relock, - else fail. - Note: recursive must be loaded before writer_thread see - comment for rw_lock_set_writer_id_and_recursion_flag(). - To achieve this we load it before rw_lock_lock_word_decr(), - which implies full memory barrier in current implementation. */ - if (!pass && local_recursive - && os_thread_eq(lock->writer_thread, thread_id)) { + else fail. */ + if (!pass && os_thread_eq(lock->writer_thread, thread_id)) { /* This thread owns an X or SX lock */ if (lock->sx_recursive++ == 0) { /* This thread is making first SX-lock request |