/* Copyright (c) 2008, 2024, Oracle and/or its affiliates. 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 designed to work 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 either included with the program or referenced in the documentation. 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 */ #ifndef MYSQL_MUTEX_H #define MYSQL_MUTEX_H /** @file include/mysql/psi/mysql_mutex.h Instrumentation helpers for mutexes. This header file provides the necessary declarations to use the mutex API with the performance schema instrumentation. In some compilers (SunStudio), 'static inline' functions, when declared but not used, are not optimized away (because they are unused) by default, so that including a static inline function from a header file does create unwanted dependencies, causing unresolved symbols at link time. Other compilers, like gcc, optimize these dependencies by default. */ /* Note: there are several orthogonal dimensions here. Dimension 1: Instrumentation HAVE_PSI_MUTEX_INTERFACE is defined when the instrumentation is compiled in. This may happen both in debug or production builds. Dimension 2: Debug SAFE_MUTEX is defined when debug is compiled in. This may happen both with and without instrumentation. Dimension 3: Platform Mutexes are implemented with one of: - the pthread library - fast mutexes - window apis This is implemented by various macro definitions in my_thread.h This causes complexity with '#ifdef'-ery that can't be avoided. */ /* HAVE_PSI_*_INTERFACE */ #include "my_psi_config.h" // IWYU pragma: keep #include "mysql/components/services/bits/mysql_mutex_bits.h" #include "mysql/components/services/bits/psi_mutex_bits.h" #include "mysql/psi/psi_mutex.h" #include "thr_mutex.h" #if defined(MYSQL_SERVER) || defined(PFS_DIRECT_CALL) /* PSI_MUTEX_CALL() as direct call. */ #include "pfs_mutex_provider.h" // IWYU pragma: keep #endif #ifndef PSI_MUTEX_CALL #define PSI_MUTEX_CALL(M) psi_mutex_service->M #endif /** @defgroup psi_api_mutex Mutex Instrumentation (API) @ingroup psi_api @{ */ /* Consider the following code: static inline void foo() { bar(); } when foo() is never called. With gcc, foo() is a local static function, so the dependencies are optimized away at compile time, and there is no dependency on bar(). With other compilers (HP, Sun Studio), the function foo() implementation is compiled, and bar() needs to be present to link. Due to the existing header dependencies in MySQL code, this header file is sometime used when it is not needed, which in turn cause link failures on some platforms. The proper fix would be to cut these extra dependencies in the calling code. DISABLE_MYSQL_THREAD_H is a work around to limit dependencies. DISABLE_MYSQL_PRLOCK_H is similar, and is used to disable specifically the prlock wrappers. */ #ifndef DISABLE_MYSQL_THREAD_H /** @def mysql_mutex_assert_owner(M) Wrapper, to use safe_mutex_assert_owner with instrumented mutexes. @c mysql_mutex_assert_owner is a drop-in replacement for @c safe_mutex_assert_owner. */ #ifdef SAFE_MUTEX #define mysql_mutex_assert_owner(M) \ safe_mutex_assert_owner((M)->m_mutex.m_u.m_safe_ptr); #else #define mysql_mutex_assert_owner(M) \ {} #endif /** @def mysql_mutex_assert_not_owner(M) Wrapper, to use safe_mutex_assert_not_owner with instrumented mutexes. @c mysql_mutex_assert_not_owner is a drop-in replacement for @c safe_mutex_assert_not_owner. */ #ifdef SAFE_MUTEX #define mysql_mutex_assert_not_owner(M) \ safe_mutex_assert_not_owner((M)->m_mutex.m_u.m_safe_ptr); #else #define mysql_mutex_assert_not_owner(M) \ {} #endif /** @def mysql_mutex_register(P1, P2, P3) Mutex registration. */ #define mysql_mutex_register(P1, P2, P3) inline_mysql_mutex_register(P1, P2, P3) /** @def mysql_mutex_init(K, M, A) Instrumented mutex_init. @c mysql_mutex_init is a replacement for @c pthread_mutex_init. @param K The PSI_mutex_key for this instrumented mutex @param M The mutex to initialize @param A Mutex attributes */ #define mysql_mutex_init(K, M, A) \ mysql_mutex_init_with_src(K, M, A, __FILE__, __LINE__) #define mysql_mutex_init_with_src(K, M, A, F, L) \ inline_mysql_mutex_init(K, M, A, F, L) /** @def mysql_mutex_destroy(M) Instrumented mutex_destroy. @c mysql_mutex_destroy is a drop-in replacement for @c pthread_mutex_destroy. */ #define mysql_mutex_destroy(M) \ mysql_mutex_destroy_with_src(M, __FILE__, __LINE__) #define mysql_mutex_destroy_with_src(M, F, L) \ inline_mysql_mutex_destroy(M, F, L) /** @def mysql_mutex_lock(M) Instrumented mutex_lock. @c mysql_mutex_lock is a drop-in replacement for @c pthread_mutex_lock. @param M The mutex to lock */ #define mysql_mutex_lock(M) mysql_mutex_lock_with_src(M, __FILE__, __LINE__) #define mysql_mutex_lock_with_src(M, F, L) inline_mysql_mutex_lock(M, F, L) /** @def mysql_mutex_trylock(M) Instrumented mutex_lock. @c mysql_mutex_trylock is a drop-in replacement for @c my_mutex_trylock. */ #define mysql_mutex_trylock(M) \ mysql_mutex_trylock_with_src(M, __FILE__, __LINE__) #define mysql_mutex_trylock_with_src(M, F, L) \ inline_mysql_mutex_trylock(M, F, L) /** @def mysql_mutex_unlock(M) Instrumented mutex_unlock. @c mysql_mutex_unlock is a drop-in replacement for @c pthread_mutex_unlock. */ #define mysql_mutex_unlock(M) mysql_mutex_unlock_with_src(M, __FILE__, __LINE__) #define mysql_mutex_unlock_with_src(M, F, L) inline_mysql_mutex_unlock(M, F, L) static inline void inline_mysql_mutex_register(const char *category [[maybe_unused]], PSI_mutex_info *info [[maybe_unused]], int count [[maybe_unused]]) { #ifdef HAVE_PSI_MUTEX_INTERFACE PSI_MUTEX_CALL(register_mutex)(category, info, count); #endif } static inline int inline_mysql_mutex_init(PSI_mutex_key key [[maybe_unused]], mysql_mutex_t *that, const native_mutexattr_t *attr, const char *src_file [[maybe_unused]], uint src_line [[maybe_unused]]) { #ifdef HAVE_PSI_MUTEX_INTERFACE that->m_psi = PSI_MUTEX_CALL(init_mutex)(key, &that->m_mutex); #else that->m_psi = nullptr; #endif return my_mutex_init(&that->m_mutex, attr #ifdef SAFE_MUTEX , src_file, src_line #endif ); } static inline int inline_mysql_mutex_destroy(mysql_mutex_t *that, const char *src_file [[maybe_unused]], uint src_line [[maybe_unused]]) { #ifdef HAVE_PSI_MUTEX_INTERFACE if (that->m_psi != nullptr) { PSI_MUTEX_CALL(destroy_mutex)(that->m_psi); that->m_psi = nullptr; } #endif return my_mutex_destroy(&that->m_mutex #ifdef SAFE_MUTEX , src_file, src_line #endif ); } static inline int inline_mysql_mutex_lock(mysql_mutex_t *that, const char *src_file [[maybe_unused]], uint src_line [[maybe_unused]]) { int result; #ifdef HAVE_PSI_MUTEX_INTERFACE if (that->m_psi != nullptr) { if (that->m_psi->m_enabled) { /* Instrumentation start */ PSI_mutex_locker *locker; PSI_mutex_locker_state state; locker = PSI_MUTEX_CALL(start_mutex_wait)( &state, that->m_psi, PSI_MUTEX_LOCK, src_file, src_line); /* Instrumented code */ result = my_mutex_lock(&that->m_mutex #ifdef SAFE_MUTEX , src_file, src_line #endif ); /* Instrumentation end */ if (locker != nullptr) { PSI_MUTEX_CALL(end_mutex_wait)(locker, result); } return result; } } #endif /* Non instrumented code */ result = my_mutex_lock(&that->m_mutex #ifdef SAFE_MUTEX , src_file, src_line #endif ); return result; } static inline int inline_mysql_mutex_trylock(mysql_mutex_t *that, const char *src_file [[maybe_unused]], uint src_line [[maybe_unused]]) { int result; #ifdef HAVE_PSI_MUTEX_INTERFACE if (that->m_psi != nullptr) { if (that->m_psi->m_enabled) { /* Instrumentation start */ PSI_mutex_locker *locker; PSI_mutex_locker_state state; locker = PSI_MUTEX_CALL(start_mutex_wait)( &state, that->m_psi, PSI_MUTEX_TRYLOCK, src_file, src_line); /* Instrumented code */ result = my_mutex_trylock(&that->m_mutex #ifdef SAFE_MUTEX , src_file, src_line #endif ); /* Instrumentation end */ if (locker != nullptr) { PSI_MUTEX_CALL(end_mutex_wait)(locker, result); } return result; } } #endif /* Non instrumented code */ result = my_mutex_trylock(&that->m_mutex #ifdef SAFE_MUTEX , src_file, src_line #endif ); return result; } static inline int inline_mysql_mutex_unlock(mysql_mutex_t *that, const char *src_file [[maybe_unused]], uint src_line [[maybe_unused]]) { int result; #ifdef HAVE_PSI_MUTEX_INTERFACE if (that->m_psi != nullptr) { PSI_MUTEX_CALL(unlock_mutex)(that->m_psi); } #endif result = my_mutex_unlock(&that->m_mutex #ifdef SAFE_MUTEX , src_file, src_line #endif ); return result; } #endif /* DISABLE_MYSQL_THREAD_H */ /** @} (end of group psi_api_mutex) */ #endif