1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-14485 Server hangs on startup in THD::init

Solve 3 way deadlock between plugin_initialiaze(), THD::init() and
mysql_sys_var_char().

The deadlock exists because of the lock order inversion between
LOCK_global_system_variables mutex and LOCK_system_variables_hash
read-write lock-

In this case, it is enough to change LOCK_system_variables_hash to prefer
reads to fix the deadlock, i.e change it to mysql_prlock_t
This commit is contained in:
Vladislav Vaintroub
2018-01-27 17:46:31 +00:00
parent 1da063a45b
commit 7cdf759c86
6 changed files with 22 additions and 22 deletions

View File

@ -1394,10 +1394,10 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin,
mysql_mutex_unlock(&LOCK_plugin);
mysql_rwlock_wrlock(&LOCK_system_variables_hash);
mysql_prlock_wrlock(&LOCK_system_variables_hash);
if (test_plugin_options(tmp_root, plugin, argc, argv))
state= PLUGIN_IS_DISABLED;
mysql_rwlock_unlock(&LOCK_system_variables_hash);
mysql_prlock_unlock(&LOCK_system_variables_hash);
if (options_only || state == PLUGIN_IS_DISABLED)
{
@ -2797,11 +2797,11 @@ sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
if (!locked)
mysql_mutex_lock(&LOCK_plugin);
mysql_rwlock_rdlock(&LOCK_system_variables_hash);
mysql_prlock_rdlock(&LOCK_system_variables_hash);
if ((var= intern_find_sys_var(str, length)) &&
(pi= var->cast_pluginvar()))
{
mysql_rwlock_unlock(&LOCK_system_variables_hash);
mysql_prlock_unlock(&LOCK_system_variables_hash);
LEX *lex= thd ? thd->lex : 0;
if (!(plugin= intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
var= NULL; /* failed to lock it, it must be uninstalling */
@ -2814,7 +2814,7 @@ sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
}
}
else
mysql_rwlock_unlock(&LOCK_system_variables_hash);
mysql_prlock_unlock(&LOCK_system_variables_hash);
if (!locked)
mysql_mutex_unlock(&LOCK_plugin);
@ -3043,9 +3043,9 @@ static uchar *intern_sys_var_ptr(THD* thd, int offset, bool global_lock)
if (!thd->variables.dynamic_variables_ptr ||
(uint)offset > thd->variables.dynamic_variables_head)
{
mysql_rwlock_rdlock(&LOCK_system_variables_hash);
mysql_prlock_rdlock(&LOCK_system_variables_hash);
sync_dynamic_session_variables(thd, global_lock);
mysql_rwlock_unlock(&LOCK_system_variables_hash);
mysql_prlock_unlock(&LOCK_system_variables_hash);
}
DBUG_RETURN((uchar*)thd->variables.dynamic_variables_ptr + offset);
}
@ -3160,7 +3160,7 @@ static void cleanup_variables(struct system_variables *vars)
st_bookmark *v;
uint idx;
mysql_rwlock_rdlock(&LOCK_system_variables_hash);
mysql_prlock_rdlock(&LOCK_system_variables_hash);
for (idx= 0; idx < bookmark_hash.records; idx++)
{
v= (st_bookmark*) my_hash_element(&bookmark_hash, idx);
@ -3179,7 +3179,7 @@ static void cleanup_variables(struct system_variables *vars)
*ptr= NULL;
}
}
mysql_rwlock_unlock(&LOCK_system_variables_hash);
mysql_prlock_unlock(&LOCK_system_variables_hash);
DBUG_ASSERT(vars->table_plugin == NULL);
DBUG_ASSERT(vars->tmp_table_plugin == NULL);
@ -4234,10 +4234,10 @@ int thd_key_create(MYSQL_THD_KEY_T *key)
PLUGIN_VAR_NOSYSVAR | PLUGIN_VAR_NOCMDOPT;
char namebuf[256];
snprintf(namebuf, sizeof(namebuf), "%u", thd_key_no++);
mysql_rwlock_wrlock(&LOCK_system_variables_hash);
mysql_prlock_wrlock(&LOCK_system_variables_hash);
// non-letters in the name as an extra safety
st_bookmark *bookmark= register_var("\a\v\a\t\a\r", namebuf, flags);
mysql_rwlock_unlock(&LOCK_system_variables_hash);
mysql_prlock_unlock(&LOCK_system_variables_hash);
if (bookmark)
{
*key= bookmark->offset;