diff options
Diffstat (limited to 'storage/innobase/include/sync0sync.ic')
-rw-r--r-- | storage/innobase/include/sync0sync.ic | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/storage/innobase/include/sync0sync.ic b/storage/innobase/include/sync0sync.ic new file mode 100644 index 00000000000..616e53d4aac --- /dev/null +++ b/storage/innobase/include/sync0sync.ic @@ -0,0 +1,414 @@ +/***************************************************************************** + +Copyright (c) 1995, 2009, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2008, Google Inc. + +Portions of this file contain modifications contributed and copyrighted by +Google, Inc. Those modifications are gratefully acknowledged and are described +briefly in the InnoDB documentation. The contributions by Google are +incorporated with their permission, and subject to the conditions contained in +the file COPYING.Google. + +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, Suite 500, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/**************************************************//** +@file include/sync0sync.ic +Mutex, the basic synchronization primitive + +Created 9/5/1995 Heikki Tuuri +*******************************************************/ + +/******************************************************************//** +Sets the waiters field in a mutex. */ +UNIV_INTERN +void +mutex_set_waiters( +/*==============*/ + ib_mutex_t* mutex, /*!< in: mutex */ + ulint n); /*!< in: value to set */ +/******************************************************************//** +Reserves a mutex for the current thread. If the mutex is reserved, the +function spins a preset time (controlled by SYNC_SPIN_ROUNDS) waiting +for the mutex before suspending the thread. */ +UNIV_INTERN +void +mutex_spin_wait( +/*============*/ + ib_mutex_t* mutex, /*!< in: pointer to mutex */ + const char* file_name, /*!< in: file name where mutex + requested */ + ulint line); /*!< in: line where requested */ +#ifdef UNIV_SYNC_DEBUG +/******************************************************************//** +Sets the debug information for a reserved mutex. */ +UNIV_INTERN +void +mutex_set_debug_info( +/*=================*/ + ib_mutex_t* mutex, /*!< in: mutex */ + const char* file_name, /*!< in: file where requested */ + ulint line); /*!< in: line where requested */ +#endif /* UNIV_SYNC_DEBUG */ +/******************************************************************//** +Releases the threads waiting in the primary wait array for this mutex. */ +UNIV_INTERN +void +mutex_signal_object( +/*================*/ + ib_mutex_t* mutex); /*!< in: mutex */ + +/******************************************************************//** +Performs an atomic test-and-set instruction to the lock_word field of a +mutex. +@return the previous value of lock_word: 0 or 1 */ +UNIV_INLINE +byte +ib_mutex_test_and_set( +/*===============*/ + ib_mutex_t* mutex) /*!< in: mutex */ +{ +#if defined(HAVE_ATOMIC_BUILTINS) +# if defined(HAVE_ATOMIC_BUILTINS_BYTE) + return(os_atomic_test_and_set_byte(&mutex->lock_word, 1)); +# else + return(os_atomic_test_and_set_ulint(&mutex->lock_word, 1)); +# endif +#else + ibool ret; + + ret = os_fast_mutex_trylock(&(mutex->os_fast_mutex)); + + if (ret == 0) { + /* We check that os_fast_mutex_trylock does not leak + and allow race conditions */ + ut_a(mutex->lock_word == 0); + + mutex->lock_word = 1; + os_wmb; + } + + return((byte) ret); +#endif +} + +/******************************************************************//** +Performs a reset instruction to the lock_word field of a mutex. This +instruction also serializes memory operations to the program order. */ +UNIV_INLINE +void +mutex_reset_lock_word( +/*==================*/ + ib_mutex_t* mutex) /*!< in: mutex */ +{ +#if defined(HAVE_ATOMIC_BUILTINS) + /* In theory __sync_lock_release should be used to release the lock. + Unfortunately, it does not work properly alone. The workaround is + that more conservative __sync_lock_test_and_set is used instead. */ +# if defined(HAVE_ATOMIC_BUILTINS_BYTE) + os_atomic_test_and_set_byte(&mutex->lock_word, 0); +# else + os_atomic_test_and_set_ulint(&mutex->lock_word, 0); +# endif +#else + mutex->lock_word = 0; + + os_fast_mutex_unlock(&(mutex->os_fast_mutex)); +#endif +} + +/******************************************************************//** +Gets the value of the lock word. */ +UNIV_INLINE +lock_word_t +mutex_get_lock_word( +/*================*/ + const ib_mutex_t* mutex) /*!< in: mutex */ +{ + ut_ad(mutex); + + return(mutex->lock_word); +} + +/******************************************************************//** +Gets the waiters field in a mutex. +@return value to set */ +UNIV_INLINE +ulint +mutex_get_waiters( +/*==============*/ + const ib_mutex_t* mutex) /*!< in: mutex */ +{ + const volatile ulint* ptr; /*!< declared volatile to ensure that + the value is read from memory */ + ut_ad(mutex); + + ptr = &(mutex->waiters); + + return(*ptr); /* Here we assume that the read of a single + word from memory is atomic */ +} + +/******************************************************************//** +NOTE! Use the corresponding macro mutex_exit(), not directly this function! +Unlocks a mutex owned by the current thread. */ +UNIV_INLINE +void +mutex_exit_func( +/*============*/ + ib_mutex_t* mutex) /*!< in: pointer to mutex */ +{ + ut_ad(mutex_own(mutex)); + + ut_d(mutex->thread_id = (os_thread_id_t) ULINT_UNDEFINED); + +#ifdef UNIV_SYNC_DEBUG + sync_thread_reset_level(mutex); +#endif + mutex_reset_lock_word(mutex); + + /* A problem: we assume that mutex_reset_lock word + is a memory barrier, that is when we read the waiters + field next, the read must be serialized in memory + after the reset. A speculative processor might + perform the read first, which could leave a waiting + thread hanging indefinitely. + + Our current solution call every second + sync_arr_wake_threads_if_sema_free() + to wake up possible hanging threads if + they are missed in mutex_signal_object. */ + + if (mutex_get_waiters(mutex) != 0) { + + mutex_signal_object(mutex); + } + +#ifdef UNIV_SYNC_PERF_STAT + mutex_exit_count++; +#endif +} + +/******************************************************************//** +Locks a mutex for the current thread. If the mutex is reserved, the function +spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the mutex +before suspending the thread. */ +UNIV_INLINE +void +mutex_enter_func( +/*=============*/ + ib_mutex_t* mutex, /*!< in: pointer to mutex */ + const char* file_name, /*!< in: file name where locked */ + ulint line) /*!< in: line where locked */ +{ + ut_ad(mutex_validate(mutex)); + ut_ad(!mutex_own(mutex)); + + /* Note that we do not peek at the value of lock_word before trying + the atomic test_and_set; we could peek, and possibly save time. */ + + if (!ib_mutex_test_and_set(mutex)) { + ut_d(mutex->thread_id = os_thread_get_curr_id()); +#ifdef UNIV_SYNC_DEBUG + mutex_set_debug_info(mutex, file_name, line); +#endif + return; /* Succeeded! */ + } + + mutex_spin_wait(mutex, file_name, line); +} + +#ifdef UNIV_PFS_MUTEX +/******************************************************************//** +NOTE! Please use the corresponding macro mutex_enter(), not directly +this function! +This is a performance schema instrumented wrapper function for +mutex_enter_func(). */ +UNIV_INLINE +void +pfs_mutex_enter_func( +/*=================*/ + ib_mutex_t* mutex, /*!< in: pointer to mutex */ + const char* file_name, /*!< in: file name where locked */ + ulint line) /*!< in: line where locked */ +{ + if (mutex->pfs_psi != NULL) { + PSI_mutex_locker* locker; + PSI_mutex_locker_state state; + + locker = PSI_MUTEX_CALL(start_mutex_wait)( + &state, mutex->pfs_psi, + PSI_MUTEX_LOCK, file_name, + static_cast<uint>(line)); + + mutex_enter_func(mutex, file_name, line); + + if (locker != NULL) { + PSI_MUTEX_CALL(end_mutex_wait)(locker, 0); + } + } else { + mutex_enter_func(mutex, file_name, line); + } +} + +/********************************************************************//** +NOTE! Please use the corresponding macro mutex_enter_nowait(), not directly +this function! +This is a performance schema instrumented wrapper function for +mutex_enter_nowait_func. +@return 0 if succeed, 1 if not */ +UNIV_INLINE +ulint +pfs_mutex_enter_nowait_func( +/*========================*/ + ib_mutex_t* mutex, /*!< in: pointer to mutex */ + const char* file_name, /*!< in: file name where mutex + requested */ + ulint line) /*!< in: line where requested */ +{ + ulint ret; + + if (mutex->pfs_psi != NULL) { + PSI_mutex_locker* locker; + PSI_mutex_locker_state state; + + locker = PSI_MUTEX_CALL(start_mutex_wait)( + &state, mutex->pfs_psi, + PSI_MUTEX_TRYLOCK, file_name, + static_cast<uint>(line)); + + ret = mutex_enter_nowait_func(mutex, file_name, line); + + if (locker != NULL) { + PSI_MUTEX_CALL(end_mutex_wait)(locker, (int) ret); + } + } else { + ret = mutex_enter_nowait_func(mutex, file_name, line); + } + + return(ret); +} + +/******************************************************************//** +NOTE! Please use the corresponding macro mutex_exit(), not directly +this function! +A wrap function of mutex_exit_func() with performance schema instrumentation. +Unlocks a mutex owned by the current thread. */ +UNIV_INLINE +void +pfs_mutex_exit_func( +/*================*/ + ib_mutex_t* mutex) /*!< in: pointer to mutex */ +{ + if (mutex->pfs_psi != NULL) { + PSI_MUTEX_CALL(unlock_mutex)(mutex->pfs_psi); + } + + mutex_exit_func(mutex); +} + +/******************************************************************//** +NOTE! Please use the corresponding macro mutex_create(), not directly +this function! +A wrapper function for mutex_create_func(), registers the mutex +with performance schema if "UNIV_PFS_MUTEX" is defined when +creating the mutex */ +UNIV_INLINE +void +pfs_mutex_create_func( +/*==================*/ + mysql_pfs_key_t key, /*!< in: Performance Schema key */ + ib_mutex_t* mutex, /*!< in: pointer to memory */ +# ifdef UNIV_DEBUG + const char* cmutex_name, /*!< in: mutex name */ +# ifdef UNIV_SYNC_DEBUG + ulint level, /*!< in: level */ +# endif /* UNIV_SYNC_DEBUG */ +# endif /* UNIV_DEBUG */ + const char* cfile_name, /*!< in: file name where created */ + ulint cline) /*!< in: file line where created */ +{ + mutex->pfs_psi = PSI_MUTEX_CALL(init_mutex)(key, mutex); + + mutex_create_func(mutex, +# ifdef UNIV_DEBUG + cmutex_name, +# ifdef UNIV_SYNC_DEBUG + level, +# endif /* UNIV_SYNC_DEBUG */ +# endif /* UNIV_DEBUG */ + cfile_name, + cline); +} + +/******************************************************************//** +NOTE! Please use the corresponding macro mutex_free(), not directly +this function! +Wrapper function for mutex_free_func(). Also destroys the performance +schema probes when freeing the mutex */ +UNIV_INLINE +void +pfs_mutex_free_func( +/*================*/ + ib_mutex_t* mutex) /*!< in: mutex */ +{ + if (mutex->pfs_psi != NULL) { + PSI_MUTEX_CALL(destroy_mutex)(mutex->pfs_psi); + mutex->pfs_psi = NULL; + } + + mutex_free_func(mutex); +} + +#endif /* UNIV_PFS_MUTEX */ + +#ifndef HAVE_ATOMIC_BUILTINS +/**********************************************************//** +Function that uses a mutex to decrement a variable atomically */ +UNIV_INLINE +void +os_atomic_dec_ulint_func( +/*=====================*/ + ib_mutex_t* mutex, /*!< in: mutex guarding the dec */ + volatile ulint* var, /*!< in/out: variable to decrement */ + ulint delta) /*!< in: delta to decrement */ +{ + mutex_enter(mutex); + + /* I don't think we will encounter a situation where + this check will not be required. */ + ut_ad(*var >= delta); + + *var -= delta; + + mutex_exit(mutex); +} + +/**********************************************************//** +Function that uses a mutex to increment a variable atomically */ +UNIV_INLINE +void +os_atomic_inc_ulint_func( +/*=====================*/ + ib_mutex_t* mutex, /*!< in: mutex guarding the increment */ + volatile ulint* var, /*!< in/out: variable to increment */ + ulint delta) /*!< in: delta to increment */ +{ + mutex_enter(mutex); + + *var += delta; + + mutex_exit(mutex); +} +#endif /* !HAVE_ATOMIC_BUILTINS */ |