diff options
author | Monty <monty@mariadb.org> | 2015-11-12 14:51:01 +0200 |
---|---|---|
committer | Monty <monty@mariadb.org> | 2015-11-12 14:51:01 +0200 |
commit | e8c1b35f180efef8bc486aadda73fef7f8febf56 (patch) | |
tree | a9b38e840a72ceffa96f5323e1bc5567f0fad9be /sql | |
parent | 83ed38d92ad27d31f0837f0c8277ed7bb21b68f6 (diff) | |
download | mariadb-git-e8c1b35f180efef8bc486aadda73fef7f8febf56.tar.gz |
MDEV-8476 Race condition in slave SQL thread shutdown
Patch backported from MariaDB 10.1
- Ensure that we wait with cleanup() until slave thread has stopped.
- Added signal_thd_deleted() to signal close_connections() that all THD's has been freed.
Other things
- Removed not needed calls to THD_CHECK_SENTRY() when we are calling 'delete thd'.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/mysqld.cc | 42 | ||||
-rw-r--r-- | sql/mysqld.h | 3 | ||||
-rw-r--r-- | sql/slave.cc | 18 |
3 files changed, 41 insertions, 22 deletions
diff --git a/sql/mysqld.cc b/sql/mysqld.cc index bc4d857d704..2e325ab352e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -475,7 +475,7 @@ ulong delay_key_write_options; uint protocol_version; uint lower_case_table_names; ulong tc_heuristic_recover= 0; -int32 thread_count; +int32 thread_count, service_thread_count; int32 thread_running; int32 slave_open_temp_tables; ulong thread_created; @@ -1708,7 +1708,7 @@ static void close_connections(void) /* All threads has now been aborted */ DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count)); mysql_mutex_lock(&LOCK_thread_count); - while (thread_count) + while (thread_count || service_thread_count) { mysql_cond_wait(&COND_thread_count, &LOCK_thread_count); DBUG_PRINT("quit",("One thread died (count=%u)",thread_count)); @@ -2730,8 +2730,27 @@ void delete_running_thd(THD *thd) delete thd; dec_thread_running(); thread_safe_decrement32(&thread_count, &thread_count_lock); - if (!thread_count) + signal_thd_deleted(); +} + + +/* + Send a signal to unblock close_conneciton() if there is no more + threads running with a THD attached + + It's safe to check for thread_count and service_thread_count outside + of a mutex as we are only interested to see if they where decremented + to 0 by a previous unlink_thd() call. + + We should only signal COND_thread_count if both variables are 0, + false positives are ok. +*/ + +void signal_thd_deleted() +{ + if (!thread_count && ! service_thread_count) { + /* Signal close_connections() that all THD's are freed */ mysql_mutex_lock(&LOCK_thread_count); mysql_cond_broadcast(&COND_thread_count); mysql_mutex_unlock(&LOCK_thread_count); @@ -2887,19 +2906,7 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache) if (put_in_cache && cache_thread()) DBUG_RETURN(0); // Thread is reused - /* - It's safe to check for thread_count outside of the mutex - as we are only interested to see if it was counted to 0 by the - above unlink_thd() call. We should only signal COND_thread_count if - thread_count is likely to be 0. (false positives are ok) - */ - if (!thread_count) - { - mysql_mutex_lock(&LOCK_thread_count); - DBUG_PRINT("signal", ("Broadcasting COND_thread_count")); - mysql_cond_broadcast(&COND_thread_count); - mysql_mutex_unlock(&LOCK_thread_count); - } + signal_thd_deleted(); DBUG_LEAVE; // Must match DBUG_ENTER() #if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) ERR_remove_state(0); @@ -8185,7 +8192,8 @@ static int mysql_init_variables(void) cleanup_done= 0; server_id_supplied= 0; test_flags= select_errors= dropping_tables= ha_open_options=0; - thread_count= thread_running= kill_cached_threads= wake_thread=0; + thread_count= thread_running= kill_cached_threads= wake_thread= 0; + service_thread_count= 0; slave_open_temp_tables= 0; cached_thread_count= 0; opt_endinfo= using_udf_functions= 0; diff --git a/sql/mysqld.h b/sql/mysqld.h index 156e7f99bb5..72bcc3dc686 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -59,6 +59,7 @@ void close_connection(THD *thd, uint sql_errno= 0); void handle_connection_in_main_thread(THD *thd); void create_thread_to_handle_connection(THD *thd); void delete_running_thd(THD *thd); +void signal_thd_deleted(); void unlink_thd(THD *thd); bool one_thread_per_connection_end(THD *thd, bool put_in_cache); void flush_thread_cache(); @@ -538,7 +539,7 @@ extern mysql_cond_t COND_thread_count; extern mysql_cond_t COND_manager; extern mysql_cond_t COND_slave_init; extern int32 thread_running; -extern int32 thread_count; +extern int32 thread_count, service_thread_count; extern my_atomic_rwlock_t thread_running_lock, thread_count_lock; extern my_atomic_rwlock_t slave_executed_entries_lock; diff --git a/sql/slave.cc b/sql/slave.cc index 5af48b6a793..fda7e500237 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -294,6 +294,7 @@ handle_slave_init(void *arg __attribute__((unused))) thd->thread_id= thread_id++; mysql_mutex_unlock(&LOCK_thread_count); thd->system_thread = SYSTEM_THREAD_SLAVE_INIT; + thread_safe_increment32(&service_thread_count, &thread_count_lock); thd->store_globals(); thd->security_ctx->skip_grants(); thd->set_command(COM_DAEMON); @@ -309,6 +310,8 @@ handle_slave_init(void *arg __attribute__((unused))) mysql_mutex_lock(&LOCK_thread_count); delete thd; mysql_mutex_unlock(&LOCK_thread_count); + thread_safe_decrement32(&service_thread_count, &thread_count_lock); + signal_thd_deleted(); my_thread_end(); mysql_mutex_lock(&LOCK_slave_init); @@ -2953,6 +2956,11 @@ static int init_slave_thread(THD* thd, Master_info *mi, simulate_error|= (1 << SLAVE_THD_IO);); DBUG_EXECUTE_IF("simulate_sql_slave_error_on_init", simulate_error|= (1 << SLAVE_THD_SQL);); + + thd->system_thread = (thd_type == SLAVE_THD_SQL) ? + SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO; + thread_safe_increment32(&service_thread_count, &thread_count_lock); + /* We must call store_globals() before doing my_net_init() */ if (init_thr_lock() || thd->store_globals() || my_net_init(&thd->net, 0, MYF(MY_THREAD_SPECIFIC)) || @@ -2962,8 +2970,6 @@ static int init_slave_thread(THD* thd, Master_info *mi, DBUG_RETURN(-1); } - thd->system_thread = (thd_type == SLAVE_THD_SQL) ? - SYSTEM_THREAD_SLAVE_SQL : SYSTEM_THREAD_SLAVE_IO; thd->security_ctx->skip_grants(); thd->slave_thread= 1; thd->connection_name= mi->connection_name; @@ -4228,11 +4234,14 @@ err_during_init: mi->rli.relay_log.description_event_for_queue= 0; // TODO: make rpl_status part of Master_info change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); + mysql_mutex_lock(&LOCK_thread_count); thd->unlink(); mysql_mutex_unlock(&LOCK_thread_count); - THD_CHECK_SENTRY(thd); delete thd; + thread_safe_decrement32(&service_thread_count, &thread_count_lock); + signal_thd_deleted(); + mi->abort_slave= 0; mi->slave_running= MYSQL_SLAVE_NOT_RUN; mi->io_thd= 0; @@ -4848,9 +4857,10 @@ err_during_init: mysql_mutex_unlock(&LOCK_active_mi); mysql_mutex_lock(&LOCK_thread_count); - THD_CHECK_SENTRY(thd); delete thd; mysql_mutex_unlock(&LOCK_thread_count); + thread_safe_decrement32(&service_thread_count, &thread_count_lock); + signal_thd_deleted(); DBUG_LEAVE; // Must match DBUG_ENTER() my_thread_end(); |