>The problem is that pthread_unlock() must not access any structures of the lock, after giving up the ownership (allowing another thread to access), because that structure can be freed by a thread that is doing pthread_mutex_destroy() in parallel
you are right, it's not safe to destroy the mutex in parallel with it being held. POSIX also classifies it as UB.
The guarantee is given by ~THD destructor:
/*
|
Other threads may have a lock on LOCK_thd_kill to ensure that this
|
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);
|
Suchwise, if worker is reached execution again after c->start_io and reach threadpool_remove_connection() before LOCK_thd_kill will be unlocked, it will wait until it is unlocked there. This means, that it is safe to unlock LOCK_thd_kill after c->start_io(), if it was locked before.
The similar guarantee works when we are releasing LOCK_thd_kill by requestor threads, or by killer threads. Also that code in ~THD, but what makes it work there – is THD_list_iterator::iterate (actually it's just server_threads) and Find_THD_variable::operator(), that is, LOCK_thd_kill is locked before server_threads.lock is unlocked.
On the connection side, you may see that firs unlink_thd(thd) is called (that is, server_thread.erase(thd), so server_threads.lock was reacquired), and then LOCK_thd_kill is reacquired in ~THD.
This means, that we cannot acquire LOCK_thd_kill without server_threads.lock in general. But we can do so, when we gauarantee that connection is not executed and not in the io pool.
> More, this lock and unlock once per every single command seems too expensive
I also didn't like it having this way. That's why I've put significant effort, to make this lock happen not before the command, but after it. By the way, it is not a lock per command, but rather a lock per available request data, and only when no requests are left! So it should be quite nice on performance. The context switching was anyway costy: we aquired sonme locks, put mysys_vars with pthread_setspecific (at least twice). See thread_attach and Worker_thread_context::save for a clue. Whatnot. I guess we also check for killed quite regularly, which implies another LOCK_thd_kill acquisition.
You may also like more an obstruction-free approach, see next commit. There, only two atomic increments are the overhead. But one increment is before any command is processed, so it's 1ns worse. Also, it implies weaker guarantees for a requestor thread, but should be ok, given that it is very specific request.
https://dev.mysql.com/doc/refman/5.7/en/performance-schema-system-variable-tables.html