innodb中srw_mutex的实现原理

1、涉及到的源文件代码节选

1)storage/innobase/include/srw_lock.h

#ifndef _SRW_LOCK_H_
#define _SRW_LOCK_H_

#include <assert.h>
#include <atomic>

using namespace std;

#define DBUG_ASSERT	assert
#define HOLDER (1U << 31)

/** Futex-based mutex */
class srw_mutex final
{
  /** The lock word, containing HOLDER and a count of waiters */
  atomic<uint32_t> lock;
  /** Identifies that the lock is being held */
  //static constexpr uint32_t HOLDER= 1U << 31;

  /** Wait until the mutex has been acquired */
  void wait_and_lock();
  /** Wait for lock!=lk */
  inline void wait(uint32_t lk);
  /** Wake up one wait() thread */
  void wake();
public:
  /** @return whether the mutex is being held or waited for */
  bool is_locked_or_waiting() const
  { return lock.load(memory_order_relaxed) != 0; }
  /** @return whether the mutex is being held by any thread */
  bool is_locked() const
  { return (lock.load(memory_order_relaxed) & HOLDER) != 0; }

  void init() { DBUG_ASSERT(!is_locked_or_waiting()); }
  void destroy() { DBUG_ASSERT(!is_locked_or_waiting()); }

  /** @return whether the mutex was acquired */
  bool wr_lock_try()
  {
    uint32_t lk= 0;
    return lock.compare_exchange_strong(lk, HOLDER,
                                        memory_order_acquire,
                                        memory_order_relaxed);
  }

  void wr_lock() { if (!wr_lock_try()) wait_and_lock(); }
  void wr_unlock()
  { 
    const uint32_t lk= lock.fetch_and(~HOLDER, memory_order_release);
    if (lk != HOLDER)
    {
      DBUG_ASSERT(lk & HOLDER);
      wake();
    }
  }
};

#endif

2)storage/innobase/sync/srw_lock.cc 

#include <sys/syscall.h>
#include <unistd.h>
#include "my_cpu.h"
#include "srw_lock.h"

#ifdef __linux__
#   include <linux/futex.h>
#   include <sys/syscall.h>
#   define SRW_FUTEX(a,op,n) \
    syscall(SYS_futex, a, FUTEX_ ## op ## _PRIVATE, n, nullptr, nullptr, 0)
#elif defined __OpenBSD__
#   include <sys/time.h>
#   include <sys/futex.h>
#   define SRW_FUTEX(a,op,n) \
    futex((volatile uint32_t*) a, FUTEX_ ## op, n, nullptr, nullptr)
#else
#   error "no futex support"
#endif

inline void srw_mutex::wait(uint32_t lk) { SRW_FUTEX(&lock, WAIT, lk); }
void srw_mutex::wake() { SRW_FUTEX(&lock, WAKE, 1); }

void srw_mutex::wait_and_lock()
{
  uint32_t lk= 1 + lock.fetch_add(1, std::memory_order_relaxed);
  for (auto spin= srv_n_spin_wait_rounds; spin; spin--)
  {
    lk&= ~HOLDER;
    DBUG_ASSERT(lk);
    while (!lock.compare_exchange_weak(lk, HOLDER | (lk - 1),
                                       std::memory_order_acquire,
                                       std::memory_order_relaxed))
      if (lk & HOLDER)
        goto occupied;
    return;
occupied:
    ut_delay(srv_spin_wait_delay);
  }

  for (;;)
  {
    lk= lock.load(std::memory_order_relaxed);
    while (!(lk & HOLDER))
    {
      DBUG_ASSERT(lk);
      if (lock.compare_exchange_weak(lk, HOLDER | (lk - 1),
                                     std::memory_order_acquire,
                                     std::memory_order_relaxed))
        return;
    }
    DBUG_ASSERT(lk > HOLDER);
    wait(lk);
  }
}

2、加解锁的逻辑
1)初始时 lock 的值为 0,如果当前锁没有被占用,则通过 exchange 方法(srw_lock.h 文件的第 41 行)将 lock 的值置为 1000...0000,即最高位为 1,表示当前锁已经被占用,解锁时,将 lock 的最高位置为 0(srw_lock.h 文件的第 49 行)。

2)如果当前锁已经被占用,则 exchange 方法(srw_lock.h 文件的第 41 行)返回为 false,此时将调用 wait_and_lock 函数(srw_lock.h 文件的第 46 行)进行加锁。

在该函数中,先将 lock 的值加 1 赋给 lk,同时 lock 自增 1 (srw_lock.cc 文件的第 25 行),也就是说,此时 lock 和 lk 的最高位都为 1。(如果接下来其他线程很快解锁了,那么 lock 的最高位将会为 0)。

接着, 抹去 lk 的最高位(srw_lock.cc 文件的第 28 行),然后判断 lock 和 lk 是否相等(srw_lock.cc 文件的第 30 行),由上面可知,只有在解锁的情况下,lock 和 lk 才可能相等(因为此时 lk 的最高位已经为 0 了)。

如果 lock 和 lk 相等,则 exchange 方法返回成功,即加锁成功,此时将 lock 的最高位置为 1(表示锁已被占用), 同时将 lock 的低位置为 lk - 1 (srw_lock.cc 文件的第 30 行),这是为了在解锁后能够让其他等待的线程加锁成功。

加锁成功后函数返回(srw_lock.cc 文件的第 35 行)。如果加锁失败,则将 lock 的值赋给 lk (exchang 方法自身的逻辑),即最高位为 1,同时第 33 行的判断条件为真,此时需要通过 ut_delay 函数进行延迟等待(srw_lock.cc 的第 37 行),然后继续尝试加锁。

3)如果连续尝试 srv_n_spin_wait_rounds 次加锁都未成功,那么线程将通过 wait 函数进行阻塞(srw_lock.cc 文件的第 52 行),唤醒的逻辑在解锁里面(srw_lock.h 的第 53 行),阻塞和唤醒都是通过系统调用 futex 实现的(srw_lock.cc 文件的第 10 行)。

唤醒后,继续尝试加锁 (srw_lock.cc 文件的第 46 行),如果还是失败,则再次阻塞、等待唤醒、尝试加锁,直到加锁成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值