diff options
Diffstat (limited to 'sql/thread_cache.h')
-rw-r--r-- | sql/thread_cache.h | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/sql/thread_cache.h b/sql/thread_cache.h new file mode 100644 index 00000000..5cb6c0fe --- /dev/null +++ b/sql/thread_cache.h @@ -0,0 +1,210 @@ +/* + Copyright (C) 2020 MariaDB Foundation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA +*/ + + +/** + MariaDB thread cache for "one thread per connection" scheduler. + + Thread cache allows to re-use threads (as well as THD objects) for + subsequent connections. +*/ +class Thread_cache +{ + mutable mysql_cond_t COND_thread_cache; + mutable mysql_cond_t COND_flush_thread_cache; + mutable mysql_mutex_t LOCK_thread_cache; + /** Queue of new connection requests. */ + I_List<CONNECT> list; + /** Number of threads parked in the cache. */ + ulong cached_thread_count; + /** Number of active flush requests. */ + uint32_t kill_cached_threads; + /** + PFS stuff, only used during initialization. + Unfortunately needs to survive till destruction. + */ + PSI_cond_key key_COND_thread_cache, key_COND_flush_thread_cache; + PSI_mutex_key key_LOCK_thread_cache; + +public: + void init() + { +#ifdef HAVE_PSI_INTERFACE + PSI_cond_info conds[]= + { + { &key_COND_thread_cache, "COND_thread_cache", PSI_FLAG_GLOBAL }, + { &key_COND_flush_thread_cache, "COND_flush_thread_cache", + PSI_FLAG_GLOBAL } + }; + PSI_mutex_info mutexes[]= + { + { &key_LOCK_thread_cache, "LOCK_thread_cache", PSI_FLAG_GLOBAL } + }; + mysql_mutex_register("sql", mutexes, array_elements(mutexes)); + mysql_cond_register("sql", conds, array_elements(conds)); +#endif + mysql_mutex_init(key_LOCK_thread_cache, &LOCK_thread_cache, + MY_MUTEX_INIT_FAST); + mysql_cond_init(key_COND_thread_cache, &COND_thread_cache, 0); + mysql_cond_init(key_COND_flush_thread_cache, &COND_flush_thread_cache, 0); + list.empty(); + kill_cached_threads= 0; + cached_thread_count= 0; + } + + + void destroy() + { + DBUG_ASSERT(cached_thread_count == 0); + DBUG_ASSERT(list.is_empty()); + mysql_cond_destroy(&COND_flush_thread_cache); + mysql_cond_destroy(&COND_thread_cache); + mysql_mutex_destroy(&LOCK_thread_cache); + } + + + /** + Flushes thread cache. + + Awakes parked threads and requests them to shutdown. + Waits until last parked thread leaves the cache. + */ + void flush() + { + mysql_mutex_lock(&LOCK_thread_cache); + kill_cached_threads++; + while (cached_thread_count) + { + mysql_cond_broadcast(&COND_thread_cache); + mysql_cond_wait(&COND_flush_thread_cache, &LOCK_thread_cache); + } + kill_cached_threads--; + mysql_mutex_unlock(&LOCK_thread_cache); + } + + + /** + Flushes thread cache and forbids threads parking in the cache. + + This is a pre-shutdown hook. + */ + void final_flush() + { + kill_cached_threads++; + flush(); + } + + + /** + Requests parked thread to serve new connection. + + @return + @retval true connection is enqueued and parked thread is about to serve it + @retval false thread cache is empty + */ + bool enqueue(CONNECT *connect) + { + mysql_mutex_lock(&LOCK_thread_cache); + if (cached_thread_count) + { + list.push_back(connect); + cached_thread_count--; + mysql_mutex_unlock(&LOCK_thread_cache); + mysql_cond_signal(&COND_thread_cache); + return true; + } + mysql_mutex_unlock(&LOCK_thread_cache); + return false; + } + + + /** + Parks thread in the cache. + + Thread execution is suspended until either of the following occurs: + - thread is requested to serve new connection; + - thread cache is flushed; + - THREAD_CACHE_TIMEOUT elapsed. + + @return + @retval pointer to CONNECT if requested to serve new connection + @retval 0 if thread cache is flushed or on timeout + */ + CONNECT *park() + { + struct timespec abstime; + CONNECT *connect; + bool flushed= false; + DBUG_ENTER("Thread_cache::park"); + set_timespec(abstime, THREAD_CACHE_TIMEOUT); + + /* + Delete the instrumentation for the job that just completed, + before parking this pthread in the cache (blocked on COND_thread_cache). + */ + PSI_CALL_delete_current_thread(); + +#ifndef DBUG_OFF + while (_db_is_pushed_()) + _db_pop_(); +#endif + + mysql_mutex_lock(&LOCK_thread_cache); + if ((connect= list.get())) + cached_thread_count++; + else if (cached_thread_count < thread_cache_size && !kill_cached_threads) + { + /* Don't kill the thread, just put it in cache for reuse */ + DBUG_PRINT("info", ("Adding thread to cache")); + cached_thread_count++; + for (;;) + { + int error= mysql_cond_timedwait(&COND_thread_cache, &LOCK_thread_cache, + &abstime); + flushed= kill_cached_threads; + if ((connect= list.get())) + break; + else if (flushed || error == ETIMEDOUT || error == ETIME) + { + /* + If timeout, end thread. + If a new thread is requested, we will handle the call, even if we + got a timeout (as we are already awake and free) + */ + cached_thread_count--; + break; + } + } + } + mysql_mutex_unlock(&LOCK_thread_cache); + if (flushed) + mysql_cond_signal(&COND_flush_thread_cache); + DBUG_RETURN(connect); + } + + + /** Returns the number of parked threads. */ + ulong size() const + { + mysql_mutex_lock(&LOCK_thread_cache); + ulong r= cached_thread_count; + mysql_mutex_unlock(&LOCK_thread_cache); + return r; + } +}; + +extern Thread_cache thread_cache; |