/* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mysqld_thd_manager.h" #include "mysql/thread_pool_priv.h" // inc_thread_created #include "mutex_lock.h" // Mutex_lock #include "debug_sync.h" // DEBUG_SYNC_C #include "sql_class.h" // THD #include #include int Global_THD_manager::global_thd_count= 0; Global_THD_manager *Global_THD_manager::thd_manager = NULL; /** Internal class used in do_for_all_thd() and do_for_all_thd_copy() implementation. */ class Do_THD : public std::unary_function { public: explicit Do_THD(Do_THD_Impl *impl) : m_impl(impl) {} /** Users of this class will override operator() in the _Impl class. @param thd THD of one element in global thread list */ void operator()(THD* thd) { m_impl->operator()(thd); } private: Do_THD_Impl *m_impl; }; /** Internal class used in find_thd() implementation. */ class Find_THD : public std::unary_function { public: explicit Find_THD(Find_THD_Impl *impl) : m_impl(impl) {} bool operator()(THD* thd) { return m_impl->operator()(thd); } private: Find_THD_Impl *m_impl; }; #ifdef HAVE_PSI_INTERFACE static PSI_mutex_key key_LOCK_thd_list; static PSI_mutex_key key_LOCK_thd_remove; static PSI_mutex_key key_LOCK_thread_ids; static PSI_mutex_info all_thd_manager_mutexes[]= { { &key_LOCK_thd_list, "LOCK_thd_list", PSI_FLAG_GLOBAL}, { &key_LOCK_thd_remove, "LOCK_thd_remove", PSI_FLAG_GLOBAL}, { &key_LOCK_thread_ids, "LOCK_thread_ids", PSI_FLAG_GLOBAL } }; static PSI_cond_key key_COND_thd_list; static PSI_cond_info all_thd_manager_conds[]= { { &key_COND_thd_list, "COND_thd_list", PSI_FLAG_GLOBAL} }; #endif // HAVE_PSI_INTERFACE const my_thread_id Global_THD_manager::reserved_thread_id= 0; Global_THD_manager::Global_THD_manager() : thd_list(PSI_INSTRUMENT_ME), thread_ids(PSI_INSTRUMENT_ME), num_thread_running(0), thread_created(0), thread_id_counter(reserved_thread_id + 1), unit_test(false) { #ifdef HAVE_PSI_INTERFACE int count= array_elements(all_thd_manager_mutexes); mysql_mutex_register("sql", all_thd_manager_mutexes, count); count= array_elements(all_thd_manager_conds); mysql_cond_register("sql", all_thd_manager_conds, count); #endif mysql_mutex_init(key_LOCK_thd_list, &LOCK_thd_list, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_thd_remove, &LOCK_thd_remove, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_thread_ids, &LOCK_thread_ids, MY_MUTEX_INIT_FAST); mysql_cond_init(key_COND_thd_list, &COND_thd_list); // The reserved thread ID should never be used by normal threads, // so mark it as in-use. This ID is used by temporary THDs never // added to the list of THDs. thread_ids.push_back(reserved_thread_id); } Global_THD_manager::~Global_THD_manager() { thread_ids.erase_unique(reserved_thread_id); DBUG_ASSERT(thd_list.empty()); DBUG_ASSERT(thread_ids.empty()); mysql_mutex_destroy(&LOCK_thd_list); mysql_mutex_destroy(&LOCK_thd_remove); mysql_mutex_destroy(&LOCK_thread_ids); mysql_cond_destroy(&COND_thd_list); } /* Singleton Instance creation This method do not require mutex guard as it is called only from main thread. */ bool Global_THD_manager::create_instance() { if (thd_manager == NULL) thd_manager= new (std::nothrow) Global_THD_manager(); return (thd_manager == NULL); } void Global_THD_manager::destroy_instance() { delete thd_manager; thd_manager= NULL; } void Global_THD_manager::add_thd(THD *thd) { DBUG_PRINT("info", ("Global_THD_manager::add_thd %p", thd)); // Should have an assigned ID before adding to the list. DBUG_ASSERT(thd->thread_id() != reserved_thread_id); mysql_mutex_lock(&LOCK_thd_list); // Technically it is not supported to compare pointers, but it works. std::pair insert_result= thd_list.insert_unique(thd); if (insert_result.second) { ++global_thd_count; } // Adding the same THD twice is an error. DBUG_ASSERT(insert_result.second); mysql_mutex_unlock(&LOCK_thd_list); } void Global_THD_manager::remove_thd(THD *thd) { DBUG_PRINT("info", ("Global_THD_manager::remove_thd %p", thd)); mysql_mutex_lock(&LOCK_thd_remove); mysql_mutex_lock(&LOCK_thd_list); if (!unit_test) DBUG_ASSERT(thd->release_resources_done()); /* Used by binlog_reset_master. It would be cleaner to use DEBUG_SYNC here, but that's not possible because the THD's debug sync feature has been shut down at this point. */ DBUG_EXECUTE_IF("sleep_after_lock_thread_count_before_delete_thd", sleep(5);); const size_t num_erased= thd_list.erase_unique(thd); if (num_erased == 1) --global_thd_count; // Removing a THD that was never added is an error. DBUG_ASSERT(1 == num_erased); mysql_mutex_unlock(&LOCK_thd_remove); mysql_cond_broadcast(&COND_thd_list); mysql_mutex_unlock(&LOCK_thd_list); } my_thread_id Global_THD_manager::get_new_thread_id() { my_thread_id new_id; Mutex_lock lock(&LOCK_thread_ids); do { new_id= thread_id_counter++; } while (!thread_ids.insert_unique(new_id).second); return new_id; } void Global_THD_manager::release_thread_id(my_thread_id thread_id) { if (thread_id == reserved_thread_id) return; // Some temporary THDs are never given a proper ID. Mutex_lock lock(&LOCK_thread_ids); const size_t num_erased MY_ATTRIBUTE((unused))= thread_ids.erase_unique(thread_id); // Assert if the ID was not found in the list. DBUG_ASSERT(1 == num_erased); } void Global_THD_manager::set_thread_id_counter(my_thread_id new_id) { DBUG_ASSERT(unit_test == true); Mutex_lock lock(&LOCK_thread_ids); thread_id_counter= new_id; } void Global_THD_manager::wait_till_no_thd() { mysql_mutex_lock(&LOCK_thd_list); while (get_thd_count() > 0) { mysql_cond_wait(&COND_thd_list, &LOCK_thd_list); DBUG_PRINT("quit", ("One thread died (count=%u)", get_thd_count())); } mysql_mutex_unlock(&LOCK_thd_list); } void Global_THD_manager::do_for_all_thd_copy(Do_THD_Impl *func) { Do_THD doit(func); mysql_mutex_lock(&LOCK_thd_remove); mysql_mutex_lock(&LOCK_thd_list); /* Take copy of global_thread_list. */ THD_array thd_list_copy(thd_list); /* Allow inserts to global_thread_list. Newly added thd will not be accounted for when executing func. */ mysql_mutex_unlock(&LOCK_thd_list); /* Execute func for all existing threads. */ std::for_each(thd_list_copy.begin(), thd_list_copy.end(), doit); DEBUG_SYNC_C("inside_do_for_all_thd_copy"); mysql_mutex_unlock(&LOCK_thd_remove); } void Global_THD_manager::do_for_all_thd(Do_THD_Impl *func) { Do_THD doit(func); mysql_mutex_lock(&LOCK_thd_list); std::for_each(thd_list.begin(), thd_list.end(), doit); mysql_mutex_unlock(&LOCK_thd_list); } THD* Global_THD_manager::find_thd(Find_THD_Impl *func) { Find_THD find_thd(func); mysql_mutex_lock(&LOCK_thd_list); THD_array::const_iterator it= std::find_if(thd_list.begin(), thd_list.end(), find_thd); THD* ret= NULL; if (it != thd_list.end()) ret= *it; mysql_mutex_unlock(&LOCK_thd_list); return ret; } void inc_thread_created() { Global_THD_manager::get_instance()->inc_thread_created(); } void thd_lock_thread_count(THD *) { mysql_mutex_lock(&Global_THD_manager::get_instance()->LOCK_thd_list); } void thd_unlock_thread_count(THD *) { Global_THD_manager *thd_manager= Global_THD_manager::get_instance(); mysql_cond_broadcast(&thd_manager->COND_thd_list); mysql_mutex_unlock(&thd_manager->LOCK_thd_list); } template class Run_free_function : public Do_THD_Impl { public: typedef void (do_thd_impl)(THD*, T); Run_free_function(do_thd_impl *f, T arg) : m_func(f), m_arg(arg) {} virtual void operator()(THD *thd) { (*m_func)(thd, m_arg); } private: do_thd_impl *m_func; T m_arg; }; void do_for_all_thd(do_thd_impl_uint64 f, uint64 v) { Run_free_function runner(f, v); Global_THD_manager::get_instance()->do_for_all_thd(&runner); }