/***************************************************************************** Copyright (c) 2013, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2017, 2018, MariaDB Corporation. 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 Street, Fifth Floor, Boston, MA 02110-1335 USA *****************************************************************************/ /******************************************************************//** @file include/sync0policy.h Policies for mutexes. Created 2012-08-21 Sunny Bains. ***********************************************************************/ #ifndef sync0policy_h #define sync0policy_h #include "ut0rnd.h" #include "os0thread.h" #include "srv0mon.h" #include "sync0debug.h" #ifdef UNIV_DEBUG template class MutexDebug: public latch_t { /** Mutex to check for lock order violation */ const Mutex *m_mutex; /** Filename from where enter was called */ const char *m_filename; /** Line mumber in filename */ unsigned m_line; /** Thread ID of the thread that owns the mutex */ os_thread_id_t m_thread_id; /** Mutex protecting the above members */ mutable OSMutex m_debug_mutex; void set(const Mutex *mutex, const char *filename, unsigned line, os_thread_id_t thread_id) { m_debug_mutex.enter(); m_mutex= mutex; m_filename= filename; m_line= line; m_thread_id= thread_id; m_debug_mutex.exit(); } const MutexDebug get() const { MutexDebug ret; m_debug_mutex.enter(); ret.m_mutex= m_mutex; ret.m_filename= m_filename; ret.m_line= m_line; ret.m_thread_id= m_thread_id; m_debug_mutex.exit(); return ret; } /** Called either when mutex is locked or destroyed. Thus members are protected from concurrent modification. */ void assert_clean_context() { ut_ad(!m_mutex); ut_ad(!m_filename); ut_ad(!m_line); ut_ad(m_thread_id == os_thread_id_t(ULINT_UNDEFINED)); } public: /** Called when the mutex is "created". Note: Not from the constructor but when the mutex is initialised. @param[in] id Mutex ID */ void init(latch_id_t id) { ut_ad(id != LATCH_ID_NONE); m_id= id; m_debug_mutex.init(); set(0, 0, 0, os_thread_id_t(ULINT_UNDEFINED)); } /** Mutex is being destroyed. */ void destroy() { assert_clean_context(); m_debug_mutex.destroy(); } /** Called when an attempt is made to lock the mutex @param[in] mutex Mutex instance to be locked @param[in] filename Filename from where it was called @param[in] line Line number from where it was called */ void enter(const Mutex &mutex, const char *filename, unsigned line) { MutexDebug context; ut_ad(!is_owned()); context.init(m_id); context.set(&mutex, filename, line, os_thread_get_curr_id()); /* Check for latch order violation. */ sync_check_lock_validate(&context); context.set(0, 0, 0, os_thread_id_t(ULINT_UNDEFINED)); context.destroy(); } /** Called when the mutex is locked @param[in] mutex Mutex instance that was locked @param[in] filename Filename from where it was called @param[in] line Line number from where it was called */ void locked(const Mutex &mutex, const char *filename, unsigned line) { assert_clean_context(); set(&mutex, filename, line, os_thread_get_curr_id()); sync_check_lock_granted(this); } /** Called when the mutex is released @param[in] mutex Mutex that was released */ void release(const Mutex &mutex) { ut_ad(is_owned()); set(0, 0, 0, os_thread_id_t(ULINT_UNDEFINED)); sync_check_unlock(this); } /** @return true if thread owns the mutex */ bool is_owned() const { return os_thread_eq(get_thread_id(), os_thread_get_curr_id()); } /** @return the name of the file from the mutex was acquired */ const char* get_enter_filename() const { return get().m_filename; } /** @return the name of the file from the mutex was acquired */ unsigned get_enter_line() const { return get().m_line; } /** @return id of the thread that was trying to acquire the mutex */ os_thread_id_t get_thread_id() const { return get().m_thread_id; } /** Print information about the latch @return the string representation */ virtual std::string to_string() const { std::ostringstream msg; const MutexDebug ctx= get(); msg << m_mutex->policy().to_string(); if (ctx.m_mutex) msg << " addr: " << ctx.m_mutex << " acquired: " << sync_basename(ctx.get_enter_filename()) << ":" << ctx.get_enter_line(); else msg << "Not locked"; return(msg.str()); } }; #endif /* UNIV_DEBUG */ /** Collect the metrics per mutex instance, no aggregation. */ template struct GenericPolicy { public: /** Called when the mutex is "created". Note: Not from the constructor but when the mutex is initialised. @param[in] id Mutex ID @param[in] filename File where mutex was created @param[in] line Line in filename */ void init( const Mutex&, latch_id_t id, const char* filename, uint32_t line) UNIV_NOTHROW { m_id = id; latch_meta_t& meta = sync_latch_get_meta(id); ut_ad(meta.get_id() == id); meta.get_counter()->single_register(&m_count); m_filename = filename; m_line = line; } /** Called when the mutex is destroyed. */ void destroy() UNIV_NOTHROW { latch_meta_t& meta = sync_latch_get_meta(m_id); meta.get_counter()->single_deregister(&m_count); } /** Called after a successful mutex acquire. @param[in] n_spins Number of times the thread did spins while trying to acquire the mutex @param[in] n_waits Number of times the thread waited in some type of OS queue */ void add( uint32_t n_spins, uint32_t n_waits) UNIV_NOTHROW { /* Currently global on/off. Keeps things simple and fast */ if (!m_count.m_enabled) { return; } m_count.m_spins += n_spins; m_count.m_waits += n_waits; ++m_count.m_calls; } /** Print the information about the latch @return the string representation */ std::string print() const UNIV_NOTHROW; /** @return the latch ID */ latch_id_t get_id() const UNIV_NOTHROW { return(m_id); } /** @return the string representation */ std::string to_string() const { return sync_mutex_to_string(get_id(), std::string(m_filename) .append(":") .append(std::to_string(m_line))); } #ifdef UNIV_DEBUG MutexDebug context; #endif private: const char *m_filename; uint32_t m_line; /** The user visible counters, registered with the meta-data. */ latch_meta_t::CounterType::Count m_count; /** Latch meta data ID */ latch_id_t m_id; }; #endif /* sync0policy_h */