mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-10892 - rpl.rpl_semi_sync_uninstall_plugin fails with Assertion `0' failure
in buildbot Removed plugin_array_version: it is being checked without mutex protection and thus is prone to data race and race conditions. In effect plugins are not protected from concurrent destruction. Removed state_mask inversion: doesn't seem to make any sense. When collecting local plugins list, only add plugins that match state_mask. Use plugin ref counting to protect against concurrent plugin destruction.
This commit is contained in:
@ -194,7 +194,6 @@ static DYNAMIC_ARRAY plugin_array;
|
|||||||
static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
|
static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM];
|
||||||
static MEM_ROOT plugin_mem_root;
|
static MEM_ROOT plugin_mem_root;
|
||||||
static bool reap_needed= false;
|
static bool reap_needed= false;
|
||||||
static int plugin_array_version=0;
|
|
||||||
|
|
||||||
static bool initialized= 0;
|
static bool initialized= 0;
|
||||||
|
|
||||||
@ -312,7 +311,6 @@ static void plugin_vars_free_values(sys_var *vars);
|
|||||||
static void restore_pluginvar_names(sys_var *first);
|
static void restore_pluginvar_names(sys_var *first);
|
||||||
static void plugin_opt_set_limits(struct my_option *,
|
static void plugin_opt_set_limits(struct my_option *,
|
||||||
const struct st_mysql_sys_var *);
|
const struct st_mysql_sys_var *);
|
||||||
static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
|
|
||||||
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
|
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
|
||||||
static void reap_plugins(void);
|
static void reap_plugins(void);
|
||||||
|
|
||||||
@ -924,14 +922,16 @@ SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc)
|
static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc,
|
||||||
|
uint state_mask= PLUGIN_IS_READY |
|
||||||
|
PLUGIN_IS_UNINITIALIZED)
|
||||||
{
|
{
|
||||||
st_plugin_int *pi= plugin_ref_to_int(rc);
|
st_plugin_int *pi= plugin_ref_to_int(rc);
|
||||||
DBUG_ENTER("intern_plugin_lock");
|
DBUG_ENTER("intern_plugin_lock");
|
||||||
|
|
||||||
mysql_mutex_assert_owner(&LOCK_plugin);
|
mysql_mutex_assert_owner(&LOCK_plugin);
|
||||||
|
|
||||||
if (pi->state & (PLUGIN_IS_READY | PLUGIN_IS_UNINITIALIZED))
|
if (pi->state & state_mask)
|
||||||
{
|
{
|
||||||
plugin_ref plugin;
|
plugin_ref plugin;
|
||||||
#ifdef DBUG_OFF
|
#ifdef DBUG_OFF
|
||||||
@ -1111,7 +1111,6 @@ static bool plugin_add(MEM_ROOT *tmp_root,
|
|||||||
|
|
||||||
if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
|
if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp)))
|
||||||
goto err;
|
goto err;
|
||||||
plugin_array_version++;
|
|
||||||
if (my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
|
if (my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr))
|
||||||
tmp_plugin_ptr->state= PLUGIN_IS_FREED;
|
tmp_plugin_ptr->state= PLUGIN_IS_FREED;
|
||||||
init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096);
|
init_alloc_root(&tmp_plugin_ptr->mem_root, 4096, 4096);
|
||||||
@ -1210,7 +1209,6 @@ static void plugin_del(struct st_plugin_int *plugin)
|
|||||||
if (plugin->plugin_dl)
|
if (plugin->plugin_dl)
|
||||||
plugin_dl_del(&plugin->plugin_dl->dl);
|
plugin_dl_del(&plugin->plugin_dl->dl);
|
||||||
plugin->state= PLUGIN_IS_FREED;
|
plugin->state= PLUGIN_IS_FREED;
|
||||||
plugin_array_version++;
|
|
||||||
free_root(&plugin->mem_root, MYF(0));
|
free_root(&plugin->mem_root, MYF(0));
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
@ -2297,64 +2295,55 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name,
|
|||||||
bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
|
bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
|
||||||
int type, uint state_mask, void *arg)
|
int type, uint state_mask, void *arg)
|
||||||
{
|
{
|
||||||
uint idx, total;
|
uint idx, total= 0;
|
||||||
struct st_plugin_int *plugin, **plugins;
|
struct st_plugin_int *plugin;
|
||||||
int version=plugin_array_version;
|
plugin_ref *plugins;
|
||||||
|
my_bool res= FALSE;
|
||||||
DBUG_ENTER("plugin_foreach_with_mask");
|
DBUG_ENTER("plugin_foreach_with_mask");
|
||||||
|
|
||||||
if (!initialized)
|
if (!initialized)
|
||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(FALSE);
|
||||||
|
|
||||||
state_mask= ~state_mask; // do it only once
|
|
||||||
|
|
||||||
mysql_mutex_lock(&LOCK_plugin);
|
mysql_mutex_lock(&LOCK_plugin);
|
||||||
total= type == MYSQL_ANY_PLUGIN ? plugin_array.elements
|
|
||||||
: plugin_hash[type].records;
|
|
||||||
/*
|
/*
|
||||||
Do the alloca out here in case we do have a working alloca:
|
Do the alloca out here in case we do have a working alloca:
|
||||||
leaving the nested stack frame invalidates alloca allocation.
|
leaving the nested stack frame invalidates alloca allocation.
|
||||||
*/
|
*/
|
||||||
plugins=(struct st_plugin_int **)my_alloca(total*sizeof(plugin));
|
|
||||||
if (type == MYSQL_ANY_PLUGIN)
|
if (type == MYSQL_ANY_PLUGIN)
|
||||||
{
|
{
|
||||||
for (idx= 0; idx < total; idx++)
|
plugins= (plugin_ref*) my_alloca(plugin_array.elements * sizeof(plugin_ref));
|
||||||
|
for (idx= 0; idx < plugin_array.elements; idx++)
|
||||||
{
|
{
|
||||||
plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
|
plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **);
|
||||||
plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL;
|
if ((plugins[total]= intern_plugin_lock(0, plugin_int_to_ref(plugin),
|
||||||
|
state_mask)))
|
||||||
|
total++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
HASH *hash= plugin_hash + type;
|
HASH *hash= plugin_hash + type;
|
||||||
for (idx= 0; idx < total; idx++)
|
plugins= (plugin_ref*) my_alloca(hash->records * sizeof(plugin_ref));
|
||||||
|
for (idx= 0; idx < hash->records; idx++)
|
||||||
{
|
{
|
||||||
plugin= (struct st_plugin_int *) my_hash_element(hash, idx);
|
plugin= (struct st_plugin_int *) my_hash_element(hash, idx);
|
||||||
plugins[idx]= !(plugin->state & state_mask) ? plugin : NULL;
|
if ((plugins[total]= intern_plugin_lock(0, plugin_int_to_ref(plugin),
|
||||||
|
state_mask)))
|
||||||
|
total++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mysql_mutex_unlock(&LOCK_plugin);
|
mysql_mutex_unlock(&LOCK_plugin);
|
||||||
|
|
||||||
for (idx= 0; idx < total; idx++)
|
for (idx= 0; idx < total; idx++)
|
||||||
{
|
{
|
||||||
if (unlikely(version != plugin_array_version))
|
|
||||||
{
|
|
||||||
mysql_mutex_lock(&LOCK_plugin);
|
|
||||||
for (uint i=idx; i < total; i++)
|
|
||||||
if (plugins[i] && plugins[i]->state & state_mask)
|
|
||||||
plugins[i]=0;
|
|
||||||
mysql_mutex_unlock(&LOCK_plugin);
|
|
||||||
}
|
|
||||||
plugin= plugins[idx];
|
|
||||||
/* It will stop iterating on first engine error when "func" returns TRUE */
|
/* It will stop iterating on first engine error when "func" returns TRUE */
|
||||||
if (plugin && func(thd, plugin_int_to_ref(plugin), arg))
|
if ((res= func(thd, plugins[idx], arg)))
|
||||||
goto err;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plugin_unlock_list(0, plugins, total);
|
||||||
my_afree(plugins);
|
my_afree(plugins);
|
||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(res);
|
||||||
err:
|
|
||||||
my_afree(plugins);
|
|
||||||
DBUG_RETURN(TRUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ int fill_plugins(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
TABLE *table= tables->table;
|
TABLE *table= tables->table;
|
||||||
|
|
||||||
if (plugin_foreach_with_mask(thd, show_plugins, MYSQL_ANY_PLUGIN,
|
if (plugin_foreach_with_mask(thd, show_plugins, MYSQL_ANY_PLUGIN,
|
||||||
~PLUGIN_IS_FREED, table))
|
~(PLUGIN_IS_FREED | PLUGIN_IS_DYING), table))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
@ -5294,7 +5294,8 @@ int fill_schema_engines(THD *thd, TABLE_LIST *tables, COND *cond)
|
|||||||
DBUG_ENTER("fill_schema_engines");
|
DBUG_ENTER("fill_schema_engines");
|
||||||
if (plugin_foreach_with_mask(thd, iter_schema_engines,
|
if (plugin_foreach_with_mask(thd, iter_schema_engines,
|
||||||
MYSQL_STORAGE_ENGINE_PLUGIN,
|
MYSQL_STORAGE_ENGINE_PLUGIN,
|
||||||
~PLUGIN_IS_FREED, tables->table))
|
~(PLUGIN_IS_FREED | PLUGIN_IS_DYING),
|
||||||
|
tables->table))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user