MySQL中的互斥锁包装器

我们习惯性的使用互斥锁来保护某个临界区,它可能是一个变量,或者一段代码,比如在进入某个函数后,需要对mutex加锁,而无论任何情况下,只要函数退出,就要把锁释放掉,而函数可能会因为各种情况而退出,包括遇到各种异常,错误,不同的结果等等,一个可行的方案是在所有可能退出函数的地方进行锁的释放,亦或者通过goto/break等语法来控制函数在特定的位置退出,并且退出前进行锁的释放,但这些实现方式相比较今天要说的互斥锁包装器来说,没那么优雅,下面看下MySQL是如何使用互斥锁包装器的。

在MySQL中,通过Mutex_lock这个类来实现对mutex锁的简单封装,代码非常简练,只有十几行,如下:

class Mutex_lock
{
public:
  explicit Mutex_lock(mysql_mutex_t *mutex) : m_mutex(mutex)
  {
    if (m_mutex)
      mysql_mutex_lock(m_mutex);
  }
  ~Mutex_lock()
  {
    if (m_mutex)
      mysql_mutex_unlock(m_mutex);
  }
private:
  mysql_mutex_t *m_mutex;

  Mutex_lock(const Mutex_lock&);                /* Not copyable. */
  void operator=(const Mutex_lock&);            /* Not assignable. */
};

Mutex_lock类将拷贝构造函数和赋值函数设置为private类型,来避免实例通过这种方式被复制。

Mutex_lock的原理就是在局部变量离开当前作用域时,会被自动析构,而析构函数中调用解锁函数。

MySQL中也有很多地方利用Mutex_lock来实现代码作用域的锁控制,比如

int add_status_vars(const SHOW_VAR *list)
{
  Mutex_lock lock(status_vars_inited ? &LOCK_status : NULL);

  try
  {
    while (list->name)
      all_status_vars.push_back(*list++);
  }
  catch (std::bad_alloc)
  {
    my_error(ER_OUTOFMEMORY, MYF(ME_FATALERROR),
             static_cast(sizeof(Status_var_array::value_type)));
    return 1;
  }

  if (status_vars_inited)
    std::sort(all_status_vars.begin(), all_status_vars.end(), Show_var_cmp());
  
  status_var_array_version++;
  return 0;
}

或者是:

/**
  Calculate the timestamp difference for password expiry

  @param thd			 thread handle
  @param acl_user		 ACL_USER handle

  @retval 0  password is valid
  @retval 1  password has expired
*/
bool
check_password_lifetime(THD *thd, const ACL_USER *acl_user)
{

  bool password_time_expired= false;

  if (likely(acl_user != NULL) && !acl_user->password_expired &&
      acl_user->password_last_changed.time_type != MYSQL_TIMESTAMP_ERROR
      && auth_plugin_is_built_in(acl_user->plugin.str)
      && (acl_user->use_default_password_lifetime ||
      acl_user->password_lifetime))
  {
    MYSQL_TIME cur_time, password_change_by;
    Interval interval;

    thd->set_time();
    thd->variables.time_zone->gmt_sec_to_TIME(&cur_time,
      static_cast(thd->query_start()));
    password_change_by= acl_user->password_last_changed;
    memset(&interval, 0, sizeof(interval));

    if (!acl_user->use_default_password_lifetime)
      interval.day= acl_user->password_lifetime;
    else
    {
      Mutex_lock lock(&LOCK_default_password_lifetime);
      interval.day= default_password_lifetime;
    }
    if (interval.day)
    {
      if (!date_add_interval(&password_change_by, INTERVAL_DAY, interval))
        password_time_expired= my_time_compare(&password_change_by,
                                               &cur_time) >=0 ? false: true;
      else
      {
        DBUG_ASSERT(FALSE);
        /* Make the compiler happy. */
      }
    }
  }
  DBUG_EXECUTE_IF("force_password_interval_expire",
                  {
                    if (!acl_user->use_default_password_lifetime &&
                        acl_user->password_lifetime)
                      password_time_expired= true;
                  });
  DBUG_EXECUTE_IF("force_password_interval_expire_for_time_type",
                  {
                    if (acl_user->password_last_changed.time_type !=
                        MYSQL_TIMESTAMP_ERROR)
                      password_time_expired= true;
                  });
  return password_time_expired;
}

你可能感兴趣的:(MySQL)