diff options
author | Jon Olav Hauglid <jon.hauglid@oracle.com> | 2010-11-18 16:01:58 +0100 |
---|---|---|
committer | Jon Olav Hauglid <jon.hauglid@oracle.com> | 2010-11-18 16:01:58 +0100 |
commit | 2215dc3cc1bc8858761d6366436cc4c1f697b1ed (patch) | |
tree | 7cc348d24f298fedc96ce3eef04478cb467d360f /sql/sql_class.cc | |
parent | a6f40e50719134a6489095b01ac9e41fc8af767d (diff) | |
parent | e0a17f56988d2c0d44a8c2107588b352f6dec07a (diff) | |
download | mariadb-git-2215dc3cc1bc8858761d6366436cc4c1f697b1ed.tar.gz |
Merge from mysql-5.5-runtime to mysql-5.5-bugteam
No conflicts
Diffstat (limited to 'sql/sql_class.cc')
-rw-r--r-- | sql/sql_class.cc | 73 |
1 files changed, 58 insertions, 15 deletions
diff --git a/sql/sql_class.cc b/sql/sql_class.cc index bace0295a04..2df7a2c8572 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1179,36 +1179,70 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, } +/** + Awake a thread. + + @param[in] state_to_set value for THD::killed + + This is normally called from another thread's THD object. + + @note Do always call this while holding LOCK_thd_data. +*/ + void THD::awake(THD::killed_state state_to_set) { DBUG_ENTER("THD::awake"); - DBUG_PRINT("enter", ("this: 0x%lx", (long) this)); + DBUG_PRINT("enter", ("this: %p current_thd: %p", this, current_thd)); THD_CHECK_SENTRY(this); mysql_mutex_assert_owner(&LOCK_thd_data); + /* Set the 'killed' flag of 'this', which is the target THD object. */ killed= state_to_set; + if (state_to_set != THD::KILL_QUERY) { - thr_alarm_kill(thread_id); - if (!slave_thread) - MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (this)); #ifdef SIGNAL_WITH_VIO_CLOSE if (this != current_thd) { /* - In addition to a signal, let's close the socket of the thread that - is being killed. This is to make sure it does not block if the - signal is lost. This needs to be done only on platforms where - signals are not a reliable interruption mechanism. - - If we're killing ourselves, we know that we're not blocked, so this - hack is not used. + Before sending a signal, let's close the socket of the thread + that is being killed ("this", which is not the current thread). + This is to make sure it does not block if the signal is lost. + This needs to be done only on platforms where signals are not + a reliable interruption mechanism. + + Note that the downside of this mechanism is that we could close + the connection while "this" target thread is in the middle of + sending a result to the application, thus violating the client- + server protocol. + + On the other hand, without closing the socket we have a race + condition. If "this" target thread passes the check of + thd->killed, and then the current thread runs through + THD::awake(), sets the 'killed' flag and completes the + signaling, and then the target thread runs into read(), it will + block on the socket. As a result of the discussions around + Bug#37780, it has been decided that we accept the race + condition. A second KILL awakes the target from read(). + + If we are killing ourselves, we know that we are not blocked. + We also know that we will check thd->killed before we go for + reading the next statement. */ close_active_vio(); } -#endif +#endif + + /* Mark the target thread's alarm request expired, and signal alarm. */ + thr_alarm_kill(thread_id); + + /* Send an event to the scheduler that a thread should be killed. */ + if (!slave_thread) + MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (this)); } + + /* Broadcast a condition to kick the target if it is waiting on it. */ if (mysys_var) { mysql_mutex_lock(&mysys_var->mutex); @@ -1232,6 +1266,11 @@ void THD::awake(THD::killed_state state_to_set) we issue a second KILL or the status it's waiting for happens). It's true that we have set its thd->killed but it may not see it immediately and so may have time to reach the cond_wait(). + + However, where possible, we test for killed once again after + enter_cond(). This should make the signaling as safe as possible. + However, there is still a small chance of failure on platforms with + instruction or memory write reordering. */ if (mysys_var->current_cond && mysys_var->current_mutex) { @@ -3456,11 +3495,15 @@ void THD::set_mysys_var(struct st_my_thread_var *new_mysys_var) void THD::leave_locked_tables_mode() { locked_tables_mode= LTM_NONE; - /* Make sure we don't release the global read lock when leaving LTM. */ - mdl_context.reset_trans_sentinel(global_read_lock.global_shared_lock()); + mdl_context.set_transaction_duration_for_all_locks(); + /* + Make sure we don't release the global read lock and commit blocker + when leaving LTM. + */ + global_read_lock.set_explicit_lock_duration(this); /* Also ensure that we don't release metadata locks for open HANDLERs. */ if (handler_tables_hash.records) - mysql_ha_move_tickets_after_trans_sentinel(this); + mysql_ha_set_explicit_lock_duration(this); } void THD::get_definer(LEX_USER *definer) |