如果条件 1 满足就使用 CAS 减去 X_LOCK_DECR, 然后等待lock_word的值为 0.
如果条件 1 不满足, 则判断是否可以递归加锁, 即在申请 X 锁之前是否已经持有了 SX 或者 X 锁.
如果该线程之前已经持有了 SX 锁, 则将lock_word的值减去 X_LOCK_DECR, 然后等待lock_word的值为 -X_LOCK_HALF_DECR, 因为 SX 锁和 S 锁兼容, 所以由 SX 锁升级为 X 锁后, 需要等待其他线程持有的 S 锁释放.
如果该线程之前已经持有了 X 锁, 则修改lock_word后即可.
上述条件都不满足, 即其他线程可能持有了 SX 锁或者 X 锁, 则进入 spin 的等待状态.
UNIV_INLINEboolrw_lock_x_lock_low(rw_lock_t*lock,/*!< in: pointer to rw-lock */ulintpass,/*!< in: pass value; != 0, if the lock will
be passed to another thread to unlock */constchar*file_name,/*!< in: file name where lock requested */ulintline)/*!< in: line where requested */{if(rw_lock_lock_word_decr(lock,X_LOCK_DECR,X_LOCK_HALF_DECR)){/* 1. 如果当前不存在 S 锁或者 SX 锁或者其他 X 锁, 步骤 1 是满足的,
* 步骤 2 也无需等待直接返回加锁成功. */rw_lock_set_writer_id_and_recursion_flag(lock,!pass);/* 2. 如果这个锁已经被其他线程加了数量小于 X_LOCK_HALF_DECR 个 S 锁,
* 步骤 1 也是满足的, 但是步骤 2 需要进入等待逻辑直到所有的 S 锁释放,
* 即使当前读写锁上已经有其他的 S 锁, 但是为了防止 X 锁饥饿状态,
* 所以将 X 预先分配给申请线程. */rw_lock_x_lock_wait(lock,pass,0,file_name,line);}else{if(!pass&&lock->recursive.load(std::memory_order_acquire)&&lock->writer_thread.load(std::memory_order_relaxed)==std::this_thread::get_id()){/* The existing X or SX lock is from this thread */if(rw_lock_lock_word_decr(lock,X_LOCK_DECR,0)){/* 之前已经持有了 SX 锁, 由 SX 锁升级为 X 锁. */rw_lock_x_lock_wait(lock,pass,-X_LOCK_HALF_DECR,file_name,line);}else{/* 之前已经持有了 X 锁. */if(lock->lock_word==0||lock->lock_word==-X_LOCK_HALF_DECR){/* 1. 上一次直接申请了 X 锁, 所以 lock_word 为 0.
* 2. 上一次是 SX 锁升级为 X 锁, 所以 lock_word 为-X_LOCK_HALF_DECR.
* 3. 所以 X 锁的 lock_word 减去 X_LOCK_DECR. */lock->lock_word-=X_LOCK_DECR;}else{ut_ad(lock->lock_word<=-X_LOCK_DECR);/* 之前已经持有了两个 X 锁, 所以当前的 lock_word 递减 1 即可. */--lock->lock_word;}}}else{/* 申请失败, 返回进入等待. */returnfalse;}}ut_d(rw_lock_add_debug_info(lock,pass,RW_LOCK_X,file_name,line));lock->last_x_file_name=file_name;ut_ad(line<=std::numeric_limits<decltype(lock->last_x_line)>::max());lock->last_x_line=line;/* 申请 X 锁成功. */returntrue;}
申请 SX 锁
通过函数rw_lock_sx_lock_low()来申请获取 SX 锁:
判断当前的lock_word是否大于 X_LOCK_HALF_DECR.
如果条件 1 满足就使用 CAS 减去 X_LOCK_DECR, 因为 SX 锁和 S 锁兼容, 所以无需等待 lock_word 的值为 0.
如果条件 1 不满足, 则判断是否可以递归加锁, 即在申请 X 锁之前是否已经持有了 SX 或者 X 锁.
如果该线程之前已经成功申请了一个 X 锁, 所以 lock_word -= X_LOCK_HALF_DECR.
上述条件都不满足, 即 SX 或者 X 锁已经被其他线程持有, 则进入 spin 的等待状态.
boolrw_lock_sx_lock_low(rw_lock_t*lock,/*!< in: pointer to rw-lock */ulintpass,/*!< in: pass value; != 0, if the lock will
be passed to another thread to unlock */constchar*file_name,/*!< in: file name where lock requested */ulintline)/*!< in: line where requested */{if(rw_lock_lock_word_decr(lock,X_LOCK_HALF_DECR,X_LOCK_HALF_DECR)){/* 1. 判断当前的 lock_word 是否大于 X_LOCK_HALF_DECR.
* 2. 如果条件 1 满足就使用 CAS 减去 X_LOCK_DECR, 因为 SX 锁和 S 锁兼容,
* 所以无需等待 lock_word 的值为 0. *//* lock->recursive == true implies that the lock->writer_thread is the
current writer. As we are going to write our own thread id in that field it
must be the case that the current writer_thread value is not the current
writer anymore, thus recursive flag must be false. */ut_a(!lock->recursive.load(std::memory_order_relaxed));/* Decrement occurred: we are the SX lock owner. */rw_lock_set_writer_id_and_recursion_flag(lock,!pass);/* 设置 sx_recursive 的标记为 1. */lock->sx_recursive=1;}else{/* ... */if(!pass&&lock->recursive.load(std::memory_order_acquire)&&lock->writer_thread.load(std::memory_order_relaxed)==std::this_thread::get_id()){/* This thread owns an X or SX lock */if(lock->sx_recursive++==0){/* ... */ut_ad((lock->lock_word==0)||((lock->lock_word<=-X_LOCK_DECR)&&(lock->lock_word>-(X_LOCK_DECR+X_LOCK_HALF_DECR))));/* 之前已经成功申请了一个 X 锁, 所以 lock_word -= X_LOCK_HALF_DECR. */lock->lock_word-=X_LOCK_HALF_DECR;}}else{/* Another thread locked before us */returnfalse;}}ut_d(rw_lock_add_debug_info(lock,pass,RW_LOCK_SX,file_name,line));lock->last_x_file_name=file_name;ut_ad(line<=std::numeric_limits<decltype(lock->last_x_line)>::max());lock->last_x_line=line;returntrue;}
申请 S 锁
通过函数rw_lock_s_lock_low()来申请获取 S 锁, 判断的逻辑是当前的lock_word是否大于 0, 如果大于 0 则使用 CAS 操作减去 1.
如何通过 lock_word 判断锁信息
lock_word = X_LOCK_DECR: 没有任何锁持有.
X_LOCK_HALF_DECR < lock_word < X_LOCK_DECR: 存在 S 锁, S 锁的数量是(X_LOCK_DECR - lock_word), 并且没有任何 SX/X 锁的申请等待.