1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-18 23:03:28 +03:00

Move all kill mutex protection to LOCK_thd_kill

LOCK_thd_data was used to protect both THD data and
ensure that the THD is not deleted while it was in use

This patch moves the THD delete protection to LOCK_thd_kill,
which already protects the THD for kill.

The benefits are:
- More well defined what LOCK_thd_data protects
- LOCK_thd_data usage is now much simpler and easier to verify
- Less chance of deadlocks in SHOW PROCESS LIST as there is less
  chance of interactions between mutexes
- Remove not needed LOCK_thread_count from
  thd_get_error_context_description()
- Fewer mutex taken for thd->awake()

Other things:
- Don't take mysys->var mutex in show processlist to check if thread
  is kill marked
- thd->awake() now automatically takes the LOCK_thd_kill mutex
  (Simplifies code)
- Apc uses LOCK_thd_kill instead of LOCK_thd_data
This commit is contained in:
Monty
2017-12-07 21:28:00 +02:00
parent 3574f9c7bc
commit c2118a08b1
15 changed files with 109 additions and 120 deletions

View File

@ -125,7 +125,7 @@ relative_event_id relative_end_event_id event_name comment nesting_event_type re
15 15 stage/sql/Starting cleanup (stage) STATEMENT 0 15 15 stage/sql/Starting cleanup (stage) STATEMENT 0
16 16 stage/sql/Freeing items (stage) STATEMENT 0 16 16 stage/sql/Freeing items (stage) STATEMENT 0
17 17 wait/io/socket/sql/client_connection send STATEMENT 0 17 17 wait/io/socket/sql/client_connection send STATEMENT 0
18 18 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 0 18 18 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 0
19 20 stage/sql/Reset for next command (stage) STATEMENT 0 19 20 stage/sql/Reset for next command (stage) STATEMENT 0
20 20 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 19 20 20 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 19
21 21 idle idle NULL NULL 21 21 idle idle NULL NULL
@ -147,7 +147,7 @@ relative_event_id relative_end_event_id event_name comment nesting_event_type re
37 37 stage/sql/Starting cleanup (stage) STATEMENT 22 37 37 stage/sql/Starting cleanup (stage) STATEMENT 22
38 38 stage/sql/Freeing items (stage) STATEMENT 22 38 38 stage/sql/Freeing items (stage) STATEMENT 22
39 39 wait/io/socket/sql/client_connection send STATEMENT 22 39 39 wait/io/socket/sql/client_connection send STATEMENT 22
40 40 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 22 40 40 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 22
41 42 stage/sql/Reset for next command (stage) STATEMENT 22 41 42 stage/sql/Reset for next command (stage) STATEMENT 22
42 42 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 41 42 42 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 41
43 43 idle idle NULL NULL 43 43 idle idle NULL NULL
@ -169,7 +169,7 @@ relative_event_id relative_end_event_id event_name comment nesting_event_type re
59 59 stage/sql/Starting cleanup (stage) STATEMENT 44 59 59 stage/sql/Starting cleanup (stage) STATEMENT 44
60 60 stage/sql/Freeing items (stage) STATEMENT 44 60 60 stage/sql/Freeing items (stage) STATEMENT 44
61 61 wait/io/socket/sql/client_connection send STATEMENT 44 61 61 wait/io/socket/sql/client_connection send STATEMENT 44
62 62 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 44 62 62 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 44
63 64 stage/sql/Reset for next command (stage) STATEMENT 44 63 64 stage/sql/Reset for next command (stage) STATEMENT 44
64 64 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 63 64 64 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 63
65 65 idle idle NULL NULL 65 65 idle idle NULL NULL
@ -194,7 +194,7 @@ select "With a third part to make things complete" as payload NULL NULL
82 82 stage/sql/Starting cleanup (stage) STATEMENT 66 82 82 stage/sql/Starting cleanup (stage) STATEMENT 66
83 85 stage/sql/Freeing items (stage) STATEMENT 66 83 85 stage/sql/Freeing items (stage) STATEMENT 66
84 84 wait/io/socket/sql/client_connection send STAGE 83 84 84 wait/io/socket/sql/client_connection send STAGE 83
85 85 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 83 85 85 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 83
86 103 statement/sql/select select "And this is the second part of a multi query" as payload; 86 103 statement/sql/select select "And this is the second part of a multi query" as payload;
select "With a third part to make things complete" as payload NULL NULL select "With a third part to make things complete" as payload NULL NULL
87 89 stage/sql/Init (stage) STATEMENT 86 87 89 stage/sql/Init (stage) STATEMENT 86
@ -213,7 +213,7 @@ select "With a third part to make things complete" as payload NULL NULL
100 100 stage/sql/Starting cleanup (stage) STATEMENT 86 100 100 stage/sql/Starting cleanup (stage) STATEMENT 86
101 103 stage/sql/Freeing items (stage) STATEMENT 86 101 103 stage/sql/Freeing items (stage) STATEMENT 86
102 102 wait/io/socket/sql/client_connection send STAGE 101 102 102 wait/io/socket/sql/client_connection send STAGE 101
103 103 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 101 103 103 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STAGE 101
104 122 statement/sql/select select "With a third part to make things complete" as payload NULL NULL 104 122 statement/sql/select select "With a third part to make things complete" as payload NULL NULL
105 106 stage/sql/Init (stage) STATEMENT 104 105 106 stage/sql/Init (stage) STATEMENT 104
106 106 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 105 106 106 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 105
@ -230,7 +230,7 @@ select "With a third part to make things complete" as payload NULL NULL
117 117 stage/sql/Starting cleanup (stage) STATEMENT 104 117 117 stage/sql/Starting cleanup (stage) STATEMENT 104
118 118 stage/sql/Freeing items (stage) STATEMENT 104 118 118 stage/sql/Freeing items (stage) STATEMENT 104
119 119 wait/io/socket/sql/client_connection send STATEMENT 104 119 119 wait/io/socket/sql/client_connection send STATEMENT 104
120 120 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 104 120 120 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 104
121 122 stage/sql/Reset for next command (stage) STATEMENT 104 121 122 stage/sql/Reset for next command (stage) STATEMENT 104
122 122 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 121 122 122 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 121
123 123 idle idle NULL NULL 123 123 idle idle NULL NULL
@ -252,7 +252,7 @@ select "With a third part to make things complete" as payload NULL NULL
139 139 stage/sql/Starting cleanup (stage) STATEMENT 124 139 139 stage/sql/Starting cleanup (stage) STATEMENT 124
140 140 stage/sql/Freeing items (stage) STATEMENT 124 140 140 stage/sql/Freeing items (stage) STATEMENT 124
141 141 wait/io/socket/sql/client_connection send STATEMENT 124 141 141 wait/io/socket/sql/client_connection send STATEMENT 124
142 142 wait/synch/mutex/sql/THD::LOCK_thd_data lock STATEMENT 124 142 142 wait/synch/mutex/sql/THD::LOCK_thd_kill lock STATEMENT 124
143 144 stage/sql/Reset for next command (stage) STATEMENT 124 143 144 stage/sql/Reset for next command (stage) STATEMENT 124
144 144 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 143 144 144 wait/synch/mutex/sql/THD::LOCK_thd_data lock STAGE 143
disconnect con1; disconnect con1;

View File

@ -39,6 +39,7 @@ update performance_schema.setup_instruments set enabled='YES', timed='YES'
'wait/io/socket/sql/client_connection', 'wait/io/socket/sql/client_connection',
'wait/synch/rwlock/sql/LOCK_grant', 'wait/synch/rwlock/sql/LOCK_grant',
'wait/synch/mutex/sql/THD::LOCK_thd_data', 'wait/synch/mutex/sql/THD::LOCK_thd_data',
'wait/synch/mutex/sql/THD::LOCK_thd_kill',
'wait/io/file/sql/query_log'); 'wait/io/file/sql/query_log');
update performance_schema.setup_instruments set enabled='YES', timed='YES' update performance_schema.setup_instruments set enabled='YES', timed='YES'

View File

@ -648,14 +648,11 @@ Event_scheduler::stop()
state= STOPPING; state= STOPPING;
DBUG_PRINT("info", ("Scheduler thread has id %lu", DBUG_PRINT("info", ("Scheduler thread has id %lu",
(ulong) scheduler_thd->thread_id)); (ulong) scheduler_thd->thread_id));
/* Lock from delete */
mysql_mutex_lock(&scheduler_thd->LOCK_thd_data);
/* This will wake up the thread if it waits on Queue's conditional */ /* This will wake up the thread if it waits on Queue's conditional */
sql_print_information("Event Scheduler: Killing the scheduler thread, " sql_print_information("Event Scheduler: Killing the scheduler thread, "
"thread id %lu", "thread id %lu",
(ulong) scheduler_thd->thread_id); (ulong) scheduler_thd->thread_id);
scheduler_thd->awake(KILL_CONNECTION); scheduler_thd->awake(KILL_CONNECTION);
mysql_mutex_unlock(&scheduler_thd->LOCK_thd_data);
/* thd could be 0x0, when shutting down */ /* thd could be 0x0, when shutting down */
sql_print_information("Event Scheduler: " sql_print_information("Event Scheduler: "

View File

@ -34,7 +34,7 @@
void Apc_target::init(mysql_mutex_t *target_mutex) void Apc_target::init(mysql_mutex_t *target_mutex)
{ {
DBUG_ASSERT(!enabled); DBUG_ASSERT(!enabled);
LOCK_thd_data_ptr= target_mutex; LOCK_thd_kill_ptr= target_mutex;
#ifndef DBUG_OFF #ifndef DBUG_OFF
n_calls_processed= 0; n_calls_processed= 0;
#endif #endif
@ -45,7 +45,7 @@ void Apc_target::init(mysql_mutex_t *target_mutex)
void Apc_target::enqueue_request(Call_request *qe) void Apc_target::enqueue_request(Call_request *qe)
{ {
mysql_mutex_assert_owner(LOCK_thd_data_ptr); mysql_mutex_assert_owner(LOCK_thd_kill_ptr);
if (apc_calls) if (apc_calls)
{ {
Call_request *after= apc_calls->prev; Call_request *after= apc_calls->prev;
@ -71,7 +71,7 @@ void Apc_target::enqueue_request(Call_request *qe)
void Apc_target::dequeue_request(Call_request *qe) void Apc_target::dequeue_request(Call_request *qe)
{ {
mysql_mutex_assert_owner(LOCK_thd_data_ptr); mysql_mutex_assert_owner(LOCK_thd_kill_ptr);
if (apc_calls == qe) if (apc_calls == qe)
{ {
if ((apc_calls= apc_calls->next) == qe) if ((apc_calls= apc_calls->next) == qe)
@ -145,14 +145,14 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
int wait_res= 0; int wait_res= 0;
PSI_stage_info old_stage; PSI_stage_info old_stage;
caller_thd->ENTER_COND(&apc_request.COND_request, LOCK_thd_data_ptr, caller_thd->ENTER_COND(&apc_request.COND_request, LOCK_thd_kill_ptr,
&stage_show_explain, &old_stage); &stage_show_explain, &old_stage);
/* todo: how about processing other errors here? */ /* todo: how about processing other errors here? */
while (!apc_request.processed && (wait_res != ETIMEDOUT)) while (!apc_request.processed && (wait_res != ETIMEDOUT))
{ {
/* We own LOCK_thd_data_ptr */ /* We own LOCK_thd_kill_ptr */
wait_res= mysql_cond_timedwait(&apc_request.COND_request, wait_res= mysql_cond_timedwait(&apc_request.COND_request,
LOCK_thd_data_ptr, &abstime); LOCK_thd_kill_ptr, &abstime);
// &apc_request.LOCK_request, &abstime); // &apc_request.LOCK_request, &abstime);
if (caller_thd->killed) if (caller_thd->killed)
break; break;
@ -163,7 +163,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
/* /*
The wait has timed out, or this thread was KILLed. The wait has timed out, or this thread was KILLed.
Remove the request from the queue (ok to do because we own Remove the request from the queue (ok to do because we own
LOCK_thd_data_ptr) LOCK_thd_kill_ptr)
*/ */
apc_request.processed= TRUE; apc_request.processed= TRUE;
dequeue_request(&apc_request); dequeue_request(&apc_request);
@ -176,7 +176,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
res= FALSE; res= FALSE;
} }
/* /*
exit_cond() will call mysql_mutex_unlock(LOCK_thd_data_ptr) for us: exit_cond() will call mysql_mutex_unlock(LOCK_thd_kill_ptr) for us:
*/ */
caller_thd->EXIT_COND(&old_stage); caller_thd->EXIT_COND(&old_stage);
@ -185,7 +185,7 @@ bool Apc_target::make_apc_call(THD *caller_thd, Apc_call *call,
} }
else else
{ {
mysql_mutex_unlock(LOCK_thd_data_ptr); mysql_mutex_unlock(LOCK_thd_kill_ptr);
} }
return res; return res;
} }
@ -202,11 +202,11 @@ void Apc_target::process_apc_requests()
{ {
Call_request *request; Call_request *request;
mysql_mutex_lock(LOCK_thd_data_ptr); mysql_mutex_lock(LOCK_thd_kill_ptr);
if (!(request= get_first_in_queue())) if (!(request= get_first_in_queue()))
{ {
/* No requests in the queue */ /* No requests in the queue */
mysql_mutex_unlock(LOCK_thd_data_ptr); mysql_mutex_unlock(LOCK_thd_kill_ptr);
break; break;
} }
@ -225,7 +225,7 @@ void Apc_target::process_apc_requests()
n_calls_processed++; n_calls_processed++;
#endif #endif
mysql_cond_signal(&request->COND_request); mysql_cond_signal(&request->COND_request);
mysql_mutex_unlock(LOCK_thd_data_ptr); mysql_mutex_unlock(LOCK_thd_kill_ptr);
} }
} }

View File

@ -44,7 +44,7 @@ class THD;
*/ */
class Apc_target class Apc_target
{ {
mysql_mutex_t *LOCK_thd_data_ptr; mysql_mutex_t *LOCK_thd_kill_ptr;
public: public:
Apc_target() : enabled(0), apc_calls(NULL) {} Apc_target() : enabled(0), apc_calls(NULL) {}
~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);} ~Apc_target() { DBUG_ASSERT(!enabled && !apc_calls);}
@ -66,9 +66,9 @@ public:
void disable() void disable()
{ {
DBUG_ASSERT(enabled); DBUG_ASSERT(enabled);
mysql_mutex_lock(LOCK_thd_data_ptr); mysql_mutex_lock(LOCK_thd_kill_ptr);
bool process= !--enabled && have_apc_requests(); bool process= !--enabled && have_apc_requests();
mysql_mutex_unlock(LOCK_thd_data_ptr); mysql_mutex_unlock(LOCK_thd_kill_ptr);
if (unlikely(process)) if (unlikely(process))
process_apc_requests(); process_apc_requests();
} }

View File

@ -1704,7 +1704,7 @@ static void close_connections(void)
#endif #endif
tmp->set_killed(KILL_SERVER_HARD); tmp->set_killed(KILL_SERVER_HARD);
MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp)); MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp));
mysql_mutex_lock(&tmp->LOCK_thd_data); mysql_mutex_lock(&tmp->LOCK_thd_kill);
if (tmp->mysys_var) if (tmp->mysys_var)
{ {
tmp->mysys_var->abort=1; tmp->mysys_var->abort=1;
@ -1727,7 +1727,7 @@ static void close_connections(void)
} }
mysql_mutex_unlock(&tmp->mysys_var->mutex); mysql_mutex_unlock(&tmp->mysys_var->mutex);
} }
mysql_mutex_unlock(&tmp->LOCK_thd_data); mysql_mutex_unlock(&tmp->LOCK_thd_kill);
} }
mysql_mutex_unlock(&LOCK_thread_count); // For unlink from list mysql_mutex_unlock(&LOCK_thread_count); // For unlink from list

View File

@ -343,9 +343,7 @@ handle_slave_background(void *arg __attribute__((unused)))
THD *to_kill= p->to_kill; THD *to_kill= p->to_kill;
kill_list= p->next; kill_list= p->next;
mysql_mutex_lock(&to_kill->LOCK_thd_data);
to_kill->awake(KILL_CONNECTION); to_kill->awake(KILL_CONNECTION);
mysql_mutex_unlock(&to_kill->LOCK_thd_data);
mysql_mutex_lock(&to_kill->LOCK_wakeup_ready); mysql_mutex_lock(&to_kill->LOCK_wakeup_ready);
to_kill->rgi_slave->killed_for_retry= to_kill->rgi_slave->killed_for_retry=
rpl_group_info::RETRY_KILL_KILLED; rpl_group_info::RETRY_KILL_KILLED;
@ -856,7 +854,7 @@ terminate_slave_thread(THD *thd,
int error __attribute__((unused)); int error __attribute__((unused));
DBUG_PRINT("loop", ("killing slave thread")); DBUG_PRINT("loop", ("killing slave thread"));
mysql_mutex_lock(&thd->LOCK_thd_data); mysql_mutex_lock(&thd->LOCK_thd_kill);
#ifndef DONT_USE_THR_ALARM #ifndef DONT_USE_THR_ALARM
/* /*
Error codes from pthread_kill are: Error codes from pthread_kill are:
@ -866,9 +864,9 @@ terminate_slave_thread(THD *thd,
int err __attribute__((unused))= pthread_kill(thd->real_id, thr_client_alarm); int err __attribute__((unused))= pthread_kill(thd->real_id, thr_client_alarm);
DBUG_ASSERT(err != EINVAL); DBUG_ASSERT(err != EINVAL);
#endif #endif
thd->awake(NOT_KILLED); thd->awake_no_mutex(NOT_KILLED);
mysql_mutex_unlock(&thd->LOCK_thd_data); mysql_mutex_unlock(&thd->LOCK_thd_kill);
/* /*
There is a small chance that slave thread might miss the first There is a small chance that slave thread might miss the first

View File

@ -555,8 +555,6 @@ char *thd_get_error_context_description(THD *thd, char *buffer,
char header[256]; char header[256];
int len; int len;
mysql_mutex_lock(&LOCK_thread_count);
/* /*
The pointers thd->query and thd->proc_info might change since they are The pointers thd->query and thd->proc_info might change since they are
being modified concurrently. This is acceptable for proc_info since its being modified concurrently. This is acceptable for proc_info since its
@ -612,7 +610,6 @@ char *thd_get_error_context_description(THD *thd, char *buffer,
} }
mysql_mutex_unlock(&thd->LOCK_thd_data); mysql_mutex_unlock(&thd->LOCK_thd_data);
} }
mysql_mutex_unlock(&LOCK_thread_count);
if (str.c_ptr_safe() == buffer) if (str.c_ptr_safe() == buffer)
return buffer; return buffer;
@ -704,10 +701,8 @@ handle_condition(THD *thd,
extern "C" void thd_kill_timeout(THD* thd) extern "C" void thd_kill_timeout(THD* thd)
{ {
thd->status_var.max_statement_time_exceeded++; thd->status_var.max_statement_time_exceeded++;
mysql_mutex_lock(&thd->LOCK_thd_data);
/* Kill queries that can't cause data corruptions */ /* Kill queries that can't cause data corruptions */
thd->awake(KILL_TIMEOUT); thd->awake(KILL_TIMEOUT);
mysql_mutex_unlock(&thd->LOCK_thd_data);
} }
@ -1363,7 +1358,7 @@ void THD::init(void)
session_tracker.enable(this); session_tracker.enable(this);
#endif //EMBEDDED_LIBRARY #endif //EMBEDDED_LIBRARY
apc_target.init(&LOCK_thd_data); apc_target.init(&LOCK_thd_kill);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -1627,9 +1622,13 @@ THD::~THD()
if (!status_in_global) if (!status_in_global)
add_status_to_global(); add_status_to_global();
/* Ensure that no one is using THD */ /*
mysql_mutex_lock(&LOCK_thd_data); Other threads may have a lock on LOCK_thd_kill to ensure that this
mysql_mutex_unlock(&LOCK_thd_data); THD is not deleted while they access it. The following mutex_lock
ensures that no one else is using this THD and it's now safe to delete
*/
mysql_mutex_lock(&LOCK_thd_kill);
mysql_mutex_unlock(&LOCK_thd_kill);
#ifdef WITH_WSREP #ifdef WITH_WSREP
mysql_mutex_lock(&LOCK_wsrep_thd); mysql_mutex_lock(&LOCK_wsrep_thd);
@ -1802,17 +1801,17 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
This is normally called from another thread's THD object. This is normally called from another thread's THD object.
@note Do always call this while holding LOCK_thd_data. @note Do always call this while holding LOCK_thd_kill.
NOT_KILLED is used to awake a thread for a slave NOT_KILLED is used to awake a thread for a slave
*/ */
void THD::awake(killed_state state_to_set) void THD::awake_no_mutex(killed_state state_to_set)
{ {
DBUG_ENTER("THD::awake"); DBUG_ENTER("THD::awake");
DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d", DBUG_PRINT("enter", ("this: %p current_thd: %p state: %d",
this, current_thd, (int) state_to_set)); this, current_thd, (int) state_to_set));
THD_CHECK_SENTRY(this); THD_CHECK_SENTRY(this);
mysql_mutex_assert_owner(&LOCK_thd_data); mysql_mutex_assert_owner(&LOCK_thd_kill);
print_aborted_warning(3, "KILLED"); print_aborted_warning(3, "KILLED");
@ -1823,8 +1822,6 @@ void THD::awake(killed_state state_to_set)
if (killed >= KILL_CONNECTION) if (killed >= KILL_CONNECTION)
state_to_set= killed; state_to_set= killed;
/* Set the 'killed' flag of 'this', which is the target THD object. */
mysql_mutex_lock(&LOCK_thd_kill);
set_killed_no_mutex(state_to_set); set_killed_no_mutex(state_to_set);
if (state_to_set >= KILL_CONNECTION || state_to_set == NOT_KILLED) if (state_to_set >= KILL_CONNECTION || state_to_set == NOT_KILLED)
@ -1911,7 +1908,6 @@ void THD::awake(killed_state state_to_set)
} }
mysql_mutex_unlock(&mysys_var->mutex); mysql_mutex_unlock(&mysys_var->mutex);
} }
mysql_mutex_unlock(&LOCK_thd_kill);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
@ -1927,10 +1923,10 @@ void THD::disconnect()
{ {
Vio *vio= NULL; Vio *vio= NULL;
mysql_mutex_lock(&LOCK_thd_data);
set_killed(KILL_CONNECTION); set_killed(KILL_CONNECTION);
mysql_mutex_lock(&LOCK_thd_data);
#ifdef SIGNAL_WITH_VIO_CLOSE #ifdef SIGNAL_WITH_VIO_CLOSE
/* /*
Since a active vio might might have not been set yet, in Since a active vio might might have not been set yet, in
@ -1963,9 +1959,9 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
{ {
/* This code is similar to kill_delayed_threads() */ /* This code is similar to kill_delayed_threads() */
DBUG_PRINT("info", ("kill delayed thread")); DBUG_PRINT("info", ("kill delayed thread"));
mysql_mutex_lock(&in_use->LOCK_thd_data); mysql_mutex_lock(&in_use->LOCK_thd_kill);
if (in_use->killed < KILL_CONNECTION) if (in_use->killed < KILL_CONNECTION)
in_use->set_killed(KILL_CONNECTION); in_use->set_killed_no_mutex(KILL_CONNECTION);
if (in_use->mysys_var) if (in_use->mysys_var)
{ {
mysql_mutex_lock(&in_use->mysys_var->mutex); mysql_mutex_lock(&in_use->mysys_var->mutex);
@ -1976,7 +1972,7 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use,
in_use->mysys_var->abort= 1; in_use->mysys_var->abort= 1;
mysql_mutex_unlock(&in_use->mysys_var->mutex); mysql_mutex_unlock(&in_use->mysys_var->mutex);
} }
mysql_mutex_unlock(&in_use->LOCK_thd_data); mysql_mutex_unlock(&in_use->LOCK_thd_kill);
signalled= TRUE; signalled= TRUE;
} }
@ -2084,7 +2080,7 @@ bool THD::store_globals()
return 1; return 1;
/* /*
mysys_var is concurrently readable by a killer thread. mysys_var is concurrently readable by a killer thread.
It is protected by LOCK_thd_data, it is not needed to lock while the It is protected by LOCK_thd_kill, it is not needed to lock while the
pointer is changing from NULL not non-NULL. If the kill thread reads pointer is changing from NULL not non-NULL. If the kill thread reads
NULL it doesn't refer to anything, but if it is non-NULL we need to NULL it doesn't refer to anything, but if it is non-NULL we need to
ensure that the thread doesn't proceed to assign another thread to ensure that the thread doesn't proceed to assign another thread to
@ -2135,9 +2131,9 @@ bool THD::store_globals()
void THD::reset_globals() void THD::reset_globals()
{ {
mysql_mutex_lock(&LOCK_thd_data); mysql_mutex_lock(&LOCK_thd_kill);
mysys_var= 0; mysys_var= 0;
mysql_mutex_unlock(&LOCK_thd_data); mysql_mutex_unlock(&LOCK_thd_kill);
/* Undocking the thread specific data. */ /* Undocking the thread specific data. */
set_current_thd(0); set_current_thd(0);
@ -5429,9 +5425,9 @@ void THD::set_query_and_id(char *query_arg, uint32 query_length_arg,
/** Assign a new value to thd->mysys_var. */ /** Assign a new value to thd->mysys_var. */
void THD::set_mysys_var(struct st_my_thread_var *new_mysys_var) void THD::set_mysys_var(struct st_my_thread_var *new_mysys_var)
{ {
mysql_mutex_lock(&LOCK_thd_data); mysql_mutex_lock(&LOCK_thd_kill);
mysys_var= new_mysys_var; mysys_var= new_mysys_var;
mysql_mutex_unlock(&LOCK_thd_data); mysql_mutex_unlock(&LOCK_thd_kill);
} }
/** /**

View File

@ -2127,11 +2127,15 @@ public:
- thd->query and thd->query_length (used by SHOW ENGINE - thd->query and thd->query_length (used by SHOW ENGINE
INNODB STATUS and SHOW PROCESSLIST INNODB STATUS and SHOW PROCESSLIST
- thd->db and thd->db_length (used in SHOW PROCESSLIST) - thd->db and thd->db_length (used in SHOW PROCESSLIST)
- thd->mysys_var (used by KILL statement and shutdown).
Is locked when THD is deleted. Is locked when THD is deleted.
*/ */
mysql_mutex_t LOCK_thd_data; mysql_mutex_t LOCK_thd_data;
/* Protect kill information */ /*
Protects:
- kill information
- mysys_var (used by KILL statement and shutdown).
- Also ensures that THD is not deleted while mutex is hold
*/
mysql_mutex_t LOCK_thd_kill; mysql_mutex_t LOCK_thd_kill;
/* all prepared statements and cursors of this connection */ /* all prepared statements and cursors of this connection */
@ -3131,7 +3135,13 @@ public:
} }
void close_active_vio(); void close_active_vio();
#endif #endif
void awake(killed_state state_to_set); void awake_no_mutex(killed_state state_to_set);
void awake(killed_state state_to_set)
{
mysql_mutex_lock(&LOCK_thd_kill);
awake_no_mutex(state_to_set);
mysql_mutex_unlock(&LOCK_thd_kill);
}
/** Disconnect the associated communication endpoint. */ /** Disconnect the associated communication endpoint. */
void disconnect(); void disconnect();

View File

@ -2702,9 +2702,9 @@ void kill_delayed_threads(void)
Delayed_insert *di; Delayed_insert *di;
while ((di= it++)) while ((di= it++))
{ {
mysql_mutex_lock(&di->thd.LOCK_thd_data); mysql_mutex_lock(&di->thd.LOCK_thd_kill);
if (di->thd.killed < KILL_CONNECTION) if (di->thd.killed < KILL_CONNECTION)
di->thd.set_killed(KILL_CONNECTION); di->thd.set_killed_no_mutex(KILL_CONNECTION);
if (di->thd.mysys_var) if (di->thd.mysys_var)
{ {
mysql_mutex_lock(&di->thd.mysys_var->mutex); mysql_mutex_lock(&di->thd.mysys_var->mutex);
@ -2722,7 +2722,7 @@ void kill_delayed_threads(void)
} }
mysql_mutex_unlock(&di->thd.mysys_var->mutex); mysql_mutex_unlock(&di->thd.mysys_var->mutex);
} }
mysql_mutex_unlock(&di->thd.LOCK_thd_data); mysql_mutex_unlock(&di->thd.LOCK_thd_kill);
} }
mysql_mutex_unlock(&LOCK_delayed_insert); // For unlink from list mysql_mutex_unlock(&LOCK_delayed_insert); // For unlink from list
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
@ -3076,9 +3076,9 @@ pthread_handler_t handle_delayed_insert(void *arg)
this. this.
*/ */
mysql_mutex_lock(&thd->LOCK_thd_data); mysql_mutex_lock(&thd->LOCK_thd_data);
thd->set_killed(KILL_CONNECTION_HARD); // If error
thd->mdl_context.set_needs_thr_lock_abort(0); thd->mdl_context.set_needs_thr_lock_abort(0);
mysql_mutex_unlock(&thd->LOCK_thd_data); mysql_mutex_unlock(&thd->LOCK_thd_data);
thd->set_killed(KILL_CONNECTION_HARD); // If error
close_thread_tables(thd); // Free the table close_thread_tables(thd); // Free the table
thd->mdl_context.release_transactional_locks(); thd->mdl_context.release_transactional_locks();

View File

@ -8758,13 +8758,13 @@ void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields,
/** /**
Find a thread by id and return it, locking it LOCK_thd_data Find a thread by id and return it, locking it LOCK_thd_kill
@param id Identifier of the thread we're looking for @param id Identifier of the thread we're looking for
@param query_id If true, search by query_id instead of thread_id @param query_id If true, search by query_id instead of thread_id
@return NULL - not found @return NULL - not found
pointer - thread found, and its LOCK_thd_data is locked. pointer - thread found, and its LOCK_thd_kill is locked.
*/ */
THD *find_thread_by_id(longlong id, bool query_id) THD *find_thread_by_id(longlong id, bool query_id)
@ -8778,7 +8778,7 @@ THD *find_thread_by_id(longlong id, bool query_id)
continue; continue;
if (id == (query_id ? tmp->query_id : (longlong) tmp->thread_id)) if (id == (query_id ? tmp->query_id : (longlong) tmp->thread_id))
{ {
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
break; break;
} }
} }
@ -8834,13 +8834,13 @@ kill_one_thread(THD *thd, longlong id, killed_state kill_signal, killed_type typ
thd->security_ctx->user_matches(tmp->security_ctx)) && thd->security_ctx->user_matches(tmp->security_ctx)) &&
!wsrep_thd_is_BF(tmp, true)) !wsrep_thd_is_BF(tmp, true))
{ {
tmp->awake(kill_signal); tmp->awake_no_mutex(kill_signal);
error=0; error=0;
} }
else else
error= (type == KILL_TYPE_QUERY ? ER_KILL_QUERY_DENIED_ERROR : error= (type == KILL_TYPE_QUERY ? ER_KILL_QUERY_DENIED_ERROR :
ER_KILL_DENIED_ERROR); ER_KILL_DENIED_ERROR);
mysql_mutex_unlock(&tmp->LOCK_thd_data); mysql_mutex_unlock(&tmp->LOCK_thd_kill);
} }
DBUG_PRINT("exit", ("%d", error)); DBUG_PRINT("exit", ("%d", error));
DBUG_RETURN(error); DBUG_RETURN(error);
@ -8898,7 +8898,7 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
DBUG_RETURN(ER_KILL_DENIED_ERROR); DBUG_RETURN(ER_KILL_DENIED_ERROR);
} }
if (!threads_to_kill.push_back(tmp, thd->mem_root)) if (!threads_to_kill.push_back(tmp, thd->mem_root))
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
} }
} }
mysql_mutex_unlock(&LOCK_thread_count); mysql_mutex_unlock(&LOCK_thread_count);
@ -8909,17 +8909,17 @@ static uint kill_threads_for_user(THD *thd, LEX_USER *user,
THD *ptr= it2++; THD *ptr= it2++;
do do
{ {
ptr->awake(kill_signal); ptr->awake_no_mutex(kill_signal);
/* /*
Careful here: The list nodes are allocated on the memroots of the Careful here: The list nodes are allocated on the memroots of the
THDs to be awakened. THDs to be awakened.
But those THDs may be terminated and deleted as soon as we release But those THDs may be terminated and deleted as soon as we release
LOCK_thd_data, which will make the list nodes invalid. LOCK_thd_kill, which will make the list nodes invalid.
Since the operation "it++" dereferences the "next" pointer of the Since the operation "it++" dereferences the "next" pointer of the
previous list node, we need to do this while holding LOCK_thd_data. previous list node, we need to do this while holding LOCK_thd_kill.
*/ */
next_ptr= it2++; next_ptr= it2++;
mysql_mutex_unlock(&ptr->LOCK_thd_data); mysql_mutex_unlock(&ptr->LOCK_thd_kill);
(*rows)++; (*rows)++;
} while ((ptr= next_ptr)); } while ((ptr= next_ptr));
} }

View File

@ -3344,7 +3344,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
if (tmp->get_command() == COM_BINLOG_DUMP && if (tmp->get_command() == COM_BINLOG_DUMP &&
tmp->variables.server_id == slave_server_id) tmp->variables.server_id == slave_server_id)
{ {
mysql_mutex_lock(&tmp->LOCK_thd_data); // Lock from delete mysql_mutex_lock(&tmp->LOCK_thd_kill); // Lock from delete
break; break;
} }
} }
@ -3356,8 +3356,8 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
it will be slow because it will iterate through the list it will be slow because it will iterate through the list
again. We just to do kill the thread ourselves. again. We just to do kill the thread ourselves.
*/ */
tmp->awake(KILL_SLAVE_SAME_ID); tmp->awake_no_mutex(KILL_SLAVE_SAME_ID);
mysql_mutex_unlock(&tmp->LOCK_thd_data); mysql_mutex_unlock(&tmp->LOCK_thd_kill);
} }
} }

View File

@ -2578,23 +2578,28 @@ static const char *thread_state_info(THD *tmp)
{ {
if (tmp->net.reading_or_writing == 2) if (tmp->net.reading_or_writing == 2)
return "Writing to net"; return "Writing to net";
else if (tmp->get_command() == COM_SLEEP) if (tmp->get_command() == COM_SLEEP)
return ""; return "";
else return "Reading from net";
return "Reading from net";
} }
else
#endif #endif
if (tmp->proc_info)
return tmp->proc_info;
/* Check if we are waiting on a condition */
if (!trylock_short(&tmp->LOCK_thd_kill))
{ {
if (tmp->proc_info) /* mysys_var is protected by above mutex */
return tmp->proc_info; bool cond= tmp->mysys_var && tmp->mysys_var->current_cond;
else if (tmp->mysys_var && tmp->mysys_var->current_cond) mysql_mutex_unlock(&tmp->LOCK_thd_kill);
if (cond)
return "Waiting on cond"; return "Waiting on cond";
else
return NULL;
} }
return NULL;
} }
void mysqld_list_processes(THD *thd,const char *user, bool verbose) void mysqld_list_processes(THD *thd,const char *user, bool verbose)
{ {
Item *field; Item *field;
@ -2657,8 +2662,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
while ((tmp=it++)) while ((tmp=it++))
{ {
Security_context *tmp_sctx= tmp->security_ctx; Security_context *tmp_sctx= tmp->security_ctx;
struct st_my_thread_var *mysys_var= 0; bool got_thd_data;
bool got_thd_data, got_mysys_lock= 0;
if ((tmp->vio_ok() || tmp->system_thread) && if ((tmp->vio_ok() || tmp->system_thread) &&
(!user || (!tmp->system_thread && (!user || (!tmp->system_thread &&
tmp_sctx->user && !strcmp(tmp_sctx->user, user)))) tmp_sctx->user && !strcmp(tmp_sctx->user, user))))
@ -2684,17 +2688,10 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
thd_info->command=(int) tmp->get_command(); thd_info->command=(int) tmp->get_command();
if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
if ((mysys_var= tmp->mysys_var))
got_mysys_lock= !trylock_short(&mysys_var->mutex);
if (got_thd_data)
{ {
/* This is correct under mysys_lock, otherwise an approximation */ /* This is an approximation */
thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ? thd_info->proc_info= (char*) (tmp->killed >= KILL_QUERY ?
"Killed" : 0); "Killed" : 0);
if (got_mysys_lock)
mysql_mutex_unlock(&mysys_var->mutex);
/* /*
The following variables are only safe to access under a lock The following variables are only safe to access under a lock
*/ */
@ -2952,13 +2949,13 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
tmp_sctx->user))) tmp_sctx->user)))
{ {
my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESS"); my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "PROCESS");
mysql_mutex_unlock(&tmp->LOCK_thd_data); mysql_mutex_unlock(&tmp->LOCK_thd_kill);
DBUG_RETURN(1); DBUG_RETURN(1);
} }
if (tmp == thd) if (tmp == thd)
{ {
mysql_mutex_unlock(&tmp->LOCK_thd_data); mysql_mutex_unlock(&tmp->LOCK_thd_kill);
my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0)); my_error(ER_TARGET_NOT_EXPLAINABLE, MYF(0));
DBUG_RETURN(1); DBUG_RETURN(1);
} }
@ -2966,7 +2963,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond)
bool bres; bool bres;
/* /*
Ok we've found the thread of interest and it won't go away because Ok we've found the thread of interest and it won't go away because
we're holding its LOCK_thd data. Post it a SHOW EXPLAIN request. we're holding its LOCK_thd_kill. Post it a SHOW EXPLAIN request.
*/ */
bool timed_out; bool timed_out;
int timeout_sec= 30; int timeout_sec= 30;
@ -3059,10 +3056,9 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
while ((tmp= it++)) while ((tmp= it++))
{ {
Security_context *tmp_sctx= tmp->security_ctx; Security_context *tmp_sctx= tmp->security_ctx;
struct st_my_thread_var *mysys_var= 0;
const char *val, *db; const char *val, *db;
ulonglong max_counter; ulonglong max_counter;
bool got_thd_data, got_mysys_lock= 0; bool got_thd_data;
if ((!tmp->vio_ok() && !tmp->system_thread) || if ((!tmp->vio_ok() && !tmp->system_thread) ||
(user && (tmp->system_thread || !tmp_sctx->user || (user && (tmp->system_thread || !tmp_sctx->user ||
@ -3090,10 +3086,6 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
strlen(tmp_sctx->host_or_ip), cs); strlen(tmp_sctx->host_or_ip), cs);
if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data))) if ((got_thd_data= !trylock_short(&tmp->LOCK_thd_data)))
if ((mysys_var= tmp->mysys_var))
got_mysys_lock= !trylock_short(&mysys_var->mutex);
if (got_thd_data)
{ {
/* DB */ /* DB */
if ((db= tmp->db)) if ((db= tmp->db))
@ -3112,9 +3104,6 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
table->field[4]->store(command_name[tmp->get_command()].str, table->field[4]->store(command_name[tmp->get_command()].str,
command_name[tmp->get_command()].length, cs); command_name[tmp->get_command()].length, cs);
if (got_mysys_lock)
mysql_mutex_unlock(&mysys_var->mutex);
/* MYSQL_TIME */ /* MYSQL_TIME */
ulonglong utime= tmp->start_utime; ulonglong utime= tmp->start_utime;
ulonglong utime_after_query_snapshot= tmp->utime_after_query; ulonglong utime_after_query_snapshot= tmp->utime_after_query;
@ -3124,13 +3113,6 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE); table->field[5]->store(utime / HRTIME_RESOLUTION, TRUE);
/* STATE */
if ((val= thread_state_info(tmp)))
{
table->field[6]->store(val, strlen(val), cs);
table->field[6]->set_notnull();
}
if (got_thd_data) if (got_thd_data)
{ {
if (tmp->query()) if (tmp->query())
@ -3162,6 +3144,13 @@ int fill_schema_processlist(THD* thd, TABLE_LIST* tables, COND* cond)
mysql_mutex_unlock(&tmp->LOCK_thd_data); mysql_mutex_unlock(&tmp->LOCK_thd_data);
} }
/* STATE */
if ((val= thread_state_info(tmp)))
{
table->field[6]->store(val, strlen(val), cs);
table->field[6]->set_notnull();
}
/* TIME_MS */ /* TIME_MS */
table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0))); table->field[8]->store((double)(utime / (HRTIME_RESOLUTION / 1000.0)));

View File

@ -250,7 +250,7 @@ static THD* threadpool_add_connection(CONNECT *connect, void *scheduler_data)
} }
delete connect; delete connect;
add_to_active_threads(thd); add_to_active_threads(thd);
thd->mysys_var= mysys_var; thd->set_mysys_var(mysys_var);
thd->event_scheduler.data= scheduler_data; thd->event_scheduler.data= scheduler_data;
/* Create new PSI thread for use with the THD. */ /* Create new PSI thread for use with the THD. */
@ -477,11 +477,11 @@ void tp_timeout_handler(TP_connection *c)
if (c->state != TP_STATE_IDLE) if (c->state != TP_STATE_IDLE)
return; return;
THD *thd=c->thd; THD *thd=c->thd;
mysql_mutex_lock(&thd->LOCK_thd_data); mysql_mutex_lock(&thd->LOCK_thd_kill);
thd->set_killed(KILL_WAIT_TIMEOUT); thd->set_killed(KILL_WAIT_TIMEOUT);
c->priority= TP_PRIORITY_HIGH; c->priority= TP_PRIORITY_HIGH;
post_kill_notification(thd); post_kill_notification(thd);
mysql_mutex_unlock(&thd->LOCK_thd_data); mysql_mutex_unlock(&thd->LOCK_thd_kill);
} }

View File

@ -2492,9 +2492,7 @@ extern "C" void wsrep_thd_awake(THD *thd, my_bool signal)
{ {
if (signal) if (signal)
{ {
mysql_mutex_lock(&thd->LOCK_thd_data);
thd->awake(KILL_QUERY); thd->awake(KILL_QUERY);
mysql_mutex_unlock(&thd->LOCK_thd_data);
} }
else else
{ {