我們習慣性的使用互斥鎖來保護某個臨界區,它可能是一個變量,或者一段代碼,比如在進入某個函數後,需要對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<int>(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<my_time_t>(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;
}