diff options
Diffstat (limited to 'sql/debug_sync.cc')
-rw-r--r-- | sql/debug_sync.cc | 244 |
1 files changed, 115 insertions, 129 deletions
diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 585be68a7f6..a789763dd25 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -221,14 +221,14 @@ There are quite a few places in MySQL, where we use a synchronization pattern like this: - pthread_mutex_lock(&mutex); + mysql_mutex_lock(&mutex); thd->enter_cond(&condition_variable, &mutex, new_message); #if defined(ENABLE_DEBUG_SYNC) if (!thd->killed && !end_of_wait_condition) DEBUG_SYNC(thd, "sync_point_name"); #endif while (!thd->killed && !end_of_wait_condition) - pthread_cond_wait(&condition_variable, &mutex); + mysql_cond_wait(&condition_variable, &mutex); thd->exit_cond(old_message); Here some explanations: @@ -264,12 +264,12 @@ while (!thd->killed && !end_of_wait_condition) { - pthread_mutex_lock(&mutex); + mysql_mutex_lock(&mutex); thd->enter_cond(&condition_variable, &mutex, new_message); if (!thd->killed [&& !end_of_wait_condition]) { [DEBUG_SYNC(thd, "sync_point_name");] - pthread_cond_wait(&condition_variable, &mutex); + mysql_cond_wait(&condition_variable, &mutex); } thd->exit_cond(old_message); } @@ -285,12 +285,12 @@ before sleeping, we hold the mutex, which is registered in mysys_var. The killing thread would try to acquire the mutex before signaling the condition variable. Since the mutex is only released implicitly in - pthread_cond_wait(), the signaling happens at the right place. We + mysql_cond_wait(), the signaling happens at the right place. We have a safe synchronization. === Co-work with the DBUG facility === - When running the MySQL test suite with the --debug command line + When running the MySQL test suite with the --debug-dbug command line option, the Debug Sync Facility writes trace messages to the DBUG trace. The following shell commands proved very useful in extracting relevant information: @@ -321,12 +321,13 @@ /* Due to weaknesses in our include files, we need to include - mysql_priv.h here. To have THD declared, we need to include + sql_priv.h here. To have THD declared, we need to include sql_class.h. This includes log_event.h, which in turn requires - declarations from mysql_priv.h (e.g. OPTION_AUTO_IS_NULL). - mysql_priv.h includes almost everything, so is sufficient here. + declarations from sql_priv.h (e.g. OPTION_AUTO_IS_NULL). + sql_priv.h includes almost everything, so is sufficient here. */ -#include "mysql_priv.h" +#include "sql_priv.h" +#include "sql_parse.h" /* Action to perform at a synchronization point. @@ -373,8 +374,8 @@ struct st_debug_sync_control struct st_debug_sync_globals { String ds_signal; /* signal variable */ - pthread_cond_t ds_cond; /* condition variable */ - pthread_mutex_t ds_mutex; /* mutex variable */ + mysql_cond_t ds_cond; /* condition variable */ + mysql_mutex_t ds_mutex; /* mutex variable */ ulonglong dsp_hits; /* statistics */ ulonglong dsp_executed; /* statistics */ ulonglong dsp_max_active; /* statistics */ @@ -386,6 +387,13 @@ static st_debug_sync_globals debug_sync_global; /* All globals in one object */ */ extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t); +/** + Callbacks from C files. +*/ +C_MODE_START +static void debug_sync_C_callback(const char *, size_t); +static int debug_sync_qsort_cmp(const void *, const void *); +C_MODE_END /** Callback for debug sync, to be used by C files. See thr_lock.c for example. @@ -394,7 +402,7 @@ extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t); We cannot place a sync point directly in C files (like those in mysys or certain storage engines written mostly in C like MyISAM or Maria). Because - they are C code and do not include mysql_priv.h. So they do not know the + they are C code and do not include sql_priv.h. So they do not know the macro DEBUG_SYNC(thd, sync_point_name). The macro needs a 'thd' argument. Hence it cannot be used in files outside of the sql/ directory. @@ -421,10 +429,41 @@ extern "C" void (*debug_sync_C_callback_ptr)(const char *, size_t); static void debug_sync_C_callback(const char *sync_point_name, size_t name_len) { - if (unlikely(opt_debug_sync_timeout)) - debug_sync(current_thd, sync_point_name, name_len); + if (unlikely(opt_debug_sync_timeout)) + debug_sync(current_thd, sync_point_name, name_len); } +#ifdef HAVE_PSI_INTERFACE +static PSI_mutex_key key_debug_sync_globals_ds_mutex; + +static PSI_mutex_info all_debug_sync_mutexes[]= +{ + { &key_debug_sync_globals_ds_mutex, "DEBUG_SYNC::mutex", PSI_FLAG_GLOBAL} +}; + +static PSI_cond_key key_debug_sync_globals_ds_cond; + +static PSI_cond_info all_debug_sync_conds[]= +{ + { &key_debug_sync_globals_ds_cond, "DEBUG_SYNC::cond", PSI_FLAG_GLOBAL} +}; + +static void init_debug_sync_psi_keys(void) +{ + const char* category= "sql"; + int count; + + if (PSI_server == NULL) + return; + + count= array_elements(all_debug_sync_mutexes); + PSI_server->register_mutex(category, all_debug_sync_mutexes, count); + + count= array_elements(all_debug_sync_conds); + PSI_server->register_cond(category, all_debug_sync_conds, count); +} +#endif /* HAVE_PSI_INTERFACE */ + /** Initialize the debug sync facility at server start. @@ -438,15 +477,21 @@ int debug_sync_init(void) { DBUG_ENTER("debug_sync_init"); +#ifdef HAVE_PSI_INTERFACE + init_debug_sync_psi_keys(); +#endif + if (opt_debug_sync_timeout) { int rc; /* Initialize the global variables. */ debug_sync_global.ds_signal.length(0); - if ((rc= pthread_cond_init(&debug_sync_global.ds_cond, NULL)) || - (rc= pthread_mutex_init(&debug_sync_global.ds_mutex, - MY_MUTEX_INIT_FAST))) + if ((rc= mysql_cond_init(key_debug_sync_globals_ds_cond, + &debug_sync_global.ds_cond, NULL)) || + (rc= mysql_mutex_init(key_debug_sync_globals_ds_mutex, + &debug_sync_global.ds_mutex, + MY_MUTEX_INIT_FAST))) DBUG_RETURN(rc); /* purecov: inspected */ /* Set the call back pointer in C files. */ @@ -476,8 +521,8 @@ void debug_sync_end(void) /* Destroy the global variables. */ debug_sync_global.ds_signal.free(); - (void) pthread_cond_destroy(&debug_sync_global.ds_cond); - (void) pthread_mutex_destroy(&debug_sync_global.ds_mutex); + mysql_cond_destroy(&debug_sync_global.ds_cond); + mysql_mutex_destroy(&debug_sync_global.ds_mutex); /* Print statistics. */ { @@ -581,18 +626,18 @@ void debug_sync_end_thread(THD *thd) action->wait_for.free(); action->sync_point.free(); } - my_free(ds_control->ds_action, MYF(0)); + my_free(ds_control->ds_action); } /* Statistics. */ - pthread_mutex_lock(&debug_sync_global.ds_mutex); + mysql_mutex_lock(&debug_sync_global.ds_mutex); debug_sync_global.dsp_hits+= ds_control->dsp_hits; debug_sync_global.dsp_executed+= ds_control->dsp_executed; if (debug_sync_global.dsp_max_active < ds_control->dsp_max_active) debug_sync_global.dsp_max_active= ds_control->dsp_max_active; - pthread_mutex_unlock(&debug_sync_global.ds_mutex); + mysql_mutex_unlock(&debug_sync_global.ds_mutex); - my_free(ds_control, MYF(0)); + my_free(ds_control); thd->debug_sync_control= NULL; } @@ -824,9 +869,9 @@ static void debug_sync_reset(THD *thd) ds_control->ds_active= 0; /* Clear the global signal. */ - pthread_mutex_lock(&debug_sync_global.ds_mutex); + mysql_mutex_lock(&debug_sync_global.ds_mutex); debug_sync_global.ds_signal.length(0); - pthread_mutex_unlock(&debug_sync_global.ds_mutex); + mysql_mutex_unlock(&debug_sync_global.ds_mutex); DBUG_VOID_RETURN; } @@ -1524,56 +1569,6 @@ static bool debug_sync_eval_action(THD *thd, char *action_str) DBUG_RETURN(FALSE); } - -/** - Check if the system variable 'debug_sync' can be set. - - @param[in] thd thread handle - @param[in] var set variable request - - @return status - @retval FALSE ok, variable can be set - @retval TRUE error, variable cannot be set -*/ - -bool sys_var_debug_sync::check(THD *thd, set_var *var) -{ - DBUG_ENTER("sys_var_debug_sync::check"); - DBUG_ASSERT(thd); - DBUG_ASSERT(var); - - /* - Variable can be set for the session only. - - This could be changed later. Then we need to have a global array of - actions in addition to the thread local ones. SET GLOBAL would - manage the global array, SET [SESSION] the local array. A sync point - would need to look for a local and a global action. Setting and - executing of global actions need to be protected by a mutex. - - The purpose of global actions could be to allow synchronizing with - connectionless threads that cannot execute SET statements. - */ - if (var->type == OPT_GLOBAL) - { - my_error(ER_LOCAL_VARIABLE, MYF(0), name); - DBUG_RETURN(TRUE); - } - - /* - Do not check for disabled facility. Test result should not - unnecessarily differ from enabled facility. - */ - - /* - Facility requires SUPER privilege. Sync points could be inside - global mutexes (e.g. LOCK_open). Waiting there forever would - stall the whole server. - */ - DBUG_RETURN(check_global_access(thd, SUPER_ACL)); -} - - /** Set the system variable 'debug_sync'. @@ -1594,28 +1589,9 @@ bool sys_var_debug_sync::check(THD *thd, set_var *var) terminators in the string. So we need to take a copy here. */ -bool sys_var_debug_sync::update(THD *thd, set_var *var) +bool debug_sync_update(THD *thd, char *val_str) { - char *val_str; - String *val_ptr; - String val_buf; - DBUG_ENTER("sys_var_debug_sync::update"); - DBUG_ASSERT(thd); - - /* - Depending on the value type (string literal, user variable, ...) - val_buf receives a copy of the value or not. But we always need - a copy. So we take a copy, if it is not done by val_str(). - If val_str() puts a copy into val_buf, then it returns &val_buf, - otherwise it returns a pointer to the string object that we need - to copy. - */ - val_ptr= var ? var->value->val_str(&val_buf) : &val_buf; - if (val_ptr != &val_buf) - { - val_buf.copy(*val_ptr); - } - val_str= val_buf.c_ptr(); + DBUG_ENTER("debug_sync_update"); DBUG_PRINT("debug_sync", ("set action: '%s'", val_str)); /* @@ -1632,8 +1608,6 @@ bool sys_var_debug_sync::update(THD *thd, set_var *var) Retrieve the value of the system variable 'debug_sync'. @param[in] thd thread handle - @param[in] type variable type, unused - @param[in] base variable base, unused @return string @retval != NULL ok, string pointer @@ -1646,20 +1620,17 @@ bool sys_var_debug_sync::update(THD *thd, set_var *var) When "ON", the current signal is added. */ -uchar *sys_var_debug_sync::value_ptr(THD *thd, - enum_var_type type __attribute__((unused)), - LEX_STRING *base __attribute__((unused))) +uchar *debug_sync_value_ptr(THD *thd) { char *value; - DBUG_ENTER("sys_var_debug_sync::value_ptr"); - DBUG_ASSERT(thd); + DBUG_ENTER("debug_sync_value_ptr"); if (opt_debug_sync_timeout) { static char on[]= "ON - current signal: '"; // Ensure exclusive access to debug_sync_global.ds_signal - pthread_mutex_lock(&debug_sync_global.ds_mutex); + mysql_mutex_lock(&debug_sync_global.ds_mutex); size_t lgt= (sizeof(on) /* includes '\0' */ + debug_sync_global.ds_signal.length() + 1 /* for '\'' */); @@ -1676,12 +1647,12 @@ uchar *sys_var_debug_sync::value_ptr(THD *thd, *(vptr++)= '\''; *vptr= '\0'; /* We have one byte reserved for the worst case. */ } - pthread_mutex_unlock(&debug_sync_global.ds_mutex); + mysql_mutex_unlock(&debug_sync_global.ds_mutex); } else { /* purecov: begin tested */ - value= strmake_root(thd->mem_root, STRING_WITH_LEN("OFF")); + value= const_cast<char*>("OFF"); /* purecov: end */ } @@ -1701,9 +1672,11 @@ uchar *sys_var_debug_sync::value_ptr(THD *thd, static void debug_sync_execute(THD *thd, st_debug_sync_action *action) { - IF_DBUG(const char *dsp_name= action->sync_point.c_ptr()); - IF_DBUG(const char *sig_emit= action->signal.c_ptr()); - IF_DBUG(const char *sig_wait= action->wait_for.c_ptr()); +#ifndef DBUG_OFF + const char *dsp_name= action->sync_point.c_ptr(); + const char *sig_emit= action->signal.c_ptr(); + const char *sig_wait= action->wait_for.c_ptr(); +#endif DBUG_ENTER("debug_sync_execute"); DBUG_ASSERT(thd); DBUG_ASSERT(action); @@ -1743,7 +1716,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) read access too, to create a memory barrier in order to avoid that threads just reads an old cached version of the signal. */ - pthread_mutex_lock(&debug_sync_global.ds_mutex); + mysql_mutex_lock(&debug_sync_global.ds_mutex); if (action->signal.length()) { @@ -1757,15 +1730,15 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) debug_sync_emergency_disable(); /* purecov: tested */ } /* Wake threads waiting in a sync point. */ - pthread_cond_broadcast(&debug_sync_global.ds_cond); + mysql_cond_broadcast(&debug_sync_global.ds_cond); DBUG_PRINT("debug_sync_exec", ("signal '%s' at: '%s'", sig_emit, dsp_name)); } /* end if (action->signal.length()) */ if (action->wait_for.length()) { - pthread_mutex_t *old_mutex; - pthread_cond_t *old_cond; + mysql_mutex_t *old_mutex; + mysql_cond_t *UNINIT_VAR(old_cond); int error= 0; struct timespec abstime; @@ -1773,11 +1746,20 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) We don't use enter_cond()/exit_cond(). They do not save old mutex and cond. This would prohibit the use of DEBUG_SYNC between other places of enter_cond() and exit_cond(). + + We need to check for existence of thd->mysys_var to also make + it possible to use DEBUG_SYNC framework in scheduler when this + variable has been set to NULL. */ - old_mutex= thd->mysys_var->current_mutex; - old_cond= thd->mysys_var->current_cond; - thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex; - thd->mysys_var->current_cond= &debug_sync_global.ds_cond; + if (thd->mysys_var) + { + old_mutex= thd->mysys_var->current_mutex; + old_cond= thd->mysys_var->current_cond; + thd->mysys_var->current_mutex= &debug_sync_global.ds_mutex; + thd->mysys_var->current_cond= &debug_sync_global.ds_cond; + } + else + old_mutex= NULL; set_timespec(abstime, action->timeout); DBUG_EXECUTE("debug_sync_exec", { @@ -1796,9 +1778,9 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) while (stringcmp(&debug_sync_global.ds_signal, &action->wait_for) && !thd->killed && opt_debug_sync_timeout) { - error= pthread_cond_timedwait(&debug_sync_global.ds_cond, - &debug_sync_global.ds_mutex, - &abstime); + error= mysql_cond_timedwait(&debug_sync_global.ds_cond, + &debug_sync_global.ds_mutex, + &abstime); DBUG_EXECUTE("debug_sync", { /* Functions as DBUG_PRINT args can change keyword and line nr. */ const char *sig_glob= debug_sync_global.ds_signal.c_ptr(); @@ -1831,18 +1813,22 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) protected mutex must always unlocked _before_ mysys_var->mutex is locked. (See comment in THD::exit_cond().) */ - pthread_mutex_unlock(&debug_sync_global.ds_mutex); - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= old_mutex; - thd->mysys_var->current_cond= old_cond; - thd_proc_info(thd, old_proc_info); - pthread_mutex_unlock(&thd->mysys_var->mutex); - + mysql_mutex_unlock(&debug_sync_global.ds_mutex); + if (old_mutex) + { + mysql_mutex_lock(&thd->mysys_var->mutex); + thd->mysys_var->current_mutex= old_mutex; + thd->mysys_var->current_cond= old_cond; + thd_proc_info(thd, old_proc_info); + mysql_mutex_unlock(&thd->mysys_var->mutex); + } + else + thd_proc_info(thd, old_proc_info); } else { /* In case we don't wait, we just release the mutex. */ - pthread_mutex_unlock(&debug_sync_global.ds_mutex); + mysql_mutex_unlock(&debug_sync_global.ds_mutex); } /* end if (action->wait_for.length()) */ } /* end if (action->execute) */ |