1/***************************************************************************** 2 3Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved. 4Copyright (c) 2008, Google Inc. 5 6Portions of this file contain modifications contributed and copyrighted by 7Google, Inc. Those modifications are gratefully acknowledged and are described 8briefly in the InnoDB documentation. The contributions by Google are 9incorporated with their permission, and subject to the conditions contained in 10the file COPYING.Google. 11 12This program is free software; you can redistribute it and/or modify 13it under the terms of the GNU General Public License, version 2.0, 14as published by the Free Software Foundation. 15 16This program is also distributed with certain software (including 17but not limited to OpenSSL) that is licensed under separate terms, 18as designated in a particular file or component or in included license 19documentation. The authors of MySQL hereby grant you an additional 20permission to link the program and your derivative works with the 21separately licensed software that they have included with MySQL. 22 23This program is distributed in the hope that it will be useful, 24but WITHOUT ANY WARRANTY; without even the implied warranty of 25MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26GNU General Public License, version 2.0, for more details. 27 28You should have received a copy of the GNU General Public License along with 29this program; if not, write to the Free Software Foundation, Inc., 3051 Franklin Street, Suite 500, Boston, MA 02110-1335 USA 31 32*****************************************************************************/ 33 34/**************************************************//** 35@file include/sync0sync.ic 36Mutex, the basic synchronization primitive 37 38Created 9/5/1995 Heikki Tuuri 39*******************************************************/ 40 41/******************************************************************//** 42Sets the waiters field in a mutex. */ 43UNIV_INTERN 44void 45mutex_set_waiters( 46/*==============*/ 47 ib_mutex_t* mutex, /*!< in: mutex */ 48 ulint n); /*!< in: value to set */ 49/******************************************************************//** 50Reserves a mutex for the current thread. If the mutex is reserved, the 51function spins a preset time (controlled by SYNC_SPIN_ROUNDS) waiting 52for the mutex before suspending the thread. */ 53UNIV_INTERN 54void 55mutex_spin_wait( 56/*============*/ 57 ib_mutex_t* mutex, /*!< in: pointer to mutex */ 58 const char* file_name, /*!< in: file name where mutex 59 requested */ 60 ulint line); /*!< in: line where requested */ 61#ifdef UNIV_SYNC_DEBUG 62/******************************************************************//** 63Sets the debug information for a reserved mutex. */ 64UNIV_INTERN 65void 66mutex_set_debug_info( 67/*=================*/ 68 ib_mutex_t* mutex, /*!< in: mutex */ 69 const char* file_name, /*!< in: file where requested */ 70 ulint line); /*!< in: line where requested */ 71#endif /* UNIV_SYNC_DEBUG */ 72/******************************************************************//** 73Releases the threads waiting in the primary wait array for this mutex. */ 74UNIV_INTERN 75void 76mutex_signal_object( 77/*================*/ 78 ib_mutex_t* mutex); /*!< in: mutex */ 79 80/******************************************************************//** 81Performs an atomic test-and-set instruction to the lock_word field of a 82mutex. 83@return the previous value of lock_word: 0 or 1 */ 84UNIV_INLINE 85lock_word_t 86ib_mutex_test_and_set( 87/*==================*/ 88 ib_mutex_t* mutex) /*!< in: mutex */ 89{ 90#if defined(HAVE_ATOMIC_BUILTINS) 91 return(os_atomic_test_and_set(&mutex->lock_word)); 92#else 93 ibool ret; 94 95 ret = os_fast_mutex_trylock(&(mutex->os_fast_mutex)); 96 97 if (ret == 0) { 98 /* We check that os_fast_mutex_trylock does not leak 99 and allow race conditions */ 100 ut_a(mutex->lock_word == 0); 101 102 mutex->lock_word = 1; 103 os_wmb; 104 } 105 106 return((byte) ret); 107#endif /* HAVE_ATOMIC_BUILTINS */ 108} 109 110/******************************************************************//** 111Performs a reset instruction to the lock_word field of a mutex. This 112instruction also serializes memory operations to the program order. */ 113UNIV_INLINE 114void 115mutex_reset_lock_word( 116/*==================*/ 117 ib_mutex_t* mutex) /*!< in: mutex */ 118{ 119#if defined(HAVE_ATOMIC_BUILTINS) 120 os_atomic_clear(&mutex->lock_word); 121#else 122 mutex->lock_word = 0; 123 124 os_fast_mutex_unlock(&(mutex->os_fast_mutex)); 125#endif /* HAVE_ATOMIC_BUILTINS */ 126} 127 128/******************************************************************//** 129Gets the value of the lock word. */ 130UNIV_INLINE 131lock_word_t 132mutex_get_lock_word( 133/*================*/ 134 const ib_mutex_t* mutex) /*!< in: mutex */ 135{ 136 ut_ad(mutex); 137 138 return(mutex->lock_word); 139} 140 141/******************************************************************//** 142Gets the waiters field in a mutex. 143@return value to set */ 144UNIV_INLINE 145ulint 146mutex_get_waiters( 147/*==============*/ 148 const ib_mutex_t* mutex) /*!< in: mutex */ 149{ 150 const volatile ulint* ptr; /*!< declared volatile to ensure that 151 the value is read from memory */ 152 ut_ad(mutex); 153 154 ptr = &(mutex->waiters); 155 156 return(*ptr); /* Here we assume that the read of a single 157 word from memory is atomic */ 158} 159 160/******************************************************************//** 161NOTE! Use the corresponding macro mutex_exit(), not directly this function! 162Unlocks a mutex owned by the current thread. */ 163UNIV_INLINE 164void 165mutex_exit_func( 166/*============*/ 167 ib_mutex_t* mutex) /*!< in: pointer to mutex */ 168{ 169 ut_ad(mutex_own(mutex)); 170 171 ut_d(mutex->thread_id = (os_thread_id_t) ULINT_UNDEFINED); 172 173#ifdef UNIV_SYNC_DEBUG 174 sync_thread_reset_level(mutex); 175#endif 176 mutex_reset_lock_word(mutex); 177 178 /* A problem: we assume that mutex_reset_lock word 179 is a memory barrier, that is when we read the waiters 180 field next, the read must be serialized in memory 181 after the reset. A speculative processor might 182 perform the read first, which could leave a waiting 183 thread hanging indefinitely. 184 185 Our current solution call every second 186 sync_arr_wake_threads_if_sema_free() 187 to wake up possible hanging threads if 188 they are missed in mutex_signal_object. */ 189 190 if (mutex_get_waiters(mutex) != 0) { 191 192 mutex_signal_object(mutex); 193 } 194 195#ifdef UNIV_SYNC_PERF_STAT 196 mutex_exit_count++; 197#endif 198} 199 200/******************************************************************//** 201Locks a mutex for the current thread. If the mutex is reserved, the function 202spins a preset time (controlled by SYNC_SPIN_ROUNDS), waiting for the mutex 203before suspending the thread. */ 204UNIV_INLINE 205void 206mutex_enter_func( 207/*=============*/ 208 ib_mutex_t* mutex, /*!< in: pointer to mutex */ 209 const char* file_name, /*!< in: file name where locked */ 210 ulint line) /*!< in: line where locked */ 211{ 212 ut_ad(mutex_validate(mutex)); 213 ut_ad(!mutex_own(mutex)); 214 215 /* Note that we do not peek at the value of lock_word before trying 216 the atomic test_and_set; we could peek, and possibly save time. */ 217 218 if (!ib_mutex_test_and_set(mutex)) { 219 ut_d(mutex->thread_id = os_thread_get_curr_id()); 220#ifdef UNIV_SYNC_DEBUG 221 mutex_set_debug_info(mutex, file_name, line); 222#endif 223 return; /* Succeeded! */ 224 } 225 226 mutex_spin_wait(mutex, file_name, line); 227} 228 229#ifdef UNIV_PFS_MUTEX 230/******************************************************************//** 231NOTE! Please use the corresponding macro mutex_enter(), not directly 232this function! 233This is a performance schema instrumented wrapper function for 234mutex_enter_func(). */ 235UNIV_INLINE 236void 237pfs_mutex_enter_func( 238/*=================*/ 239 ib_mutex_t* mutex, /*!< in: pointer to mutex */ 240 const char* file_name, /*!< in: file name where locked */ 241 ulint line) /*!< in: line where locked */ 242{ 243 if (mutex->pfs_psi != NULL) { 244 PSI_mutex_locker* locker; 245 PSI_mutex_locker_state state; 246 247 locker = PSI_MUTEX_CALL(start_mutex_wait)( 248 &state, mutex->pfs_psi, 249 PSI_MUTEX_LOCK, file_name, 250 static_cast<uint>(line)); 251 252 mutex_enter_func(mutex, file_name, line); 253 254 if (locker != NULL) { 255 PSI_MUTEX_CALL(end_mutex_wait)(locker, 0); 256 } 257 } else { 258 mutex_enter_func(mutex, file_name, line); 259 } 260} 261 262/********************************************************************//** 263NOTE! Please use the corresponding macro mutex_enter_nowait(), not directly 264this function! 265This is a performance schema instrumented wrapper function for 266mutex_enter_nowait_func. 267@return 0 if succeed, 1 if not */ 268UNIV_INLINE 269ulint 270pfs_mutex_enter_nowait_func( 271/*========================*/ 272 ib_mutex_t* mutex, /*!< in: pointer to mutex */ 273 const char* file_name, /*!< in: file name where mutex 274 requested */ 275 ulint line) /*!< in: line where requested */ 276{ 277 ulint ret; 278 279 if (mutex->pfs_psi != NULL) { 280 PSI_mutex_locker* locker; 281 PSI_mutex_locker_state state; 282 283 locker = PSI_MUTEX_CALL(start_mutex_wait)( 284 &state, mutex->pfs_psi, 285 PSI_MUTEX_TRYLOCK, file_name, 286 static_cast<uint>(line)); 287 288 ret = mutex_enter_nowait_func(mutex, file_name, line); 289 290 if (locker != NULL) { 291 PSI_MUTEX_CALL(end_mutex_wait)(locker, (int) ret); 292 } 293 } else { 294 ret = mutex_enter_nowait_func(mutex, file_name, line); 295 } 296 297 return(ret); 298} 299 300/******************************************************************//** 301NOTE! Please use the corresponding macro mutex_exit(), not directly 302this function! 303A wrap function of mutex_exit_func() with performance schema instrumentation. 304Unlocks a mutex owned by the current thread. */ 305UNIV_INLINE 306void 307pfs_mutex_exit_func( 308/*================*/ 309 ib_mutex_t* mutex) /*!< in: pointer to mutex */ 310{ 311 if (mutex->pfs_psi != NULL) { 312 PSI_MUTEX_CALL(unlock_mutex)(mutex->pfs_psi); 313 } 314 315 mutex_exit_func(mutex); 316} 317 318/******************************************************************//** 319NOTE! Please use the corresponding macro mutex_create(), not directly 320this function! 321A wrapper function for mutex_create_func(), registers the mutex 322with performance schema if "UNIV_PFS_MUTEX" is defined when 323creating the mutex */ 324UNIV_INLINE 325void 326pfs_mutex_create_func( 327/*==================*/ 328 mysql_pfs_key_t key, /*!< in: Performance Schema key */ 329 ib_mutex_t* mutex, /*!< in: pointer to memory */ 330# ifdef UNIV_DEBUG 331 const char* cmutex_name, /*!< in: mutex name */ 332# ifdef UNIV_SYNC_DEBUG 333 ulint level, /*!< in: level */ 334# endif /* UNIV_SYNC_DEBUG */ 335# endif /* UNIV_DEBUG */ 336 const char* cfile_name, /*!< in: file name where created */ 337 ulint cline) /*!< in: file line where created */ 338{ 339 mutex->pfs_psi = PSI_MUTEX_CALL(init_mutex)(key, mutex); 340 341 mutex_create_func(mutex, 342# ifdef UNIV_DEBUG 343 cmutex_name, 344# ifdef UNIV_SYNC_DEBUG 345 level, 346# endif /* UNIV_SYNC_DEBUG */ 347# endif /* UNIV_DEBUG */ 348 cfile_name, 349 cline); 350} 351 352/******************************************************************//** 353NOTE! Please use the corresponding macro mutex_free(), not directly 354this function! 355Wrapper function for mutex_free_func(). Also destroys the performance 356schema probes when freeing the mutex */ 357UNIV_INLINE 358void 359pfs_mutex_free_func( 360/*================*/ 361 ib_mutex_t* mutex) /*!< in: mutex */ 362{ 363 if (mutex->pfs_psi != NULL) { 364 PSI_MUTEX_CALL(destroy_mutex)(mutex->pfs_psi); 365 mutex->pfs_psi = NULL; 366 } 367 368 mutex_free_func(mutex); 369} 370 371#endif /* UNIV_PFS_MUTEX */ 372 373#ifndef HAVE_ATOMIC_BUILTINS 374/**********************************************************//** 375Function that uses a mutex to decrement a variable atomically */ 376UNIV_INLINE 377void 378os_atomic_dec_ulint_func( 379/*=====================*/ 380 ib_mutex_t* mutex, /*!< in: mutex guarding the dec */ 381 volatile ulint* var, /*!< in/out: variable to decrement */ 382 ulint delta) /*!< in: delta to decrement */ 383{ 384 mutex_enter(mutex); 385 386 /* I don't think we will encounter a situation where 387 this check will not be required. */ 388 ut_ad(*var >= delta); 389 390 *var -= delta; 391 392 mutex_exit(mutex); 393} 394 395/**********************************************************//** 396Function that uses a mutex to increment a variable atomically */ 397UNIV_INLINE 398void 399os_atomic_inc_ulint_func( 400/*=====================*/ 401 ib_mutex_t* mutex, /*!< in: mutex guarding the increment */ 402 volatile ulint* var, /*!< in/out: variable to increment */ 403 ulint delta) /*!< in: delta to increment */ 404{ 405 mutex_enter(mutex); 406 407 *var += delta; 408 409 mutex_exit(mutex); 410} 411#endif /* !HAVE_ATOMIC_BUILTINS */ 412