/* Our mutex implementation works as follows: After that we perform the atomic test-and-set instruction on the memory word. If the test returns zero, we know we got the lock first. If the test returns not zero, some other thread was quicker and got the lock: then we spin in a loop reading the memory word, waiting it to become zero. It is wise to just read the word in the loop, not perform numerous test-and-set instructions, because they generate memory traffic between the cache and the main memory. The read loop can just access the cache, saving bus bandwidth. If we cannot acquire the mutex lock in the specified time, we reserve a cell in the wait array, set the waiters byte in the mutex to 1. To avoid a race condition, after setting the waiters byte and before suspending the waiting thread, we still have to check that the mutex is reserved, because it may have happened that the thread which was holding the mutex has just released it and did not see the waiters byte set to 1, a case which would lead the other thread to an infinite wait. */ // 放到全局的 mutex_list 里 /** InnoDB mutex */ struct mutex_struct { os_event_t event; /*!< Used by sync0arr.c for the wait queue */ volatile lock_word_t lock_word; /*!< lock_word is the target of the atomic test-and-set instruction when atomic operations are enabled. */ #if !defined(HAVE_ATOMIC_BUILTINS) os_fast_mutex_t os_fast_mutex; /*!< We use this OS mutex in place of lock_word when atomic operations are not enabled */ #endif ulint waiters; /*!< This ulint is set to 1 if there are (or may be) threads waiting in the global wait array for this mutex to be released. Otherwise, this is 0. */ UT_LIST_NODE_T(mutex_t) list; /*!< All allocated mutexes are put into a list. Pointers to the next and prev. */ #ifdef UNIV_SYNC_DEBUG const char* file_name; /*!< File where the mutex was locked */ ulint line; /*!< Line where the mutex was locked */ ulint level; /*!< Level in the global latching order */ #endif /* UNIV_SYNC_DEBUG */ const char* cfile_name;/*!< File name where mutex created */ ulint cline; /*!< Line where created */ #ifdef UNIV_DEBUG os_thread_id_t thread_id; /*!< The thread id of the thread which locked the mutex. */ ulint magic_n; /*!< MUTEX_MAGIC_N */ /** Value of mutex_struct::magic_n */ # define MUTEX_MAGIC_N (ulint)979585 #endif /* UNIV_DEBUG */ ulong count_os_wait; /*!< count of os_wait */ #ifdef UNIV_DEBUG ulong count_using; /*!< count of times mutex used */ ulong count_spin_loop; /*!< count of spin loops */ ulong count_spin_rounds;/*!< count of spin rounds */ ulong count_os_yield; /*!< count of os_wait */ ulonglong lspent_time; /*!< mutex os_wait timer msec */ ulonglong lmax_spent_time;/*!< mutex os_wait timer msec */ const char* cmutex_name; /*!< mutex name */ ulint mutex_type; /*!< 0=usual mutex, 1=rw_lock mutex */ #endif /* UNIV_DEBUG */ #ifdef UNIV_PFS_MUTEX struct PSI_mutex* pfs_psi; /*!< The performance schema instrumentation hook */ #endif }; /* The status of a rw_lock is held in lock_word. The initial value of lock_word is X_LOCK_DECR. lock_word is decremented by 1 for each s-lock and by X_LOCK_DECR for each x-lock. This describes the lock state for each value of lock_word: lock_word == X_LOCK_DECR: Unlocked. 0 < lock_word < X_LOCK_DECR: Read locked, no waiting writers. (X_LOCK_DECR - lock_word) is the number of readers that hold the lock. lock_word == 0: Write locked -X_LOCK_DECR < lock_word < 0: Read locked, with a waiting writer. (-lock_word) is the number of readers that hold the lock. lock_word <= -X_LOCK_DECR: Recursively write locked. lock_word has been decremented by X_LOCK_DECR once for each lock, so the number of locks is: ((-lock_word) / X_LOCK_DECR) + 1 When lock_word <= -X_LOCK_DECR, we also know that lock_word % X_LOCK_DECR == 0: other values of lock_word are invalid. The lock_word is always read and updated atomically and consistently, so that it always represents the state of the lock, and the state of the lock changes with a single atomic operation. This lock_word holds all of the information that a thread needs in order to determine if it is eligible to gain the lock or if it must spin or sleep. The one exception to this is that writer_thread must be verified before recursive write locks: to solve this scenario, we make writer_thread readable by all threads, but only writeable by the x-lock holder. */ //rw lock 放在全局的 rw_lock_list 链表里 /** The structure used in the spin lock implementation of a read-write lock. Several threads may have a shared lock simultaneously in this lock, but only one writer may have an exclusive lock, in which case no shared locks are allowed. To prevent starving of a writer blocked by readers, a writer may queue for x-lock by decrementing lock_word: no new readers will be let in while the thread waits for readers to exit. */ struct rw_lock_struct { volatile lint lock_word; /*!< Holds the state of the lock. */ volatile ulint waiters;/*!< 1: there are waiters */ volatile ibool recursive;/*!< 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 os_thread_id_t writer_thread; /*!< Thread id of writer thread. Is only guaranteed to have sane and non-stale value iff recursive flag is set. */ os_event_t event; /*!< Used by sync0arr.c for thread queueing */ os_event_t wait_ex_event; /*!< Event for next-writer to wait on. A thread must decrement lock_word before waiting. */ #ifndef INNODB_RW_LOCKS_USE_ATOMICS mutex_t mutex; /*!< The mutex protecting rw_lock_struct */ #endif /* INNODB_RW_LOCKS_USE_ATOMICS */ UT_LIST_NODE_T(rw_lock_t) list; /*!< All allocated rw locks are put into a list */ #ifdef UNIV_SYNC_DEBUG UT_LIST_BASE_NODE_T(rw_lock_debug_t) debug_list; /*!< In the debug version: pointer to the debug info list of the lock */ ulint level; /*!< Level in the global latching order. */ #endif /* UNIV_SYNC_DEBUG */ #ifdef UNIV_PFS_RWLOCK struct PSI_rwlock *pfs_psi;/*!< The instrumentation hook */ #endif ulint count_os_wait; /*!< Count of os_waits. May not be accurate */ const char* cfile_name;/*!< File name where lock created */ /* last s-lock file/line is not guaranteed to be correct */ const char* last_s_file_name;/*!< File name where last s-locked */ const char* last_x_file_name;/*!< File name where last x-locked */ ibool writer_is_wait_ex; /*!< This is TRUE if the writer field is RW_LOCK_WAIT_EX; this field is located far from the memory update hotspot fields which are at the start of this struct, thus we can peek this field without causing much memory bus traffic */ unsigned cline:14; /*!< Line where created */ unsigned last_s_line:14; /*!< Line number where last time s-locked */ unsigned last_x_line:14; /*!< Line number where last time x-locked */ #ifdef UNIV_DEBUG ulint magic_n; /*!< RW_LOCK_MAGIC_N */ /** Value of rw_lock_struct::magic_n */ #define RW_LOCK_MAGIC_N 22643 #endif /* UNIV_DEBUG */ }; 写优先,