Commit 85535259 authored by Sergey Vojtovich's avatar Sergey Vojtovich

MDEV-18400 - Move shutdown handling to main thread

Signal handler is now respoinsible for setting abort_loop and breaking
poll() in main thread. The rest is handled by main thread itself.

Removed redundant LOCK_error_log init/destroy wrappers.
Removed redundant unireg_end(): it is trivial and it has only one caller.
Removed unused ready_to_exit from PFS.
Removed kill_in_progress: duplicates abort_loop.
Removed shutdown_in_progress: duplicates abort_loop.
Removed ready_to_exit: was used to make sure main thread waits for
cleanups, which are now done by main thread itself.
Removed SIGNALS_DONT_BREAK_READ, MAYBE_BROKEN_SYSCALL,
kill_broken_server: never defined/used.
Make clean_up() static.
parent c2318291
......@@ -137,7 +137,7 @@ static int prepare_for_fill(TABLE_LIST *tables)
*/
static bool going_down()
{
return shutdown_plugin || shutdown_in_progress || (thd && thd->killed);
return shutdown_plugin || abort_loop || (thd && thd->killed);
}
/**
......
......@@ -17,6 +17,5 @@
#define INIT_INCLUDED
void unireg_init(ulong options);
void unireg_end(void);
#endif /* INIT_INCLUDED */
......@@ -129,7 +129,7 @@
/* We have HAVE_valgrind below as this speeds up the shutdown of MySQL */
#if defined(SIGNALS_DONT_BREAK_READ) || defined(HAVE_valgrind) && defined(__linux__)
#if defined(HAVE_valgrind) && defined(__linux__)
#define HAVE_CLOSE_SERVER_SOCK 1
#endif
......@@ -345,7 +345,6 @@ PSI_statement_info stmt_info_rpl;
static bool lower_case_table_names_used= 0;
static bool max_long_data_size_used= false;
static bool volatile select_thread_in_use, signal_thread_in_use;
static volatile bool ready_to_exit;
static my_bool opt_debugging= 0, opt_external_locking= 0, opt_console= 0;
static my_bool opt_short_log_format= 0, opt_silent_startup= 0;
bool my_disable_leak_check= false;
......@@ -395,7 +394,6 @@ bool opt_endinfo, using_udf_functions;
my_bool locked_in_memory;
bool opt_using_transactions;
bool volatile abort_loop;
bool volatile shutdown_in_progress;
uint volatile global_disable_checkpoint;
#if defined(_WIN32) && !defined(EMBEDDED_LIBRARY)
ulong slow_start_timeout;
......@@ -685,6 +683,8 @@ SHOW_COMP_OPTION have_crypt, have_compress;
SHOW_COMP_OPTION have_profiling;
SHOW_COMP_OPTION have_openssl;
static std::atomic<char*> shutdown_user;
/* Thread specific variables */
pthread_key(THD*, THR_THD);
......@@ -692,7 +692,6 @@ pthread_key(THD*, THR_THD);
/*
LOCK_thread_count protects the following variables:
thread_count Number of threads with THD that servers queries.
ready_to_exit
*/
mysql_mutex_t LOCK_thread_count;
......@@ -759,7 +758,6 @@ char *opt_binlog_index_name=0;
/* Static variables */
static volatile sig_atomic_t kill_in_progress;
my_bool opt_stack_trace;
my_bool opt_expect_abort= 0, opt_bootstrap= 0;
static my_bool opt_myisam_log;
......@@ -1507,7 +1505,6 @@ static int fix_paths(void);
void handle_connections_sockets();
#endif
pthread_handler_t kill_server_thread(void *arg);
static bool read_init_file(char *file_name);
pthread_handler_t handle_slave(void *arg);
static void clean_up(bool print_message);
......@@ -1632,20 +1629,29 @@ static my_bool kill_all_threads_once_again(THD *thd, void *)
}
static void close_connections(void)
/**
Kills main thread.
@note this function is responsible for setting abort_loop and breaking
poll() in main thread. Shutdown as such is supposed to be performed by main
thread itself.
*/
static void break_connect_loop()
{
#ifdef EXTRA_DEBUG
int count=0;
#endif
DBUG_ENTER("close_connections");
/* Clear thread cache */
kill_cached_threads++;
flush_thread_cache();
abort_loop= 1;
/* kill connection thread */
#if !defined(__WIN__)
#if defined(__WIN__)
if (!SetEvent(hEventShutdown))
DBUG_PRINT("error", ("Got error: %ld from SetEvent", GetLastError()));
#else
/* Avoid waiting for ourselves when thread-handling=no-threads. */
if (pthread_equal(pthread_self(), select_thread))
return;
DBUG_PRINT("quit", ("waiting for select thread: %lu",
(ulong)select_thread));
......@@ -1676,7 +1682,43 @@ static void close_connections(void)
}
mysql_mutex_unlock(&LOCK_start_thread);
#endif /* __WIN__ */
}
/**
A wrapper around kill_main_thrad().
Sets shutdown user. This function may be called by multiple threads
concurrently, thus it performs safe update of shutdown_user
(first thread wins).
*/
void kill_mysql(THD *thd)
{
char user_host_buff[MAX_USER_HOST_SIZE + 1];
char *user, *expected_shutdown_user= 0;
make_user_name(thd, user_host_buff);
if ((user= my_strdup(user_host_buff, MYF(0))) &&
!shutdown_user.compare_exchange_strong(expected_shutdown_user,
user,
std::memory_order_relaxed,
std::memory_order_relaxed))
{
my_free(user);
}
break_connect_loop();
}
static void close_connections(void)
{
DBUG_ENTER("close_connections");
/* Clear thread cache */
kill_cached_threads++;
flush_thread_cache();
/* Abort listening to new connections */
DBUG_PRINT("quit",("Closing sockets"));
......@@ -1800,144 +1842,6 @@ static void close_server_sock()
#endif /*EMBEDDED_LIBRARY*/
/**
Set shutdown user
@note this function may be called by multiple threads concurrently, thus
it performs safe update of shutdown_user (first thread wins).
*/
static volatile char *shutdown_user;
static void set_shutdown_user(THD *thd)
{
char user_host_buff[MAX_USER_HOST_SIZE + 1];
char *user, *expected_shutdown_user= 0;
make_user_name(thd, user_host_buff);
if ((user= my_strdup(user_host_buff, MYF(0))) &&
!my_atomic_casptr((void **) &shutdown_user,
(void **) &expected_shutdown_user, user))
my_free(user);
}
void kill_mysql(THD *thd)
{
DBUG_ENTER("kill_mysql");
if (thd)
set_shutdown_user(thd);
#if defined(SIGNALS_DONT_BREAK_READ) && !defined(EMBEDDED_LIBRARY)
abort_loop=1; // Break connection loops
close_server_sock(); // Force accept to wake up
#endif
#if defined(__WIN__)
#if !defined(EMBEDDED_LIBRARY)
{
if (!SetEvent(hEventShutdown))
{
DBUG_PRINT("error",("Got error: %ld from SetEvent",GetLastError()));
}
}
#endif
#elif defined(HAVE_PTHREAD_KILL)
if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL))
{
DBUG_PRINT("error",("Got error %d from pthread_kill",errno)); /* purecov: inspected */
}
#elif !defined(SIGNALS_DONT_BREAK_READ)
kill(current_pid, MYSQL_KILL_SIGNAL);
#endif
DBUG_PRINT("quit",("After pthread_kill"));
shutdown_in_progress=1; // Safety if kill didn't work
#ifdef SIGNALS_DONT_BREAK_READ
if (!kill_in_progress)
{
pthread_t tmp;
int error;
abort_loop=1;
if (unlikely((error= mysql_thread_create(0, /* Not instrumented */
&tmp, &connection_attrib,
kill_server_thread, (void*) 0))))
sql_print_error("Can't create thread to kill server (errno= %d).",
error);
}
#endif
DBUG_VOID_RETURN;
}
/**
Force server down. Kill all connections and threads and exit.
@param sig Signal number that caused kill_server to be called.
@note
A signal number of 0 mean that the function was not called
from a signal handler and there is thus no signal to block
or stop, we just want to kill the server.
*/
static void kill_server(int sig)
{
DBUG_ENTER("kill_server");
#ifndef EMBEDDED_LIBRARY
// if there is a signal during the kill in progress, ignore the other
if (kill_in_progress) // Safety
{
DBUG_VOID_RETURN;
}
kill_in_progress=TRUE;
abort_loop=1; // This should be set
if (sig != 0) // 0 is not a valid signal number
my_sigset(sig, SIG_IGN); /* purify inspected */
if (sig == MYSQL_KILL_SIGNAL || sig == 0)
{
char *user= (char *) my_atomic_loadptr((void**) &shutdown_user);
sql_print_information(ER_DEFAULT(ER_NORMAL_SHUTDOWN), my_progname,
user ? user : "unknown");
if (user)
my_free(user);
}
else
sql_print_error(ER_DEFAULT(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */
#ifdef WITH_WSREP
/* Stop wsrep threads in case they are running. */
if (wsrep_running_threads > 0)
{
wsrep_shutdown_replication();
}
#endif
close_connections();
if (sig != MYSQL_KILL_SIGNAL &&
sig != 0)
unireg_abort(1); /* purecov: inspected */
else
unireg_end();
#endif /* EMBEDDED_LIBRARY*/
DBUG_VOID_RETURN;
}
#if defined(USE_ONE_SIGNAL_HAND)
pthread_handler_t kill_server_thread(void *arg __attribute__((unused)))
{
my_thread_init(); // Initialize new thread
kill_server(0);
my_thread_end();
pthread_exit(0);
return 0;
}
#endif
extern "C" sig_handler print_signal_warning(int sig)
{
if (global_system_variables.log_warnings)
......@@ -1953,36 +1857,6 @@ extern "C" sig_handler print_signal_warning(int sig)
}
#ifndef EMBEDDED_LIBRARY
static void init_error_log_mutex()
{
mysql_mutex_init(key_LOCK_error_log, &LOCK_error_log, MY_MUTEX_INIT_FAST);
}
static void clean_up_error_log_mutex()
{
mysql_mutex_destroy(&LOCK_error_log);
}
/**
cleanup all memory and end program nicely.
If SIGNALS_DONT_BREAK_READ is defined, this function is called
by the main thread. To get MySQL to shut down nicely in this case
(Mac OS X) we have to call exit() instead if pthread_exit().
@note
This function never returns.
*/
void unireg_end(void)
{
clean_up(1);
sd_notify(0, "STATUS=MariaDB server is down");
}
extern "C" void unireg_abort(int exit_code)
{
DBUG_ENTER("unireg_abort");
......@@ -2003,7 +1877,6 @@ extern "C" void unireg_abort(int exit_code)
wsrep threads here, we can only diconnect from service
*/
wsrep_close_client_connections(FALSE);
shutdown_in_progress= 1;
Wsrep_server_state::instance().disconnect();
WSREP_INFO("Service disconnected.");
wsrep_close_threads(NULL); /* this won't close all threads */
......@@ -2049,7 +1922,6 @@ static void mysqld_exit(int exit_code)
#endif /* WITH_WSREP */
mysql_audit_finalize();
clean_up_mutexes();
clean_up_error_log_mutex();
my_end((opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0));
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
shutdown_performance_schema(); // we do it as late as possible
......@@ -2074,7 +1946,7 @@ static void mysqld_exit(int exit_code)
#endif /* !EMBEDDED_LIBRARY */
void clean_up(bool print_message)
static void clean_up(bool print_message)
{
DBUG_PRINT("exit",("clean_up"));
if (cleanup_done++)
......@@ -2173,16 +2045,6 @@ void clean_up(bool print_message)
sys_var_end();
free_charsets();
/*
Signal mysqld_main() that it can exit
do the broadcast inside the lock to ensure that my_end() is not called
during broadcast()
*/
mysql_mutex_lock(&LOCK_thread_count);
ready_to_exit=1;
mysql_cond_broadcast(&COND_thread_count);
mysql_mutex_unlock(&LOCK_thread_count);
my_free(const_cast<char*>(log_bin_basename));
my_free(const_cast<char*>(log_bin_index));
#ifndef EMBEDDED_LIBRARY
......@@ -2275,6 +2137,9 @@ static void clean_up_mutexes()
mysql_mutex_destroy(&LOCK_commit_ordered);
mysql_mutex_destroy(&LOCK_slave_background);
mysql_cond_destroy(&COND_slave_background);
#ifndef EMBEDDED_LIBRARY
mysql_mutex_destroy(&LOCK_error_log);
#endif
DBUG_VOID_RETURN;
}
......@@ -2707,7 +2572,6 @@ void close_connection(THD *thd, uint sql_errno)
mysql_audit_notify_connection_disconnect(thd, sql_errno);
DBUG_VOID_RETURN;
}
#endif /* EMBEDDED_LIBRARY */
/** Called when mysqld is aborted with ^C */
......@@ -2715,11 +2579,12 @@ void close_connection(THD *thd, uint sql_errno)
extern "C" sig_handler end_mysqld_signal(int sig __attribute__((unused)))
{
DBUG_ENTER("end_mysqld_signal");
/* Don't call kill_mysql() if signal thread is not running */
/* Don't kill if signal thread is not running */
if (signal_thread_in_use)
kill_mysql(); // Take down mysqld nicely
break_connect_loop(); // Take down mysqld nicely
DBUG_VOID_RETURN; /* purecov: deadcode */
}
#endif /* EMBEDDED_LIBRARY */
/*
Decrease number of connections
......@@ -2965,7 +2830,7 @@ static BOOL WINAPI console_event_handler( DWORD type )
*/
#ifndef EMBEDDED_LIBRARY
if(hEventShutdown)
kill_mysql();
break_connect_loop();
else
#endif
sql_print_warning("CTRL-C ignored during startup");
......@@ -3304,6 +3169,18 @@ static void start_signal_handler(void)
}
#if defined(USE_ONE_SIGNAL_HAND)
pthread_handler_t kill_server_thread(void *arg __attribute__((unused)))
{
my_thread_init(); // Initialize new thread
break_connect_loop();
my_thread_end();
pthread_exit(0);
return 0;
}
#endif
/** This threads handles all signals and alarms. */
/* ARGSUSED */
pthread_handler_t signal_hand(void *arg __attribute__((unused)))
......@@ -3359,13 +3236,8 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
{
int error;
int origin;
if (shutdown_in_progress && !abort_loop)
{
sig= SIGTERM;
error=0;
}
else
while ((error= my_sigwait(&set, &sig, &origin)) == EINTR) /* no-op */;
while ((error= my_sigwait(&set, &sig, &origin)) == EINTR) /* no-op */;
if (cleanup_done)
{
DBUG_PRINT("quit",("signal_handler: calling my_thread_end()"));
......@@ -3388,7 +3260,6 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
DBUG_PRINT("info",("Got signal: %d abort_loop: %d",sig,abort_loop));
if (!abort_loop)
{
abort_loop=1; // mark abort for threads
/* Delete the instrumentation for the signal thread */
PSI_CALL_delete_current_thread();
#ifdef USE_ONE_SIGNAL_HAND
......@@ -3400,7 +3271,8 @@ pthread_handler_t signal_hand(void *arg __attribute__((unused)))
sql_print_error("Can't create thread to kill server (errno= %d)",
error);
#else
kill_server(sig); // MIT THREAD has a alarm thread
my_sigset(sig, SIG_IGN);
break_connect_loop(); // MIT THREAD has a alarm thread
#endif
}
break;
......@@ -5716,7 +5588,7 @@ int mysqld_main(int argc, char **argv)
}
#endif /* HAVE_PSI_INTERFACE */
init_error_log_mutex();
mysql_mutex_init(key_LOCK_error_log, &LOCK_error_log, MY_MUTEX_INIT_FAST);
/* Initialize audit interface globals. Audit plugins are inited later. */
mysql_audit_initialize();
......@@ -5853,18 +5725,7 @@ int mysqld_main(int argc, char **argv)
if (mysql_rm_tmp_tables() || acl_init(opt_noacl) ||
my_tz_init((THD *)0, default_tz_name, opt_bootstrap))
{
abort_loop=1;
select_thread_in_use=0;
(void) pthread_kill(signal_thread, MYSQL_KILL_SIGNAL);
delete_pid_file(MYF(MY_WME));
if (mysql_socket_getfd(unix_sock) != INVALID_SOCKET)
unlink(mysqld_unix_port);
exit(1);
}
unireg_abort(1);
if (!opt_noacl)
(void) grant_init();
......@@ -5916,7 +5777,7 @@ int mysqld_main(int argc, char **argv)
{
select_thread_in_use= 0; // Allow 'kill' to work
bootstrap(mysql_stdin);
if (!kill_in_progress)
if (!abort_loop)
unireg_abort(bootstrap_error ? 1 : 0);
else
{
......@@ -5996,34 +5857,45 @@ int mysqld_main(int argc, char **argv)
#ifdef _WIN32
handle_connections_win();
kill_server(0);
#else
handle_connections_sockets();
mysql_mutex_lock(&LOCK_start_thread);
select_thread_in_use=0;
mysql_cond_broadcast(&COND_start_thread);
mysql_mutex_unlock(&LOCK_start_thread);
#endif /* _WIN32 */
/* Shutdown requested */
char *user= shutdown_user.load(std::memory_order_relaxed);
sql_print_information(ER_DEFAULT(ER_NORMAL_SHUTDOWN), my_progname,
user ? user : "unknown");
if (user)
my_free(user);
#ifdef WITH_WSREP
/* Stop wsrep threads in case they are running. */
if (wsrep_running_threads > 0)
{
wsrep_shutdown_replication();
}
#endif
close_connections();
clean_up(1);
sd_notify(0, "STATUS=MariaDB server is down");
/* (void) pthread_attr_destroy(&connection_attrib); */
DBUG_PRINT("quit",("Exiting main thread"));
#ifndef __WIN__
mysql_mutex_lock(&LOCK_start_thread);
select_thread_in_use=0; // For close_connections
mysql_cond_broadcast(&COND_start_thread);
mysql_mutex_unlock(&LOCK_start_thread);
#endif /* __WIN__ */
/*
Disable the main thread instrumentation,
to avoid recording events during the shutdown.
*/
PSI_CALL_delete_current_thread();
/* Wait until cleanup is done */
mysql_mutex_lock(&LOCK_thread_count);
while (!ready_to_exit)
mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
mysql_mutex_unlock(&LOCK_thread_count);
#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY)
if (start_mode)
Service.Stop();
......@@ -6405,25 +6277,6 @@ void create_new_thread(CONNECT *connect)
#endif /* EMBEDDED_LIBRARY */
#ifdef SIGNALS_DONT_BREAK_READ
inline void kill_broken_server()
{
/* hack to get around signals ignored in syscalls for problem OS's */
if (mysql_socket_getfd(unix_sock) == INVALID_SOCKET ||
(!opt_disable_networking &&
mysql_socket_getfd(base_ip_sock) == INVALID_SOCKET))
{
select_thread_in_use = 0;
/* The following call will never return */
DBUG_PRINT("general", ("killing server because socket is closed"));
kill_server((void*) MYSQL_KILL_SIGNAL);
}
}
#define MAYBE_BROKEN_SYSCALL kill_broken_server();
#else
#define MAYBE_BROKEN_SYSCALL
#endif
/* Handle new connections and spawn new process to handle them */
#ifndef EMBEDDED_LIBRARY
......@@ -6566,7 +6419,6 @@ void handle_connections_sockets()
"STATUS=Taking your SQL requests now...\n");
DBUG_PRINT("general",("Waiting for connections."));
MAYBE_BROKEN_SYSCALL;
while (!abort_loop)
{
#ifdef HAVE_POLL
......@@ -6589,15 +6441,11 @@ void handle_connections_sockets()
if (!select_errors++ && !abort_loop) /* purecov: inspected */
sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */
}
MAYBE_BROKEN_SYSCALL
continue;
}
if (abort_loop)
{
MAYBE_BROKEN_SYSCALL;
break;
}
/* Is this a new connection request ? */
#ifdef HAVE_POLL
......@@ -6648,7 +6496,6 @@ void handle_connections_sockets()
if (mysql_socket_getfd(new_sock) != INVALID_SOCKET ||
(socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN))
break;
MAYBE_BROKEN_SYSCALL;
#if !defined(NO_FCNTL_NONBLOCK)
if (!(test_flags & TEST_BLOCKING))
{
......@@ -6671,7 +6518,6 @@ void handle_connections_sockets()
statistic_increment(connection_errors_accept, &LOCK_status);
if ((error_count++ & 255) == 0) // This can happen often
sql_perror("Error in accept");
MAYBE_BROKEN_SYSCALL;
if (socket_errno == SOCKET_ENFILE || socket_errno == SOCKET_EMFILE)
sleep(1); // Give other threads some time
continue;
......@@ -8167,7 +8013,6 @@ static int mysql_init_variables(void)
opt_bootstrap= opt_myisam_log= 0;
disable_log_notes= 0;
mqh_used= 0;
kill_in_progress= 0;
cleanup_done= 0;
test_flags= select_errors= dropping_tables= ha_open_options=0;
thread_count= kill_cached_threads= wake_thread= 0;
......@@ -8176,7 +8021,7 @@ static int mysql_init_variables(void)
opt_endinfo= using_udf_functions= 0;
opt_using_transactions= 0;
abort_loop= select_thread_in_use= signal_thread_in_use= 0;
ready_to_exit= shutdown_in_progress= grant_option= 0;
grant_option= 0;
aborted_threads= aborted_connects= 0;
subquery_cache_miss= subquery_cache_hit= 0;
delayed_insert_threads= delayed_insert_writes= delayed_rows_in_use= 0;
......
......@@ -82,7 +82,7 @@ enum enum_slave_parallel_mode {
};
/* Function prototypes */
void kill_mysql(THD *thd= 0);
void kill_mysql(THD *thd);
void close_connection(THD *thd, uint sql_errno= 0);
void handle_connection_in_main_thread(CONNECT *thd);
void create_thread_to_handle_connection(CONNECT *connect);
......@@ -770,8 +770,6 @@ enum enum_query_type
/* query_id */
extern query_id_t global_query_id;
void unireg_end(void);
/* increment query_id and return it. */
inline __attribute__((warn_unused_result)) query_id_t next_query_id()
{
......
......@@ -1436,7 +1436,7 @@ bool Master_info_index::add_master_info(Master_info *mi, bool write_to_file)
We have to protect against shutdown to ensure we are not calling
my_hash_insert() while my_hash_free() is in progress
*/
if (unlikely(shutdown_in_progress) ||
if (unlikely(abort_loop) ||
!my_hash_insert(&master_info_hash, (uchar*) mi))
{
if (global_system_variables.log_warnings > 1)
......@@ -1579,7 +1579,7 @@ uint any_slave_sql_running(bool already_locked)
if (!already_locked)
mysql_mutex_lock(&LOCK_active_mi);
if (unlikely(shutdown_in_progress || !master_info_index))
if (unlikely(abort_loop || !master_info_index))
count= 1;
else
{
......
......@@ -184,8 +184,6 @@ extern char internal_table_name[2];
extern char empty_c_string[1];
extern MYSQL_PLUGIN_IMPORT const char **errmesg;
extern bool volatile shutdown_in_progress;
extern "C" LEX_STRING * thd_query_string (MYSQL_THD thd);
extern "C" size_t thd_query_safe(MYSQL_THD thd, char *buf, size_t buflen);
......
......@@ -2403,10 +2403,10 @@ void wsrep_kill_mysql(THD *thd)
{
if (mysqld_server_started)
{
if (!shutdown_in_progress)
if (!abort_loop)
{
WSREP_INFO("starting shutdown");
kill_mysql();
kill_mysql(thd);
}
}
else
......
......@@ -24,8 +24,6 @@
struct sql_digest_storage;
volatile bool ready_to_exit= false;
uint lower_case_table_names= 0;
CHARSET_INFO *files_charset_info= NULL;
CHARSET_INFO *system_charset_info= NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment