123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License, version 2.0,
- as published by the Free Software Foundation.
- This program is also distributed with certain software (including
- but not limited to OpenSSL) that is licensed under separate terms,
- as designated in a particular file or component or in included license
- documentation. The authors of MySQL hereby grant you an additional
- permission to link the program and your derivative works with the
- separately licensed software that they have included with MySQL.
- Without limiting anything contained in the foregoing, this file,
- which is part of C Driver for MySQL (Connector/C), is also subject to the
- Universal FOSS Exception, version 1.0, a copy of which can be found at
- http://oss.oracle.com/licenses/universal-foss-exception.
- 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, version 2.0, 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-1301 USA */
- /**
- @file mysys/my_thr_init.cc
- Functions to handle initialization and allocation of all mysys & debug
- thread variables.
- */
- #include <stdlib.h>
- #include <sys/types.h>
- #ifdef _WIN32
- #include <signal.h>
- #endif
- #include <time.h>
- #include "my_dbug.h"
- #include "my_inttypes.h"
- #include "my_loglevel.h"
- #include "my_macros.h"
- #include "my_psi_config.h"
- #include "my_sys.h"
- #include "my_systime.h"
- #include "my_thread.h"
- #include "my_thread_local.h"
- #include "mysql/psi/mysql_cond.h"
- #include "mysql/psi/mysql_mutex.h"
- #include "mysql/psi/mysql_thread.h"
- #include "mysql/psi/psi_thread.h"
- #include "mysys/mysys_priv.h"
- #include "mysys_err.h"
- #include "thr_mutex.h"
- static bool my_thread_global_init_done = false;
- #ifndef DBUG_OFF
- static uint THR_thread_count = 0;
- static Timeout_type my_thread_end_wait_time = 5;
- static my_thread_id thread_id = 0;
- struct st_my_thread_var;
- static thread_local st_my_thread_var *THR_mysys = nullptr;
- #endif
- static thread_local int THR_myerrno = 0;
- #ifdef _WIN32
- static thread_local int THR_winerrno = 0;
- #endif
- mysql_mutex_t THR_LOCK_myisam_mmap;
- mysql_mutex_t THR_LOCK_myisam;
- mysql_mutex_t THR_LOCK_heap;
- mysql_mutex_t THR_LOCK_malloc;
- mysql_mutex_t THR_LOCK_open;
- mysql_mutex_t THR_LOCK_lock;
- mysql_mutex_t THR_LOCK_net;
- mysql_mutex_t THR_LOCK_charset;
- #ifndef DBUG_OFF
- mysql_mutex_t THR_LOCK_threads;
- mysql_cond_t THR_COND_threads;
- #endif
- #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
- native_mutexattr_t my_fast_mutexattr;
- #endif
- #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
- native_mutexattr_t my_errorcheck_mutexattr;
- #endif
- #ifdef _WIN32
- static void install_sigabrt_handler();
- #endif
- #ifndef DBUG_OFF
- struct st_my_thread_var {
- my_thread_id id;
- struct CODE_STATE *dbug;
- };
- static struct st_my_thread_var *mysys_thread_var() { return THR_mysys; }
- static int set_mysys_thread_var(struct st_my_thread_var *mysys_var) {
- THR_mysys = mysys_var;
- return 0;
- }
- #endif
- /**
- Re-initialize components initialized early with @c my_thread_global_init.
- Some mutexes were initialized before the instrumentation.
- Destroy + create them again, now that the instrumentation
- is in place.
- This is safe, since this function() is called before creating new threads,
- so the mutexes are not in use.
- */
- void my_thread_global_reinit() {
- DBUG_ASSERT(my_thread_global_init_done);
- #ifdef HAVE_PSI_INTERFACE
- my_init_mysys_psi_keys();
- #endif
- mysql_mutex_destroy(&THR_LOCK_heap);
- mysql_mutex_init(key_THR_LOCK_heap, &THR_LOCK_heap, MY_MUTEX_INIT_FAST);
- mysql_mutex_destroy(&THR_LOCK_net);
- mysql_mutex_init(key_THR_LOCK_net, &THR_LOCK_net, MY_MUTEX_INIT_FAST);
- mysql_mutex_destroy(&THR_LOCK_myisam);
- mysql_mutex_init(key_THR_LOCK_myisam, &THR_LOCK_myisam, MY_MUTEX_INIT_SLOW);
- mysql_mutex_destroy(&THR_LOCK_malloc);
- mysql_mutex_init(key_THR_LOCK_malloc, &THR_LOCK_malloc, MY_MUTEX_INIT_FAST);
- mysql_mutex_destroy(&THR_LOCK_open);
- mysql_mutex_init(key_THR_LOCK_open, &THR_LOCK_open, MY_MUTEX_INIT_FAST);
- mysql_mutex_destroy(&THR_LOCK_charset);
- mysql_mutex_init(key_THR_LOCK_charset, &THR_LOCK_charset, MY_MUTEX_INIT_FAST);
- #ifndef DBUG_OFF
- mysql_mutex_destroy(&THR_LOCK_threads);
- mysql_mutex_init(key_THR_LOCK_threads, &THR_LOCK_threads, MY_MUTEX_INIT_FAST);
- mysql_cond_destroy(&THR_COND_threads);
- mysql_cond_init(key_THR_COND_threads, &THR_COND_threads);
- #endif
- }
- /**
- initialize thread environment
- @retval false ok
- @retval true error
- */
- bool my_thread_global_init() {
- if (my_thread_global_init_done) return false;
- my_thread_global_init_done = true;
- #if defined(SAFE_MUTEX)
- safe_mutex_global_init(); /* Must be called early */
- #endif
- #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
- /*
- Set mutex type to "fast" a.k.a "adaptive"
- In this case the thread may steal the mutex from some other thread
- that is waiting for the same mutex. This will save us some
- context switches but may cause a thread to 'starve forever' while
- waiting for the mutex (not likely if the code within the mutex is
- short).
- */
- pthread_mutexattr_init(&my_fast_mutexattr);
- pthread_mutexattr_settype(&my_fast_mutexattr, PTHREAD_MUTEX_ADAPTIVE_NP);
- #endif
- #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
- /*
- Set mutex type to "errorcheck"
- */
- pthread_mutexattr_init(&my_errorcheck_mutexattr);
- pthread_mutexattr_settype(&my_errorcheck_mutexattr, PTHREAD_MUTEX_ERRORCHECK);
- #endif
- mysql_mutex_init(key_THR_LOCK_malloc, &THR_LOCK_malloc, MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_THR_LOCK_open, &THR_LOCK_open, MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_THR_LOCK_charset, &THR_LOCK_charset, MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_THR_LOCK_lock, &THR_LOCK_lock, MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_THR_LOCK_myisam, &THR_LOCK_myisam, MY_MUTEX_INIT_SLOW);
- mysql_mutex_init(key_THR_LOCK_myisam_mmap, &THR_LOCK_myisam_mmap,
- MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_THR_LOCK_heap, &THR_LOCK_heap, MY_MUTEX_INIT_FAST);
- mysql_mutex_init(key_THR_LOCK_net, &THR_LOCK_net, MY_MUTEX_INIT_FAST);
- #ifndef DBUG_OFF
- mysql_mutex_init(key_THR_LOCK_threads, &THR_LOCK_threads, MY_MUTEX_INIT_FAST);
- mysql_cond_init(key_THR_COND_threads, &THR_COND_threads);
- #endif
- return false;
- }
- void my_thread_global_end() {
- #ifndef DBUG_OFF
- struct timespec abstime;
- bool all_threads_killed = true;
- set_timespec(&abstime, my_thread_end_wait_time);
- mysql_mutex_lock(&THR_LOCK_threads);
- while (THR_thread_count > 0) {
- int error =
- mysql_cond_timedwait(&THR_COND_threads, &THR_LOCK_threads, &abstime);
- if (is_timeout(error)) {
- #ifndef _WIN32
- /*
- We shouldn't give an error here, because if we don't have
- pthread_kill(), programs like mysqld can't ensure that all threads
- are killed when we enter here.
- */
- if (THR_thread_count) /* purecov: begin inspected */
- my_message_local(ERROR_LEVEL, EE_FAILED_TO_KILL_ALL_THREADS,
- THR_thread_count);
- /* purecov: end */
- #endif
- all_threads_killed = false;
- break;
- }
- }
- mysql_mutex_unlock(&THR_LOCK_threads);
- #endif
- #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
- pthread_mutexattr_destroy(&my_fast_mutexattr);
- #endif
- #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
- pthread_mutexattr_destroy(&my_errorcheck_mutexattr);
- #endif
- mysql_mutex_destroy(&THR_LOCK_malloc);
- mysql_mutex_destroy(&THR_LOCK_open);
- mysql_mutex_destroy(&THR_LOCK_lock);
- mysql_mutex_destroy(&THR_LOCK_myisam);
- mysql_mutex_destroy(&THR_LOCK_myisam_mmap);
- mysql_mutex_destroy(&THR_LOCK_heap);
- mysql_mutex_destroy(&THR_LOCK_net);
- mysql_mutex_destroy(&THR_LOCK_charset);
- #ifndef DBUG_OFF
- if (all_threads_killed) {
- mysql_mutex_destroy(&THR_LOCK_threads);
- mysql_cond_destroy(&THR_COND_threads);
- }
- #endif
- my_thread_global_init_done = false;
- }
- /**
- Allocate thread specific memory for the thread, used by mysys and dbug
- @note This function may called multiple times for a thread, for example
- if one uses my_init() followed by mysql_server_init().
- @retval false ok
- @retval true Fatal error; mysys/dbug functions can't be used
- */
- extern "C" bool my_thread_init() {
- #ifndef DBUG_OFF
- struct st_my_thread_var *tmp;
- #endif
- if (!my_thread_global_init_done)
- return true; /* cannot proceed with unintialized library */
- #ifdef _WIN32
- install_sigabrt_handler();
- #endif
- #ifndef DBUG_OFF
- if (mysys_thread_var()) return false;
- if (!(tmp = (struct st_my_thread_var *)calloc(1, sizeof(*tmp)))) return true;
- mysql_mutex_lock(&THR_LOCK_threads);
- tmp->id = ++thread_id;
- ++THR_thread_count;
- mysql_mutex_unlock(&THR_LOCK_threads);
- set_mysys_thread_var(tmp);
- #endif
- return false;
- }
- /**
- Deallocate memory used by the thread for book-keeping
- @note This may be called multiple times for a thread.
- This happens for example when one calls 'mysql_server_init()'
- mysql_server_end() and then ends with a mysql_end().
- */
- extern "C" void my_thread_end() {
- #ifndef DBUG_OFF
- struct st_my_thread_var *tmp = mysys_thread_var();
- #endif
- #ifdef HAVE_PSI_THREAD_INTERFACE
- /*
- Remove the instrumentation for this thread.
- This must be done before trashing st_my_thread_var,
- because the LF_HASH depends on it.
- */
- PSI_THREAD_CALL(delete_current_thread)();
- #endif
- #if !defined(DBUG_OFF)
- if (tmp) {
- /* tmp->dbug is allocated inside DBUG library */
- if (tmp->dbug) {
- DBUG_POP();
- free(tmp->dbug);
- tmp->dbug = NULL;
- }
- free(tmp);
- /*
- Decrement counter for number of running threads. We are using this
- in my_thread_global_end() to wait until all threads have called
- my_thread_end and thus freed all memory they have allocated in
- my_thread_init() and DBUG_xxxx
- */
- mysql_mutex_lock(&THR_LOCK_threads);
- DBUG_ASSERT(THR_thread_count != 0);
- if (--THR_thread_count == 0) mysql_cond_signal(&THR_COND_threads);
- mysql_mutex_unlock(&THR_LOCK_threads);
- }
- set_mysys_thread_var(NULL);
- #endif
- }
- int my_errno() { return THR_myerrno; }
- void set_my_errno(int my_errno) { THR_myerrno = my_errno; }
- #ifdef _WIN32
- int thr_winerr() { return THR_winerrno; }
- void set_thr_winerr(int winerr) { THR_winerrno = winerr; }
- #endif
- #ifndef DBUG_OFF
- my_thread_id my_thread_var_id() { return mysys_thread_var()->id; }
- void set_my_thread_var_id(my_thread_id id) { mysys_thread_var()->id = id; }
- CODE_STATE **my_thread_var_dbug() {
- struct st_my_thread_var *tmp = THR_mysys;
- return tmp ? &tmp->dbug : NULL;
- }
- #endif /* DBUG_OFF */
- #ifdef _WIN32
- /*
- In Visual Studio 2005 and later, default SIGABRT handler will overwrite
- any unhandled exception filter set by the application and will try to
- call JIT debugger. This is not what we want, this we calling __debugbreak
- to stop in debugger, if process is being debugged or to generate
- EXCEPTION_BREAKPOINT and then handle_segfault will do its magic.
- */
- static void my_sigabrt_handler(int sig) { __debugbreak(); }
- static void install_sigabrt_handler() {
- /*abort() should not override our exception filter*/
- _set_abort_behavior(0, _CALL_REPORTFAULT);
- signal(SIGABRT, my_sigabrt_handler);
- }
- #endif
|