/****************************************************** Mutex, the basic synchronization primitive (c) 1995 Innobase Oy Created 9/5/1995 Heikki Tuuri *******************************************************/ /********************************************************************** Sets the waiters field in a mutex. */ void mutex_set_waiters( /*==============*/ 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. */ void mutex_spin_wait( /*============*/ mutex_t* mutex /* in: pointer to mutex */ #ifdef UNIV_SYNC_DEBUG ,char* file_name, /* in: file name where mutex requested */ ulint line /* in: line where requested */ #endif ); /********************************************************************** Sets the debug information for a reserved mutex. */ void mutex_set_debug_info( /*=================*/ mutex_t* mutex, /* in: mutex */ char* file_name, /* in: file where requested */ ulint line); /* in: line where requested */ /********************************************************************** Releases the threads waiting in the primary wait array for this mutex. */ void mutex_signal_object( /*================*/ mutex_t* mutex); /* in: mutex */ /********************************************************************** Performs an atomic test-and-set instruction to the lock_word field of a mutex. */ UNIV_INLINE ulint mutex_test_and_set( /*===============*/ /* out: the previous value of lock_word: 0 or 1 */ mutex_t* mutex) /* in: mutex */ { #ifdef _WIN32 ulint res; ulint* lw; /* assembler code is used to ensure that lock_word is loaded from memory */ ut_ad(mutex); ut_ad(sizeof(ulint) == 4); lw = &(mutex->lock_word); __asm MOV ECX, lw __asm MOV EDX, 1 __asm XCHG EDX, DWORD PTR [ECX] __asm MOV res, EDX /* The fence below would prevent this thread from reading the data structure protected by the mutex before the test-and-set operation is committed, but the fence is apparently not needed: In a posting to comp.arch newsgroup (August 10, 1997) Andy Glew said that in P6 a LOCKed instruction like XCHG establishes a fence with respect to memory reads and writes and thus an explicit fence is not needed. In P5 he seemed to agree with a previous newsgroup poster that LOCKed instructions serialize all instruction execution, and, consequently, also memory operations. This is confirmed in Intel Software Dev. Manual, Vol. 3. */ /* mutex_fence(); */ return(res); #else ibool ret; ret = os_fast_mutex_trylock(&(mutex->os_fast_mutex)); if (ret == 0) { mutex->lock_word = 1; } return(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( /*==================*/ mutex_t* mutex) /* in: mutex */ { #ifdef _WIN32 ulint* lw; /* assembler code is used to ensure that lock_word is loaded from memory */ ut_ad(mutex); lw = &(mutex->lock_word); __asm MOV EDX, 0 __asm MOV ECX, lw __asm XCHG EDX, DWORD PTR [ECX] #else mutex->lock_word = 0; os_fast_mutex_unlock(&(mutex->os_fast_mutex)); #endif } /********************************************************************** Gets the value of the lock word. */ UNIV_INLINE ulint mutex_get_lock_word( /*================*/ mutex_t* mutex) /* in: mutex */ { volatile ulint* ptr; /* declared volatile to ensure that lock_word is loaded from memory */ ut_ad(mutex); ptr = &(mutex->lock_word); return(*ptr); } /********************************************************************** Gets the waiters field in a mutex. */ UNIV_INLINE ulint mutex_get_waiters( /*==============*/ /* out: value to set */ mutex_t* mutex) /* in: mutex */ { 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 */ } /********************************************************************** Unlocks a mutex owned by the current thread. */ UNIV_INLINE void mutex_exit( /*=======*/ mutex_t* mutex) /* in: pointer to mutex */ { ut_ad(mutex_own(mutex)); #ifdef UNIV_SYNC_DEBUG mutex->thread_id = ULINT_UNDEFINED; sync_thread_reset_level(mutex); #endif mutex_reset_lock_word(mutex); 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( /*=============*/ mutex_t* mutex /* in: pointer to mutex */ #ifdef UNIV_SYNC_DEBUG ,char* file_name, /* in: file name where locked */ ulint line /* in: line where locked */ #endif ) { ut_ad(mutex_validate(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 (!mutex_test_and_set(mutex)) { #ifdef UNIV_SYNC_DEBUG mutex_set_debug_info(mutex, file_name, line); #endif return; /* Succeeded! */ } mutex_spin_wait(mutex #ifdef UNIV_SYNC_DEBUG ,file_name, line #endif ); }