Skip to content

Commit 7891a71

Browse files
committed
Don't wait too long in SHOW PROCESSLIST
This will allow show processlist to continue, without blocking all new connections, if some threads gets stuck while holding LOCK_thd_data or mysys_var->mutex Connections that has mutex 'stuck' are marked as 'Busy' in 'Command' Todo: Make F_BACKOFF to do 'pause' instead of just (1)
1 parent b3346c2 commit 7891a71

File tree

1 file changed

+126
-78
lines changed

1 file changed

+126
-78
lines changed

sql/sql_show.cc

Lines changed: 126 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,29 @@ static const LEX_CSTRING *view_algorithm(TABLE_LIST *table);
137137

138138
bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *);
139139

140+
/**
141+
Try to lock a mutex, but give up after a short while to not cause deadlocks
142+
143+
The loop is short, as the mutex we are trying to lock are mutex the should
144+
never be locked a long time, just over a few instructions.
145+
146+
@return 0 ok
147+
@return 1 error
148+
*/
149+
150+
static bool trylock_short(mysql_mutex_t *mutex)
151+
{
152+
uint i;
153+
for (i= 0 ; i < 100 ; i++)
154+
{
155+
if (!mysql_mutex_trylock(mutex))
156+
return 0;
157+
LF_BACKOFF();
158+
}
159+
return 1;
160+
}
161+
162+
140163
/***************************************************************************
141164
** List all table types supported
142165
***************************************************************************/
@@ -2634,7 +2657,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
26342657
while ((tmp=it++))
26352658
{
26362659
Security_context *tmp_sctx= tmp->security_ctx;
2637-
struct st_my_thread_var *mysys_var;
2660+
struct st_my_thread_var *mysys_var= 0;
2661+
bool got_thd_data, got_mysys_lock= 0;
26382662
if ((tmp->vio_ok() || tmp->system_thread) &&
26392663
(!user || (!tmp->system_thread &&
26402664
tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
@@ -2658,48 +2682,66 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
26582682
tmp_sctx->host_or_ip :
26592683
tmp_sctx->host ? tmp_sctx->host : "");
26602684
thd_info->command=(int) tmp->get_command();
2661-
mysql_mutex_lock(&tmp->LOCK_thd_data);
2662-
if ((thd_info->db= tmp->db)) // Safe test
2663-
thd_info->db= thd->strdup(thd_info->db);
2664-
if ((mysys_var= tmp->mysys_var))
2665-
mysql_mutex_lock(&mysys_var->mutex);
2666-
thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
2667-
"Killed" : 0);
2668-
thd_info->state_info= thread_state_info(tmp);
2669-
if (mysys_var)
2670-
mysql_mutex_unlock(&mysys_var->mutex);
26712685

2672-
/* Lock THD mutex that protects its data when looking at it. */
2673-
if (tmp->query())
2674-
{
2675-
uint length= MY_MIN(max_query_length, tmp->query_length());
2676-
char *q= thd->strmake(tmp->query(),length);
2677-
/* Safety: in case strmake failed, we set length to 0. */
2678-
thd_info->query_string=
2679-
CSET_STRING(q, q ? length : 0, tmp->query_charset());
2680-
}
2686+
if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
2687+
if ((mysys_var= tmp->mysys_var))
2688+
got_mysys_lock= !trylock_short(&mysys_var->mutex);
26812689

2682-
/*
2683-
Progress report. We need to do this under a lock to ensure that all
2684-
is from the same stage.
2685-
*/
2686-
if (tmp->progress.max_counter)
2690+
if (got_thd_data)
26872691
{
2688-
uint max_stage= MY_MAX(tmp->progress.max_stage, 1);
2689-
thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
2690-
((tmp->progress.counter /
2691-
(double) tmp->progress.max_counter) /
2692-
(double) max_stage)) *
2693-
100.0);
2694-
set_if_smaller(thd_info->progress, 100);
2692+
/* This is correct under mysys_lock, otherwise an approximation */
2693+
thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
2694+
"Killed" : 0);
2695+
if (got_mysys_lock)
2696+
mysql_mutex_unlock(&mysys_var->mutex);
2697+
2698+
/*
2699+
The following variables are only safe to access under a lock
2700+
*/
2701+
2702+
if ((thd_info->db= tmp->db)) // Safe test
2703+
thd_info->db= thd->strdup(thd_info->db);
2704+
2705+
if (tmp->query())
2706+
{
2707+
uint length= MY_MIN(max_query_length, tmp->query_length());
2708+
char *q= thd->strmake(tmp->query(),length);
2709+
/* Safety: in case strmake failed, we set length to 0. */
2710+
thd_info->query_string=
2711+
CSET_STRING(q, q ? length : 0, tmp->query_charset());
2712+
}
2713+
2714+
/*
2715+
Progress report. We need to do this under a lock to ensure that all
2716+
is from the same stage.
2717+
*/
2718+
if (tmp->progress.max_counter)
2719+
{
2720+
uint max_stage= MY_MAX(tmp->progress.max_stage, 1);
2721+
thd_info->progress= (((tmp->progress.stage / (double) max_stage) +
2722+
((tmp->progress.counter /
2723+
(double) tmp->progress.max_counter) /
2724+
(double) max_stage)) *
2725+
100.0);
2726+
set_if_smaller(thd_info->progress, 100);
2727+
}
2728+
else
2729+
thd_info->progress= 0.0;
26952730
}
26962731
else
2732+
{
2733+
thd_info->proc_info= "Busy";
26972734
thd_info->progress= 0.0;
2735+
}
2736+
2737+
thd_info->state_info= thread_state_info(tmp);
26982738
thd_info->start_time= tmp->start_utime;
26992739
ulonglong utime_after_query_snapshot= tmp->utime_after_query;
27002740
if (thd_info->start_time < utime_after_query_snapshot)
27012741
thd_info->start_time= utime_after_query_snapshot; // COM_SLEEP
2702-
mysql_mutex_unlock(&tmp->LOCK_thd_data);
2742+
2743+
if (got_thd_data)
2744+
mysql_mutex_unlock(&tmp->LOCK_thd_data);
27032745
thread_infos.append(thd_info);
27042746
}
27052747
}
@@ -2938,7 +2980,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
29382980
explain_req.request_thd= thd;
29392981
explain_req.failed_to_produce= FALSE;
29402982

2941-
/* Ok, we have a lock on target->LOCK_thd_data, can call: */
2983+
/* Ok, we have a lock on target->LOCK_thd_kill, can call: */
29422984
bres= tmp->apc_target.make_apc_call(thd, &explain_req, timeout_sec, &timed_out);
29432985

29442986
if (bres || explain_req.failed_to_produce)
@@ -3017,9 +3059,10 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
30173059
while ((tmp= it++))
30183060
{
30193061
Security_context *tmp_sctx= tmp->security_ctx;
3020-
struct st_my_thread_var *mysys_var;
3062+
struct st_my_thread_var *mysys_var= 0;
30213063
const char *val, *db;
30223064
ulonglong max_counter;
3065+
bool got_thd_data, got_mysys_lock= 0;
30233066

30243067
if ((!tmp->vio_ok() && !tmp->system_thread) ||
30253068
(user && (tmp->system_thread || !tmp_sctx->user ||
@@ -3045,23 +3088,33 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
30453088
else
30463089
table->field[2]->store(tmp_sctx->host_or_ip,
30473090
strlen(tmp_sctx->host_or_ip), cs);
3048-
/* DB */
3049-
mysql_mutex_lock(&tmp->LOCK_thd_data);
3050-
if ((db= tmp->db))
3091+
3092+
if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
3093+
if ((mysys_var= tmp->mysys_var))
3094+
got_mysys_lock= !trylock_short(&mysys_var->mutex);
3095+
3096+
if (got_thd_data)
30513097
{
3052-
table->field[3]->store(db, strlen(db), cs);
3053-
table->field[3]->set_notnull();
3098+
/* DB */
3099+
if ((db= tmp->db))
3100+
{
3101+
table->field[3]->store(db, strlen(db), cs);
3102+
table->field[3]->set_notnull();
3103+
}
30543104
}
30553105

3056-
if ((mysys_var= tmp->mysys_var))
3057-
mysql_mutex_lock(&mysys_var->mutex);
30583106
/* COMMAND */
3059-
if ((val= (char *) ((tmp->killed >= KILL_QUERY ?
3107+
if ((val= (char *) (!got_thd_data ? "Busy" :
3108+
(tmp->killed >= KILL_QUERY ?
30603109
"Killed" : 0))))
30613110
table->field[4]->store(val, strlen(val), cs);
30623111
else
30633112
table->field[4]->store(command_name[tmp->get_command()].str,
30643113
command_name[tmp->get_command()].length, cs);
3114+
3115+
if (got_mysys_lock)
3116+
mysql_mutex_unlock(&mysys_var->mutex);
3117+
30653118
/* MYSQL_TIME */
30663119
ulonglong utime= tmp->start_utime;
30673120
ulonglong utime_after_query_snapshot= tmp->utime_after_query;
@@ -3070,52 +3123,47 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
30703123
utime= utime && utime < unow ? unow - utime : 0;
30713124

30723125
table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE);
3126+
30733127
/* STATE */
30743128
if ((val= thread_state_info(tmp)))
30753129
{
30763130
table->field[6]->store(val, strlen(val), cs);
30773131
table->field[6]->set_notnull();
30783132
}
30793133

3080-
if (mysys_var)
3081-
mysql_mutex_unlock(&mysys_var->mutex);
3082-
mysql_mutex_unlock(&tmp->LOCK_thd_data);
3083-
3084-
/* TIME_MS */
3085-
table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0)));
3086-
3087-
/* INFO */
3088-
/* Lock THD mutex that protects its data when looking at it. */
3089-
mysql_mutex_lock(&tmp->LOCK_thd_data);
3090-
if (tmp->query())
3134+
if (got_thd_data)
30913135
{
3092-
table->field[7]->store(tmp->query(),
3093-
MY_MIN(PROCESS_LIST_INFO_WIDTH,
3094-
tmp->query_length()), cs);
3095-
table->field[7]->set_notnull();
3096-
}
3136+
if (tmp->query())
3137+
{
3138+
table->field[7]->store(tmp->query(),
3139+
MY_MIN(PROCESS_LIST_INFO_WIDTH,
3140+
tmp->query_length()), cs);
3141+
table->field[7]->set_notnull();
3142+
3143+
/* INFO_BINARY */
3144+
table->field[16]->store(tmp->query(),
3145+
MY_MIN(PROCESS_LIST_INFO_WIDTH,
3146+
tmp->query_length()),
3147+
&my_charset_bin);
3148+
table->field[16]->set_notnull();
3149+
}
30973150

3098-
/* INFO_BINARY */
3099-
if (tmp->query())
3100-
{
3101-
table->field[16]->store(tmp->query(),
3102-
MY_MIN(PROCESS_LIST_INFO_WIDTH,
3103-
tmp->query_length()), &my_charset_bin);
3104-
table->field[16]->set_notnull();
3151+
/*
3152+
Progress report. We need to do this under a lock to ensure that all
3153+
is from the same stage.
3154+
*/
3155+
if ((max_counter= tmp->progress.max_counter))
3156+
{
3157+
table->field[9]->store((longlong) tmp->progress.stage + 1, 1);
3158+
table->field[10]->store((longlong) tmp->progress.max_stage, 1);
3159+
table->field[11]->store((double) tmp->progress.counter /
3160+
(double) max_counter*100.0);
3161+
}
3162+
mysql_mutex_unlock(&tmp->LOCK_thd_data);
31053163
}
31063164

3107-
/*
3108-
Progress report. We need to do this under a lock to ensure that all
3109-
is from the same stage.
3110-
*/
3111-
if ((max_counter= tmp->progress.max_counter))
3112-
{
3113-
table->field[9]->store((longlong) tmp->progress.stage + 1, 1);
3114-
table->field[10]->store((longlong) tmp->progress.max_stage, 1);
3115-
table->field[11]->store((double) tmp->progress.counter /
3116-
(double) max_counter*100.0);
3117-
}
3118-
mysql_mutex_unlock(&tmp->LOCK_thd_data);
3165+
/* TIME_MS */
3166+
table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0)));
31193167

31203168
/*
31213169
This may become negative if we free a memory allocated by another

0 commit comments

Comments
 (0)