diff options
author | Alexey Botchkov <holyfoot@askmonty.org> | 2020-08-11 00:01:53 +0400 |
---|---|---|
committer | Alexey Botchkov <holyfoot@askmonty.org> | 2020-08-11 00:01:53 +0400 |
commit | 19bdb03e97d6f760699cd718e11f71e01c268f5e (patch) | |
tree | 3399b856c7bc246e3faec3cb1aad40badac8764a | |
parent | 0041dacc1b8e85e1958355d1cfdc36055b05a884 (diff) | |
download | mariadb-git-bb-mdev21211-hf.tar.gz |
MDEV-21211 LOCK_plugin optimization.bb-mdev21211-hf
Use Apc_target calls to 'notify' threads about changes in plugins.
-rw-r--r-- | sql/sql_explain.cc | 26 | ||||
-rw-r--r-- | sql/sql_explain.h | 8 | ||||
-rw-r--r-- | sql/sql_lex.cc | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 4 | ||||
-rw-r--r-- | sql/sql_plugin.cc | 421 | ||||
-rw-r--r-- | sql/sql_plugin.h | 5 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_show.cc | 6 | ||||
-rw-r--r-- | sql/sql_tvc.cc | 2 |
9 files changed, 234 insertions, 242 deletions
diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 645c1618a8a..f45751d86c5 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -40,10 +40,10 @@ const char *pushed_select_text= "PUSHED SELECT"; static void write_item(Json_writer *writer, Item *item); static void append_item_to_str(String *out, Item *item); -Explain_query::Explain_query(THD *thd_arg, MEM_ROOT *root) : +Explain_query::Explain_query(THD *thd, MEM_ROOT *root) : mem_root(root), upd_del_plan(NULL), insert_plan(NULL), - unions(root), selects(root), thd(thd_arg), apc_enabled(false), - operations(0) + unions(root), selects(root), query_plan_ready(false), + m_thd(thd), operations(0) { } @@ -62,9 +62,6 @@ static void print_json_array(Json_writer *writer, Explain_query::~Explain_query() { - if (apc_enabled) - thd->apc_target.disable(); - delete upd_del_plan; delete insert_plan; uint i; @@ -139,24 +136,17 @@ void Explain_query::add_node(Explain_node *node) void Explain_query::add_insert_plan(Explain_insert *insert_plan_arg) { insert_plan= insert_plan_arg; - query_plan_ready(); + query_plan_set_ready(); } void Explain_query::add_upd_del_plan(Explain_update *upd_del_plan_arg) { upd_del_plan= upd_del_plan_arg; - query_plan_ready(); + query_plan_set_ready(); } -void Explain_query::query_plan_ready() -{ - if (!apc_enabled) - thd->apc_target.enable(); - apc_enabled= true; -} - /* Send EXPLAIN output to the client. */ @@ -237,9 +227,9 @@ void Explain_query::print_explain_json(select_result_sink *output, CHARSET_INFO *cs= system_charset_info; List<Item> item_list; const String *buf= writer.output.get_string(); - item_list.push_back(new (thd->mem_root) - Item_string(thd, buf->ptr(), buf->length(), cs), - thd->mem_root); + item_list.push_back(new (m_thd->mem_root) + Item_string(m_thd, buf->ptr(), buf->length(), cs), + m_thd->mem_root); output->send_data(item_list); } diff --git a/sql/sql_explain.h b/sql/sql_explain.h index ce3f3ef06e1..c9ad0ac4e0c 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -482,7 +482,8 @@ public: /* If true, at least part of EXPLAIN can be printed */ bool have_query_plan() { return insert_plan || upd_del_plan|| get_node(1) != NULL; } - void query_plan_ready(); + void query_plan_set_ready() { query_plan_ready= true; } + bool query_plan_is_ready() const { return query_plan_ready; } MEM_ROOT *mem_root; @@ -496,9 +497,10 @@ private: Dynamic_array<Explain_union*> unions; Dynamic_array<Explain_select*> selects; + + bool query_plan_ready; + THD *m_thd; //thd->mem_root to be used in print_explain_json - THD *thd; // for APC start/stop - bool apc_enabled; /* Debugging aid: count how many times add_node() was called. Ideally, it should be one, we currently allow O(1) query plan saves for each diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index f8b93abd4ae..b174b88bd6e 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5221,7 +5221,7 @@ int st_select_lex_unit::save_union_explain(Explain_query *output) output->add_node(eu); if (eu->get_select_id() == 1) - output->query_plan_ready(); + output->query_plan_set_ready(); return 0; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index bf33148811e..1f38c55b922 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1348,10 +1348,10 @@ bool do_command(THD *thd) my_net_set_read_timeout(net, thd->variables.net_read_timeout); DBUG_ASSERT(packet_length); - DBUG_ASSERT(!thd->apc_target.is_enabled()); + thd->apc_target.enable(); return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1), FALSE, FALSE); - DBUG_ASSERT(!thd->apc_target.is_enabled()); + thd->apc_target.disable(); out: thd->lex->restore_set_statement_var(); diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 981420f03ae..84438e3ff70 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -217,17 +217,28 @@ static struct #include "sql_plugin_services.ic" /* - A mutex LOCK_plugin must be acquired before accessing the - following variables/structures. - We are always manipulating ref count, so a rwlock here is unneccessary. + A mutex LOCK_plugin must be acquired when we change the + new_plugins_state content and when we swap the global_plugins_state + and the new_plugins_state. */ mysql_mutex_t LOCK_plugin; +struct st_plugins_state +{ + DYNAMIC_ARRAY m_array; + HASH m_hash[MYSQL_MAX_PLUGIN_TYPE_NUM]; +#ifndef DBUG_OOF + uint m_ref_counter; +#endif +}; + static DYNAMIC_ARRAY plugin_dl_array; -static DYNAMIC_ARRAY plugin_array; -static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM]; static MEM_ROOT plugin_mem_root; static bool reap_needed= false; volatile int global_plugin_version= 1; +static st_plugins_state plugins_states[2]; +st_plugins_state *global_plugins_state= plugins_states; +static st_plugins_state *new_plugins_state= plugins_states + 1; + static bool initialized= 0; ulong dlopen_count; @@ -871,7 +882,8 @@ static void plugin_dl_del(struct st_plugin_dl *plugin_dl) } -static struct st_plugin_int *plugin_find_internal(const LEX_CSTRING *name, +static struct st_plugin_int *plugin_find_internal(st_plugins_state *st, + const LEX_CSTRING *name, int type) { uint i; @@ -879,21 +891,19 @@ static struct st_plugin_int *plugin_find_internal(const LEX_CSTRING *name, if (! initialized) DBUG_RETURN(0); - mysql_mutex_assert_owner(&LOCK_plugin); - if (type == MYSQL_ANY_PLUGIN) { for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) { struct st_plugin_int *plugin= (st_plugin_int *) - my_hash_search(&plugin_hash[i], (const uchar *)name->str, name->length); + my_hash_search(&st->m_hash[i], (const uchar *)name->str, name->length); if (plugin) DBUG_RETURN(plugin); } } else DBUG_RETURN((st_plugin_int *) - my_hash_search(&plugin_hash[type], (const uchar *)name->str, + my_hash_search(&st->m_hash[type], (const uchar *)name->str, name->length)); DBUG_RETURN(0); } @@ -903,32 +913,20 @@ static SHOW_COMP_OPTION plugin_status(const LEX_CSTRING *name, int type) { SHOW_COMP_OPTION rc= SHOW_OPTION_NO; struct st_plugin_int *plugin; - DBUG_ENTER("plugin_is_ready"); - mysql_mutex_lock(&LOCK_plugin); - if ((plugin= plugin_find_internal(name, type))) + DBUG_ENTER("plugin_status"); + if ((plugin= plugin_find_internal(global_plugins_state, name, type))) { rc= SHOW_OPTION_DISABLED; if (plugin->state == PLUGIN_IS_READY) rc= SHOW_OPTION_YES; } - mysql_mutex_unlock(&LOCK_plugin); DBUG_RETURN(rc); } bool plugin_is_ready(const LEX_CSTRING *name, int type) { - bool rc= FALSE; - if (plugin_status(name, type) == SHOW_OPTION_YES) - rc= TRUE; - return rc; -} - - -SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type) -{ - LEX_CSTRING plugin_name= { name, len }; - return plugin_status(&plugin_name, type); + return plugin_status(name, type) == SHOW_OPTION_YES; } @@ -944,8 +942,6 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc, st_plugin_int *pi= plugin_ref_to_int(rc); DBUG_ENTER("intern_plugin_lock"); - mysql_mutex_assert_owner(&LOCK_plugin); - if (pi->state & state_mask) { plugin_ref plugin; @@ -969,7 +965,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc, *plugin= pi; #endif - pi->ref_count++; + my_atomic_add32(&pi->ref_count, 1); DBUG_PRINT("lock",("thd: %p plugin: \"%s\" LOCK ref_count: %d", current_thd, pi->name.str, pi->ref_count)); @@ -1019,10 +1015,8 @@ plugin_ref plugin_lock(THD *thd, plugin_ref ptr) DBUG_RETURN(ptr); } #endif - mysql_mutex_lock(&LOCK_plugin); - plugin_ref_to_int(ptr)->locks_total++; + my_atomic_add32(&plugin_ref_to_int(ptr)->locks_total, 1); rc= intern_plugin_lock(lex, ptr); - mysql_mutex_unlock(&LOCK_plugin); DBUG_RETURN(rc); } @@ -1043,10 +1037,8 @@ plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type) plugin_ref rc= NULL; st_plugin_int *plugin; DBUG_ENTER("plugin_lock_by_name"); - mysql_mutex_lock(&LOCK_plugin); - if ((plugin= plugin_find_internal(name, type))) + if ((plugin= plugin_find_internal(global_plugins_state, name, type))) rc= intern_plugin_lock(lex, plugin_int_to_ref(plugin)); - mysql_mutex_unlock(&LOCK_plugin); DBUG_RETURN(rc); } @@ -1056,18 +1048,21 @@ static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin) uint i; struct st_plugin_int *tmp; DBUG_ENTER("plugin_insert_or_reuse"); - for (i= 0; i < plugin_array.elements; i++) + mysql_mutex_assert_owner(&LOCK_plugin); + for (i= 0; i < new_plugins_state->m_array.elements; i++) { - tmp= *dynamic_element(&plugin_array, i, struct st_plugin_int **); + tmp= *dynamic_element(&new_plugins_state->m_array, i, + struct st_plugin_int **); if (tmp->state == PLUGIN_IS_FREED) { memcpy(tmp, plugin, sizeof(struct st_plugin_int)); DBUG_RETURN(tmp); } } - if (insert_dynamic(&plugin_array, (uchar*)&plugin)) + if (insert_dynamic(&new_plugins_state->m_array, (uchar*)&plugin)) DBUG_RETURN(0); - tmp= *dynamic_element(&plugin_array, plugin_array.elements - 1, + tmp= *dynamic_element(&new_plugins_state->m_array, + new_plugins_state->m_array.elements - 1, struct st_plugin_int **)= (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)plugin, sizeof(struct st_plugin_int)); @@ -1088,7 +1083,9 @@ static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists, DBUG_ENTER("plugin_add"); DBUG_PRINT("enter", ("name: %s dl: %s", name->str, dl->str)); - if (name->str && plugin_find_internal(name, MYSQL_ANY_PLUGIN)) + mysql_mutex_assert_owner(&LOCK_plugin); + if (name->str && plugin_find_internal(new_plugins_state, name, + MYSQL_ANY_PLUGIN)) { if (if_not_exists) MyFlags|= ME_NOTE; @@ -1120,7 +1117,8 @@ static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists, continue; // plugin name doesn't match if (!name->str && - (maybe_dupe= plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN))) + (maybe_dupe= plugin_find_internal(new_plugins_state, + &tmp.name, MYSQL_ANY_PLUGIN))) { if (plugin->name != maybe_dupe->plugin->name) { @@ -1172,7 +1170,8 @@ static enum install_status plugin_add(MEM_ROOT *tmp_root, bool if_not_exists, if (!(tmp_plugin_ptr= plugin_insert_or_reuse(&tmp))) goto err; - if (my_hash_insert(&plugin_hash[plugin->type], (uchar*)tmp_plugin_ptr)) + if (my_hash_insert(&new_plugins_state->m_hash[plugin->type], + (uchar*)tmp_plugin_ptr)) tmp_plugin_ptr->state= PLUGIN_IS_FREED; init_alloc_root(&tmp_plugin_ptr->mem_root, "plugin", 4096, 4096, MYF(0)); @@ -1210,13 +1209,6 @@ static void plugin_variables_deinit(struct st_plugin_int *plugin) static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check) { - /* - we don't want to hold the LOCK_plugin mutex as it may cause - deinitialization to deadlock if plugins have worker threads - with plugin locks - */ - mysql_mutex_assert_not_owner(&LOCK_plugin); - if (plugin->plugin->status_vars) { /* @@ -1270,7 +1262,8 @@ static void plugin_del(struct st_plugin_int *plugin) restore_ptr_backup(plugin->nbackups, plugin->ptr_backup); if (plugin->plugin_dl) { - my_hash_delete(&plugin_hash[plugin->plugin->type], (uchar*)plugin); + my_hash_delete(&new_plugins_state->m_hash[plugin->plugin->type], + (uchar*)plugin); plugin_dl_del(plugin->plugin_dl); plugin->state= PLUGIN_IS_FREED; free_root(&plugin->mem_root, MYF(0)); @@ -1280,24 +1273,25 @@ static void plugin_del(struct st_plugin_int *plugin) DBUG_VOID_RETURN; } + static void reap_plugins(void) { uint count; struct st_plugin_int *plugin, **reap, **list; - mysql_mutex_assert_owner(&LOCK_plugin); - if (!reap_needed) return; + mysql_mutex_lock(&LOCK_plugin); reap_needed= false; - count= plugin_array.elements; + count= global_plugins_state->m_array.elements; reap= (struct st_plugin_int **)my_alloca(sizeof(plugin)*(count+1)); *(reap++)= NULL; for (uint i=0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) { - HASH *hash= plugin_hash + plugin_type_initialization_order[i]; + HASH *hash= global_plugins_state->m_hash + + plugin_type_initialization_order[i]; for (uint j= 0; j < hash->records; j++) { plugin= (struct st_plugin_int *) my_hash_element(hash, j); @@ -1310,28 +1304,26 @@ static void reap_plugins(void) } } - mysql_mutex_unlock(&LOCK_plugin); list= reap; while ((plugin= *(--list))) plugin_deinitialize(plugin, true); - mysql_mutex_lock(&LOCK_plugin); while ((plugin= *(--reap))) plugin_del(plugin); + mysql_mutex_unlock(&LOCK_plugin); my_afree(reap); } + static void intern_plugin_unlock(LEX *lex, plugin_ref plugin) { int i; st_plugin_int *pi; DBUG_ENTER("intern_plugin_unlock"); - mysql_mutex_assert_owner(&LOCK_plugin); - if (!plugin) DBUG_VOID_RETURN; @@ -1361,13 +1353,15 @@ static void intern_plugin_unlock(LEX *lex, plugin_ref plugin) } DBUG_ASSERT(pi->ref_count); - pi->ref_count--; + my_atomic_add32(&pi->ref_count, -1); DBUG_PRINT("lock",("thd: %p plugin: \"%s\" UNLOCK ref_count: %d", current_thd, pi->name.str, pi->ref_count)); if (pi->state == PLUGIN_IS_DELETED && !pi->ref_count) + { reap_needed= true; + } DBUG_VOID_RETURN; } @@ -1384,10 +1378,8 @@ void plugin_unlock(THD *thd, plugin_ref plugin) if (!plugin_dlib(plugin)) DBUG_VOID_RETURN; #endif - mysql_mutex_lock(&LOCK_plugin); intern_plugin_unlock(lex, plugin); reap_plugins(); - mysql_mutex_unlock(&LOCK_plugin); DBUG_VOID_RETURN; } @@ -1400,11 +1392,9 @@ void plugin_unlock_list(THD *thd, plugin_ref *list, uint count) DBUG_VOID_RETURN; DBUG_ASSERT(list); - mysql_mutex_lock(&LOCK_plugin); while (count--) intern_plugin_unlock(lex, *list++); reap_plugins(); - mysql_mutex_unlock(&LOCK_plugin); DBUG_VOID_RETURN; } @@ -1419,8 +1409,6 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin, uint state= plugin->state; DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED); - mysql_mutex_unlock(&LOCK_plugin); - mysql_prlock_wrlock(&LOCK_system_variables_hash); if (test_plugin_options(tmp_root, plugin, argc, argv)) state= PLUGIN_IS_DISABLED; @@ -1479,7 +1467,6 @@ err: if (ret) plugin_variables_deinit(plugin); - mysql_mutex_lock(&LOCK_plugin); plugin->state= state; DBUG_RETURN(ret); @@ -1542,6 +1529,38 @@ static void init_plugin_psi_keys(void) } #endif /* HAVE_PSI_INTERFACE */ + +static bool copy_plugins_state(st_plugins_state *dest, st_plugins_state *src) +{ + uint i; + + reset_dynamic(&dest->m_array); + for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) + my_hash_reset(&dest->m_hash[i]); + + for (i= 0; i < src->m_array.elements; i++) + { + struct st_plugin_int *ptr; + ptr= *dynamic_element(&src->m_array, i, struct st_plugin_int **); + if (insert_dynamic(&dest->m_array, (uchar *) &ptr) || + (ptr->state != PLUGIN_IS_FREED && + my_hash_insert(&dest->m_hash[ptr->plugin->type], + (uchar*) ptr))) + goto err; + } + + return false; +err: + return true; +} + + +static void set_new_global_plugins_state() +{ + swap_variables(st_plugins_state *, global_plugins_state, new_plugins_state); +} + + /* The logic is that we first load and initialize all compiled in plugins. From there we load up the dynamic types (assuming we have not been told to @@ -1579,18 +1598,22 @@ int plugin_init(int *argc, char **argv, int flags) Big enough to avoid many mallocs even in future */ if (my_init_dynamic_array(&plugin_dl_array, - sizeof(struct st_plugin_dl *), 16, 16, MYF(0)) || - my_init_dynamic_array(&plugin_array, - sizeof(struct st_plugin_int *), 80, 32, MYF(0))) + sizeof(struct st_plugin_dl *), 16, 16, MYF(0))) goto err; - for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) + for (int ps=0; ps < 2; ps++) { - if (my_hash_init(&plugin_hash[i], system_charset_info, 32, 0, 0, - get_plugin_hash_key, NULL, HASH_UNIQUE)) + struct st_plugins_state *st= plugins_states + ps; + if (my_init_dynamic_array(&st->m_array, + sizeof(struct st_plugin_int *), 80, 32, MYF(0))) goto err; + for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) + { + if (my_hash_init(st->m_hash + i, system_charset_info, 32, 0, 0, + get_plugin_hash_key, NULL, HASH_UNIQUE)) + goto err; + } } - /* prepare debug_sync service */ DBUG_ASSERT(strcmp(list_of_services[1].name, "debug_sync_service") == 0); list_of_services[1].service= *(void**)&debug_sync_C_callback_ptr; @@ -1652,7 +1675,8 @@ int plugin_init(int *argc, char **argv, int flags) First, we initialize only MyISAM - that should almost always succeed (almost always, because plugins can be loaded outside of the server, too). */ - plugin_ptr= plugin_find_internal(&MyISAM, MYSQL_STORAGE_ENGINE_PLUGIN); + plugin_ptr= plugin_find_internal(new_plugins_state, &MyISAM, + MYSQL_STORAGE_ENGINE_PLUGIN); DBUG_ASSERT(plugin_ptr || !mysql_mandatory_plugins[0]); if (plugin_ptr) { @@ -1670,7 +1694,6 @@ int plugin_init(int *argc, char **argv, int flags) DBUG_SLOW_ASSERT(plugin_ptr->ref_count == 1); } - mysql_mutex_unlock(&LOCK_plugin); /* Register (not initialize!) all dynamic plugins */ if (!(flags & PLUGIN_INIT_SKIP_DYNAMIC_LOADING)) @@ -1703,15 +1726,15 @@ int plugin_init(int *argc, char **argv, int flags) Now we initialize all remaining plugins */ - mysql_mutex_lock(&LOCK_plugin); - reap= (st_plugin_int **) my_alloca((plugin_array.elements+1) * sizeof(void*)); + reap= (st_plugin_int **) my_alloca((new_plugins_state->m_array.elements+1) * + sizeof(void*)); *(reap++)= NULL; for(;;) { for (i=0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) { - HASH *hash= plugin_hash + plugin_type_initialization_order[i]; + HASH *hash= new_plugins_state->m_hash + plugin_type_initialization_order[i]; for (uint idx= 0; idx < hash->records; idx++) { plugin_ptr= (struct st_plugin_int *) my_hash_element(hash, idx); @@ -1731,10 +1754,8 @@ int plugin_init(int *argc, char **argv, int flags) if (flags & PLUGIN_INIT_SKIP_PLUGIN_TABLE) break; - mysql_mutex_unlock(&LOCK_plugin); plugin_load(&tmp_root); flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE; - mysql_mutex_lock(&LOCK_plugin); } /* @@ -1742,11 +1763,9 @@ int plugin_init(int *argc, char **argv, int flags) */ while ((plugin_ptr= *(--reap))) { - mysql_mutex_unlock(&LOCK_plugin); if (plugin_is_forced(plugin_ptr)) reaped_mandatory_plugin= TRUE; plugin_deinitialize(plugin_ptr, true); - mysql_mutex_lock(&LOCK_plugin); plugin_del(plugin_ptr); } @@ -1756,6 +1775,7 @@ int plugin_init(int *argc, char **argv, int flags) goto err; free_root(&tmp_root, MYF(0)); + set_new_global_plugins_state(); DBUG_RETURN(0); @@ -1775,15 +1795,16 @@ static bool register_builtin(struct st_maria_plugin *plugin, tmp->ref_count= 0; tmp->plugin_dl= 0; - if (insert_dynamic(&plugin_array, (uchar*)&tmp)) + if (insert_dynamic(&new_plugins_state->m_array, (uchar*)&tmp)) DBUG_RETURN(1); - *ptr= *dynamic_element(&plugin_array, plugin_array.elements - 1, + *ptr= *dynamic_element(&new_plugins_state->m_array, + new_plugins_state->m_array.elements - 1, struct st_plugin_int **)= (struct st_plugin_int *) memdup_root(&plugin_mem_root, (uchar*)tmp, sizeof(struct st_plugin_int)); - if (my_hash_insert(&plugin_hash[plugin->type],(uchar*) *ptr)) + if (my_hash_insert(&new_plugins_state->m_hash[plugin->type],(uchar*) *ptr)) DBUG_RETURN(1); DBUG_RETURN(0); @@ -1801,8 +1822,6 @@ static void plugin_load(MEM_ROOT *tmp_root) int error; THD *new_thd= new THD(0); bool result; - unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] = - { MYSQL_AUDIT_GENERAL_CLASSMASK }; DBUG_ENTER("plugin_load"); if (global_system_variables.log_warnings >= 9) @@ -1815,7 +1834,9 @@ static void plugin_load(MEM_ROOT *tmp_root) tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_READ); tables.open_strategy= TABLE_LIST::OPEN_NORMAL; + set_new_global_plugins_state(); result= open_and_lock_tables(new_thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT); + set_new_global_plugins_state(); table= tables.table; if (result) @@ -1852,51 +1873,30 @@ static void plugin_load(MEM_ROOT *tmp_root) continue; /* - Pre-acquire audit plugins for events that may potentially occur - during [UN]INSTALL PLUGIN. - - When audit event is triggered, audit subsystem acquires interested - plugins by walking through plugin list. Evidently plugin list - iterator protects plugin list by acquiring LOCK_plugin, see - plugin_foreach_with_mask(). - - On the other hand plugin_load is acquiring LOCK_plugin - rather for a long time. - - When audit event is triggered during plugin_load plugin - list iterator acquires the same lock (within the same thread) - second time. - - This hack should be removed when LOCK_plugin is fixed so it - protects only what it supposed to protect. - - See also mysql_install_plugin(), mysql_uninstall_plugin() and - initialize_audit_plugin() - */ - if (mysql_audit_general_enabled()) - mysql_audit_acquire_plugins(new_thd, event_class_mask); - - /* there're no other threads running yet, so we don't need a mutex. but plugin_add() before is designed to work in multi-threaded environment, and it uses mysql_mutex_assert_owner(), so we lock the mutex here to satisfy the assert */ - mysql_mutex_lock(&LOCK_plugin); plugin_add(tmp_root, false, &name, &dl, MYF(ME_ERROR_LOG)); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); - mysql_mutex_unlock(&LOCK_plugin); } if (unlikely(error > 0)) sql_print_error(ER_THD(new_thd, ER_GET_ERRNO), my_errno, table->file->table_type()); + set_new_global_plugins_state(); end_read_record(&read_record_info); table->mark_table_for_reopen(); close_mysql_tables(new_thd); -end: +err_ret: new_thd->db= null_clex_str; // Avoid free on thd->db delete new_thd; + set_new_global_plugins_state(); DBUG_VOID_RETURN; + +end: + set_new_global_plugins_state(); + goto err_ret; } @@ -1935,7 +1935,6 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) } dl= name; - mysql_mutex_lock(&LOCK_plugin); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); name.str= 0; // load everything if (plugin_add(tmp_root, false, &name, &dl, @@ -1945,12 +1944,10 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) else { free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); - mysql_mutex_lock(&LOCK_plugin); if (plugin_add(tmp_root, false, &name, &dl, MYF(ME_ERROR_LOG)) != INSTALL_GOOD) goto error; } - mysql_mutex_unlock(&LOCK_plugin); name.length= dl.length= 0; dl.str= NULL; name.str= p= buffer; str= &name; @@ -1972,7 +1969,6 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) } DBUG_RETURN(FALSE); error: - mysql_mutex_unlock(&LOCK_plugin); if (name.str) sql_print_error("Couldn't load plugin '%s' from '%s'.", name.str, dl.str); @@ -1984,7 +1980,7 @@ error: void plugin_shutdown(void) { - uint i, count= plugin_array.elements; + uint i, count; struct st_plugin_int **plugins, *plugin; struct st_plugin_dl **dl; DBUG_ENTER("plugin_shutdown"); @@ -1997,8 +1993,6 @@ void plugin_shutdown(void) opt_gtid_pos_auto_plugins= NULL; } - mysql_mutex_lock(&LOCK_plugin); - reap_needed= true; /* @@ -2008,12 +2002,13 @@ void plugin_shutdown(void) TODO: Have an additional step here to notify all active plugins that shutdown is requested to allow plugins to deinitialize in parallel. */ - while (reap_needed && (count= plugin_array.elements)) + while (reap_needed && (count= global_plugins_state->m_array.elements)) { reap_plugins(); for (i= 0; i < count; i++) { - plugin= *dynamic_element(&plugin_array, i, struct st_plugin_int **); + plugin= *dynamic_element(&global_plugins_state->m_array, i, + struct st_plugin_int **); if (plugin->state == PLUGIN_IS_READY) { plugin->state= PLUGIN_IS_DELETED; @@ -2032,18 +2027,20 @@ void plugin_shutdown(void) plugins= (struct st_plugin_int **) my_alloca(sizeof(void*) * (count+1)); + mysql_mutex_lock(&LOCK_plugin); /* If we have any plugins which did not die cleanly, we force shutdown */ for (i= 0; i < count; i++) { - plugins[i]= *dynamic_element(&plugin_array, i, struct st_plugin_int **); + plugins[i]= *dynamic_element(&global_plugins_state->m_array, i, + struct st_plugin_int **); /* change the state to ensure no reaping races */ if (plugins[i]->state == PLUGIN_IS_DELETED) plugins[i]->state= PLUGIN_IS_DYING; } - mysql_mutex_unlock(&LOCK_plugin); + mysql_mutex_unlock(&LOCK_plugin); /* We loop through all plugins and call deinit() if they have one. */ @@ -2058,13 +2055,8 @@ void plugin_shutdown(void) plugin_deinitialize(plugins[i], false); } - /* - It's perfectly safe not to lock LOCK_plugin, as there're no - concurrent threads anymore. But some functions called from here - use mysql_mutex_assert_owner(), so we lock the mutex to satisfy it - */ - mysql_mutex_lock(&LOCK_plugin); + mysql_mutex_lock(&LOCK_plugin); /* We defer checking ref_counts until after all plugins are deinitialized as some may have worker threads holding on to plugin references. @@ -2078,6 +2070,7 @@ void plugin_shutdown(void) plugins[i]->state & PLUGIN_IS_DISABLED) plugin_del(plugins[i]); } + mysql_mutex_unlock(&LOCK_plugin); /* Now we can deallocate all memory. @@ -2085,7 +2078,6 @@ void plugin_shutdown(void) cleanup_variables(&global_system_variables); cleanup_variables(&max_system_variables); - mysql_mutex_unlock(&LOCK_plugin); initialized= 0; mysql_mutex_destroy(&LOCK_plugin); @@ -2095,9 +2087,13 @@ void plugin_shutdown(void) /* Dispose of the memory */ - for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) - my_hash_free(&plugin_hash[i]); - delete_dynamic(&plugin_array); + for (int ps=0; ps < 2; ps++) + { + struct st_plugins_state *st= plugins_states + ps; + for (i= 0; i < MYSQL_MAX_PLUGIN_TYPE_NUM; i++) + my_hash_free(&st->m_hash[i]); + delete_dynamic(&st->m_array); + } count= plugin_dl_array.elements; dl= (struct st_plugin_dl **)my_alloca(sizeof(void*) * count); @@ -2125,7 +2121,8 @@ void plugin_shutdown(void) static bool finalize_install(THD *thd, TABLE *table, const LEX_CSTRING *name, int *argc, char **argv) { - struct st_plugin_int *tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN); + struct st_plugin_int *tmp= plugin_find_internal(new_plugins_state, name, + MYSQL_ANY_PLUGIN); int error; DBUG_ASSERT(tmp); mysql_mutex_assert_owner(&LOCK_plugin); // because of tmp->state @@ -2176,6 +2173,44 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_CSTRING *name, return 0; } + +/* + Request object for ending the new_plugins_state change. +*/ + +class Ping_all_threads_request : public Apc_target::Apc_call +{ +public: + /* Overloaded virtual functions. */ + void call_in_target_thread(); +}; + + +void Ping_all_threads_request::call_in_target_thread() +{ + /* Do nothing. We just make sure the thread got to this point. */ +} + + +static my_bool fix_plugins_state_callback(THD *thd, THD *caller_thd) +{ + bool timed_out; + int timeout_sec= 30; + + Ping_all_threads_request ping_req; + + mysql_mutex_lock(&thd->LOCK_thd_kill); + if (thd->get_command() == COM_SLEEP) + { + mysql_mutex_unlock(&thd->LOCK_thd_kill); + return FALSE; + } + thd->apc_target.make_apc_call(caller_thd, &ping_req, + timeout_sec, &timed_out); + return FALSE; +} + + bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, const LEX_CSTRING *dl_arg) { @@ -2185,8 +2220,6 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, enum install_status error; int argc=orig_argc; char **argv=orig_argv; - unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] = - { MYSQL_AUDIT_GENERAL_CLASSMASK }; DBUG_ENTER("mysql_install_plugin"); tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_WRITE); @@ -2205,35 +2238,22 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, DBUG_RETURN(TRUE); } - /* - Pre-acquire audit plugins for events that may potentially occur - during [UN]INSTALL PLUGIN. - - When audit event is triggered, audit subsystem acquires interested - plugins by walking through plugin list. Evidently plugin list - iterator protects plugin list by acquiring LOCK_plugin, see - plugin_foreach_with_mask(). - - On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin - rather for a long time. - - When audit event is triggered during [UN]INSTALL PLUGIN, plugin - list iterator acquires the same lock (within the same thread) - second time. - - This hack should be removed when LOCK_plugin is fixed so it - protects only what it supposed to protect. + mysql_mutex_lock(&LOCK_plugin); - See also mysql_uninstall_plugin() and initialize_audit_plugin() - */ - if (mysql_audit_general_enabled()) - mysql_audit_acquire_plugins(thd, event_class_mask); + if (unlikely(copy_plugins_state(new_plugins_state, global_plugins_state))) + { + error= INSTALL_FAIL_NOT_OK; + mysql_mutex_unlock(&LOCK_plugin); + goto err; + } - mysql_mutex_lock(&LOCK_plugin); error= plugin_add(thd->mem_root, thd->lex->create_info.if_not_exists(), name, &dl, MYF(0)); if (unlikely(error != INSTALL_GOOD)) + { + mysql_mutex_unlock(&LOCK_plugin); goto err; + } if (name->str) error= finalize_install(thd, table, name, &argc, argv) @@ -2252,12 +2272,19 @@ bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, if (unlikely(error != INSTALL_GOOD)) { + mysql_mutex_unlock(&LOCK_plugin); reap_needed= true; reap_plugins(); + goto err; } -err: global_plugin_version++; + set_new_global_plugins_state(); + thd->apc_target.disable(); mysql_mutex_unlock(&LOCK_plugin); + server_threads.iterate(fix_plugins_state_callback, thd); + thd->apc_target.enable(); + +err: if (argv) free_defaults(argv); DBUG_RETURN(error == INSTALL_FAIL_NOT_OK); @@ -2273,7 +2300,8 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name) struct st_plugin_int *plugin; mysql_mutex_assert_owner(&LOCK_plugin); - if (!(plugin= plugin_find_internal(name, MYSQL_ANY_PLUGIN)) || + if (!(plugin= plugin_find_internal(global_plugins_state, + name, MYSQL_ANY_PLUGIN)) || plugin->state & (PLUGIN_IS_UNINITIALIZED | PLUGIN_IS_DYING)) { // maybe plugin is present in mysql.plugin; postpone the error @@ -2341,8 +2369,6 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, TABLE_LIST tables; LEX_CSTRING dl= *dl_arg; bool error= false; - unsigned long event_class_mask[MYSQL_AUDIT_CLASS_MASK_SIZE] = - { MYSQL_AUDIT_GENERAL_CLASSMASK }; DBUG_ENTER("mysql_uninstall_plugin"); tables.init_one_table(&MYSQL_SCHEMA_NAME, &MYSQL_PLUGIN_NAME, 0, TL_WRITE); @@ -2366,30 +2392,6 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, DBUG_RETURN(TRUE); } - /* - Pre-acquire audit plugins for events that may potentially occur - during [UN]INSTALL PLUGIN. - - When audit event is triggered, audit subsystem acquires interested - plugins by walking through plugin list. Evidently plugin list - iterator protects plugin list by acquiring LOCK_plugin, see - plugin_foreach_with_mask(). - - On the other hand [UN]INSTALL PLUGIN is acquiring LOCK_plugin - rather for a long time. - - When audit event is triggered during [UN]INSTALL PLUGIN, plugin - list iterator acquires the same lock (within the same thread) - second time. - - This hack should be removed when LOCK_plugin is fixed so it - protects only what it supposed to protect. - - See also mysql_install_plugin() and initialize_audit_plugin() - */ - if (mysql_audit_general_enabled()) - mysql_audit_acquire_plugins(thd, event_class_mask); - mysql_mutex_lock(&LOCK_plugin); if (name->str) @@ -2414,10 +2416,12 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, error|= !MyFlags; } } - reap_plugins(); - global_plugin_version++; + thd->apc_target.disable(); mysql_mutex_unlock(&LOCK_plugin); + server_threads.iterate(fix_plugins_state_callback, thd); + reap_plugins(); + thd->apc_target.enable(); DBUG_RETURN(error); #ifdef WITH_WSREP wsrep_error_label: @@ -2438,17 +2442,17 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, if (!initialized) DBUG_RETURN(FALSE); - mysql_mutex_lock(&LOCK_plugin); /* Do the alloca out here in case we do have a working alloca: leaving the nested stack frame invalidates alloca allocation. */ if (type == MYSQL_ANY_PLUGIN) { - plugins= (plugin_ref*) my_alloca(plugin_array.elements * sizeof(plugin_ref)); - for (idx= 0; idx < plugin_array.elements; idx++) + plugins= (plugin_ref*) my_alloca(global_plugins_state->m_array.elements * + sizeof(plugin_ref)); + for (idx= 0; idx < global_plugins_state->m_array.elements; idx++) { - plugin= *dynamic_element(&plugin_array, idx, struct st_plugin_int **); + plugin= *dynamic_element(&global_plugins_state->m_array, idx, struct st_plugin_int **); if ((plugins[total]= intern_plugin_lock(0, plugin_int_to_ref(plugin), state_mask))) total++; @@ -2456,7 +2460,7 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, } else { - HASH *hash= plugin_hash + type; + HASH *hash= global_plugins_state->m_hash + type; plugins= (plugin_ref*) my_alloca(hash->records * sizeof(plugin_ref)); for (idx= 0; idx < hash->records; idx++) { @@ -2466,7 +2470,6 @@ bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, total++; } } - mysql_mutex_unlock(&LOCK_plugin); for (idx= 0; idx < total; idx++) { @@ -2485,6 +2488,7 @@ static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl, st_maria_plugin *plug, plugin_foreach_func *func, void *arg) { + mysql_mutex_assert_owner(&LOCK_plugin); for (; plug->name; plug++) { st_plugin_int tmp, *plugin; @@ -2494,8 +2498,8 @@ static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl, tmp.plugin= plug; tmp.plugin_dl= plugin_dl; - mysql_mutex_lock(&LOCK_plugin); - if ((plugin= plugin_find_internal(&tmp.name, MYSQL_ANY_PLUGIN)) && + if ((plugin= plugin_find_internal(global_plugins_state, &tmp.name, + MYSQL_ANY_PLUGIN)) && plugin->plugin == plug) { @@ -2507,7 +2511,6 @@ static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl, tmp.state= PLUGIN_IS_FREED; tmp.load_option= PLUGIN_OFF; } - mysql_mutex_unlock(&LOCK_plugin); plugin= &tmp; if (func(thd, plugin_int_to_ref(plugin), arg)) @@ -2521,21 +2524,17 @@ bool plugin_dl_foreach(THD *thd, const LEX_CSTRING *dl, { bool err= 0; + mysql_mutex_lock(&LOCK_plugin); if (dl) { - mysql_mutex_lock(&LOCK_plugin); st_plugin_dl *plugin_dl= plugin_dl_add(dl, MYF(0)); - mysql_mutex_unlock(&LOCK_plugin); if (!plugin_dl) return 1; err= plugin_dl_foreach_internal(thd, plugin_dl, plugin_dl->plugins, func, arg); - - mysql_mutex_lock(&LOCK_plugin); plugin_dl_del(plugin_dl); - mysql_mutex_unlock(&LOCK_plugin); } else { @@ -2545,6 +2544,7 @@ bool plugin_dl_foreach(THD *thd, const LEX_CSTRING *dl, for (builtins= mysql_optional_plugins; !err && *builtins; builtins++) err= plugin_dl_foreach_internal(thd, 0, *builtins, func, arg); } + mysql_mutex_unlock(&LOCK_plugin); return err; } @@ -3198,7 +3198,6 @@ void plugin_thdvar_init(THD *thd) thd->variables.dynamic_variables_size= 0; thd->variables.dynamic_variables_ptr= 0; - mysql_mutex_lock(&LOCK_plugin); thd->variables.table_plugin= intern_plugin_lock(NULL, global_system_variables.table_plugin); if (global_system_variables.tmp_table_plugin) @@ -3210,7 +3209,6 @@ void plugin_thdvar_init(THD *thd) intern_plugin_unlock(NULL, old_table_plugin); intern_plugin_unlock(NULL, old_tmp_table_plugin); intern_plugin_unlock(NULL, old_enforced_table_plugin); - mysql_mutex_unlock(&LOCK_plugin); #ifndef EMBEDDED_LIBRARY thd->session_tracker.sysvars.init(thd); @@ -3284,8 +3282,6 @@ void plugin_thdvar_cleanup(THD *thd) thd->session_tracker.sysvars.deinit(thd); #endif - mysql_mutex_lock(&LOCK_plugin); - unlock_variables(thd, &thd->variables); cleanup_variables(&thd->variables); @@ -3298,8 +3294,6 @@ void plugin_thdvar_cleanup(THD *thd) } reap_plugins(); - mysql_mutex_unlock(&LOCK_plugin); - reset_dynamic(&thd->lex->plugins); DBUG_VOID_RETURN; @@ -4245,9 +4239,10 @@ void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root) if (!initialized) return; - for (uint idx= 0; idx < plugin_array.elements; idx++) + for (uint idx= 0; idx < global_plugins_state->m_array.elements; idx++) { - p= *dynamic_element(&plugin_array, idx, struct st_plugin_int **); + p= *dynamic_element(&global_plugins_state->m_array, idx, + struct st_plugin_int **); if (!(opt= construct_help_options(mem_root, p))) continue; diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index c9c75d07a6e..3258f14a0ab 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -113,8 +113,8 @@ struct st_plugin_int st_ptr_backup *ptr_backup; uint nbackups; uint state; - uint ref_count; /* number of threads using the plugin */ - uint locks_total; /* how many times the plugin was locked */ + volatile int32 ref_count; /* number of threads using the plugin */ + volatile int32 locks_total; /* how many times the plugin was locked */ void *data; /* plugin type specific, e.g. handlerton */ MEM_ROOT mem_root; /* memory for dynamic plugin structures */ sys_var *system_vars; /* server variables for this plugin */ @@ -182,7 +182,6 @@ extern void plugin_thdvar_init(THD *thd); extern void plugin_thdvar_cleanup(THD *thd); sys_var *find_plugin_sysvar(st_plugin_int *plugin, st_mysql_sys_var *var); void plugin_opt_set_limits(struct my_option *, const struct st_mysql_sys_var *); -extern SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type); extern bool check_valid_path(const char *path, size_t length); extern void plugin_mutex_init(); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index d0bb0c816ec..cfcfb4f2b6b 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -26925,7 +26925,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, } if (select_lex->is_top_level_node()) - output->query_plan_ready(); + output->query_plan_set_ready(); DBUG_RETURN(0); } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index ba895384c1a..dc7cf1b1e24 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3010,6 +3010,12 @@ void Show_explain_request::call_in_target_thread() Query_arena backup_arena; bool printed_anything= FALSE; + if (target_thd->lex == NULL || target_thd->lex->explain == NULL || + !target_thd->lex->explain->query_plan_is_ready()) + { + failed_to_produce= TRUE; + return; + } /* Change the arena because JOIN::print_explain and co. are going to allocate items. Let them allocate them on our arena. diff --git a/sql/sql_tvc.cc b/sql/sql_tvc.cc index 763fd8ec45b..b5f60681406 100644 --- a/sql/sql_tvc.cc +++ b/sql/sql_tvc.cc @@ -344,7 +344,7 @@ int table_value_constr::save_explain_data_intern(THD *thd, output->add_node(explain); if (select_lex->is_top_level_node()) - output->query_plan_ready(); + output->query_plan_set_ready(); DBUG_RETURN(0); } |