From 187d4434475a2a4e469adb1f9ce76d3a9cf4f0c4 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 17 Dec 2013 12:23:19 +0100 Subject: [PATCH] Implement support for thin mutexes refs #7622 --- icinga2.spec | 3 +- lib/base/CMakeLists.txt | 2 +- lib/base/object.hpp | 9 +-- lib/base/objectlock.cpp | 19 +++-- lib/base/objectlock.hpp | 2 +- lib/base/thinmutex.cpp | 94 +++++++++++++++++++++++ lib/base/thinmutex.hpp | 162 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 274 insertions(+), 17 deletions(-) create mode 100644 lib/base/thinmutex.cpp create mode 100644 lib/base/thinmutex.hpp diff --git a/icinga2.spec b/icinga2.spec index 3f80c2da5..0e076e510 100644 --- a/icinga2.spec +++ b/icinga2.spec @@ -205,7 +205,8 @@ CMAKE_OPTS="$CMAKE_OPTS -DBOOST_LIBRARYDIR=/usr/lib/boost141 \ %if "%{_vendor}" != "suse" CMAKE_OPTS="$CMAKE_OPTS -DICINGA2_PLUGINDIR=%{_libdir}/nagios/plugins" %else -CMAKE_OPTS="$CMAKE_OPTS -DICINGA2_PLUGINDIR=%{_prefix}/lib/nagios/plugins" +CMAKE_OPTS="$CMAKE_OPTS -DICINGA2_PLUGINDIR=%{_prefix}/lib/nagios/plugins \ + -DCMAKE_C_FLAGS='-march=i686' -DCMAKE_CXX_FLAGS='-march=i686'" %endif %if 0%{?use_systemd} diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 8467c9468..1ae0f4554 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -30,7 +30,7 @@ set(base_SOURCES ringbuffer.cpp scriptfunction.cpp scriptfunctionwrapper.cpp scriptutils.cpp scriptvariable.cpp serializer.cpp socket.cpp stacktrace.cpp statsfunction.cpp stdiostream.cpp stream.cpp streamlogger.cpp streamlogger.thpp string.cpp - sysloglogger.cpp sysloglogger.thpp tcpsocket.cpp threadpool.cpp timer.cpp + sysloglogger.cpp sysloglogger.thpp tcpsocket.cpp thinmutex.cpp threadpool.cpp timer.cpp tlsstream.cpp tlsutility.cpp type.cpp unixsocket.cpp utility.cpp value.cpp value-operators.cpp workqueue.cpp ) diff --git a/lib/base/object.hpp b/lib/base/object.hpp index 673f58dc7..6ce60fd5f 100644 --- a/lib/base/object.hpp +++ b/lib/base/object.hpp @@ -22,6 +22,7 @@ #include "base/i2-base.hpp" #include "base/debug.hpp" +#include "base/thinmutex.hpp" #include #ifndef _DEBUG @@ -179,18 +180,14 @@ private: Object(const Object& other); Object& operator=(const Object& rhs); -#ifndef _DEBUG - typedef boost::mutex MutexType; -#else /* _DEBUG */ - typedef boost::recursive_mutex MutexType; + mutable ThinMutex m_Mutex; +#ifdef _DEBUG static boost::mutex m_DebugMutex; mutable bool m_Locked; mutable boost::thread::id m_LockOwner; #endif /* _DEBUG */ - mutable MutexType m_Mutex; - friend struct ObjectLock; }; diff --git a/lib/base/objectlock.cpp b/lib/base/objectlock.cpp index d236bf75c..17a7a4995 100644 --- a/lib/base/objectlock.cpp +++ b/lib/base/objectlock.cpp @@ -23,7 +23,7 @@ using namespace icinga; ObjectLock::ObjectLock(void) - : m_Object(NULL), m_Lock() + : m_Object(NULL), m_Locked(false) { } ObjectLock::~ObjectLock(void) @@ -32,14 +32,14 @@ ObjectLock::~ObjectLock(void) } ObjectLock::ObjectLock(const Object::Ptr& object) - : m_Object(object.get()), m_Lock() + : m_Object(object.get()), m_Locked(false) { if (m_Object) Lock(); } ObjectLock::ObjectLock(const Object *object) - : m_Object(object), m_Lock() + : m_Object(object), m_Locked(false) { if (m_Object) Lock(); @@ -47,10 +47,11 @@ ObjectLock::ObjectLock(const Object *object) void ObjectLock::Lock(void) { - ASSERT(!m_Lock.owns_lock() && m_Object != NULL); + ASSERT(!m_Locked && m_Object != NULL); ASSERT(!m_Object->OwnsLock()); - m_Lock = Object::MutexType::scoped_lock(m_Object->m_Mutex); + m_Object->m_Mutex.Lock(); + m_Locked = true; #ifdef _DEBUG { @@ -66,11 +67,13 @@ void ObjectLock::Unlock(void) #ifdef _DEBUG { boost::mutex::scoped_lock lock(Object::m_DebugMutex); - - if (m_Lock.owns_lock()) + if (m_Locked) m_Object->m_Locked = false; } #endif /* _DEBUG */ - m_Lock = Object::MutexType::scoped_lock(); + if (m_Locked) { + m_Object->m_Mutex.Unlock(); + m_Locked = false; + } } diff --git a/lib/base/objectlock.hpp b/lib/base/objectlock.hpp index 4215f089a..bcb293830 100644 --- a/lib/base/objectlock.hpp +++ b/lib/base/objectlock.hpp @@ -40,7 +40,7 @@ public: private: const Object *m_Object; - Object::MutexType::scoped_lock m_Lock; + bool m_Locked; }; } diff --git a/lib/base/thinmutex.cpp b/lib/base/thinmutex.cpp new file mode 100644 index 000000000..e5307b75c --- /dev/null +++ b/lib/base/thinmutex.cpp @@ -0,0 +1,94 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2013 Icinga Development Team (http://www.icinga.org/) * + * * + * 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; either version 2 * + * of the License, or (at your option) any later version. * + * * + * 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-1301, USA. * + ******************************************************************************/ + +#include "base/thinmutex.hpp" +#include "base/initialize.hpp" +#include "base/timer.hpp" +#include "base/convert.hpp" +#include "base/logger.hpp" +#include + +using namespace icinga; + +#ifdef _DEBUG +uintptr_t ThinMutex::m_TotalMutexes; +uintptr_t ThinMutex::m_InflatedMutexes; +uintptr_t ThinMutex::m_DeadMutexes; + +static Timer::Ptr l_Timer; + +void ThinMutex::DebugTimerHandler(void) +{ + Log(LogNotice, "ThinMutex") + << "Mutexes: " << ThinMutex::m_TotalMutexes - ThinMutex::m_DeadMutexes + << ", Inflated Mutexes: " << ThinMutex::m_InflatedMutexes + << ", Dead Mutexes: " << ThinMutex::m_DeadMutexes; +} + +static void InitThinMutex(void) +{ + l_Timer = make_shared(); + l_Timer->SetInterval(10); + l_Timer->OnTimerExpired.connect(boost::bind(&ThinMutex::DebugTimerHandler)); + l_Timer->Start(); +} + +INITIALIZE_ONCE(&InitThinMutex); +#endif /* _DEBUG */ + +void ThinMutex::MakeNative(void) +{ + boost::mutex *mtx = new boost::mutex(); + mtx->lock(); +#ifdef _WIN32 +# ifdef _WIN64 + InterlockedCompareExchange64(&m_Data, reinterpret_cast(mtx), THINLOCK_LOCKED); +# else /* _WIN64 */ + InterlockedCompareExchange(&m_Data, reinterpret_cast(mtx), THINLOCK_LOCKED); +# endif /* _WIN64 */ +#else /* _WIN32 */ + __sync_bool_compare_and_swap(&m_Data, THINLOCK_LOCKED, reinterpret_cast(mtx)); +#endif /* _WIN32 */ + +#ifdef _DEBUG +# ifdef _WIN32 + InterlockedIncrement(&m_InflatedMutexes); +# else /* _WIN32 */ + __sync_fetch_and_add(&m_InflatedMutexes, 1); +# endif /* _WIN32 */ +#endif /* _DEBUG */ +} + +void ThinMutex::DestroyNative(void) +{ + delete reinterpret_cast(m_Data); +} + +void ThinMutex::LockNative(void) +{ + boost::mutex *mtx = reinterpret_cast(m_Data); + mtx->lock(); +} + +void ThinMutex::UnlockNative(void) +{ + boost::mutex *mtx = reinterpret_cast(m_Data); + mtx->unlock(); +} + diff --git a/lib/base/thinmutex.hpp b/lib/base/thinmutex.hpp new file mode 100644 index 000000000..ab85ef37f --- /dev/null +++ b/lib/base/thinmutex.hpp @@ -0,0 +1,162 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012-2013 Icinga Development Team (http://www.icinga.org/) * + * * + * 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; either version 2 * + * of the License, or (at your option) any later version. * + * * + * 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-1301, USA. * + ******************************************************************************/ + +#ifndef THINMUTEX_H +#define THINMUTEX_H + +#include "base/i2-base.hpp" +#ifndef _WIN32 +#include +#endif /* _WIN32 */ + +#if defined(_MSC_VER) && _MSC_VER >= 1310 && (defined(_M_IX86) || defined(_M_X64)) +extern "C" void _mm_pause(); +#pragma intrinsic(_mm_pause) +#define SPIN_PAUSE() _mm_pause() +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#define SPIN_PAUSE() __asm__ __volatile__("rep; nop" : : : "memory") +#endif + +#define THINLOCK_UNLOCKED 0 +#define THINLOCK_LOCKED 1 + +namespace icinga { + +class I2_BASE_API ThinMutex +{ +public: + inline ThinMutex(void) + : m_Data(THINLOCK_UNLOCKED) + { +#ifdef _DEBUG +# ifdef _WIN32 + InterlockedIncrement(&m_TotalMutexes); +# else /* _WIN32 */ + __sync_fetch_and_add(&m_TotalMutexes, 1); +# endif /* _WIN32 */ +#endif /* _DEBUG */ + } + + inline ~ThinMutex(void) + { + if (m_Data > THINLOCK_LOCKED) + DestroyNative(); + +#ifdef _DEBUG +# ifdef _WIN32 + InterlockedDecrement(&m_TotalMutexes); +# else /* _WIN32 */ + __sync_fetch_and_add(&m_DeadMutexes, 1); +# endif /* _WIN32 */ +#endif /* _DEBUG */ + } + + inline void Spin(unsigned int it) + { + if (it < 8) { + /* Do nothing. */ + } +#ifdef SPIN_PAUSE + else if (it < 16) { + SPIN_PAUSE(); + } +#endif /* SPIN_PAUSE */ + else { +#ifdef _WIN32 + Sleep(0); +#else /* _WIN32 */ + sched_yield(); +#endif /* _WIN32 */ + } + } + + inline void Lock(void) + { + bool contended = false; + unsigned int it = 0; + +#ifdef _WIN32 +# ifdef _WIN64 + while (InterlockedCompareExchange64(&m_Data, THINLOCK_LOCKED, THINLOCK_UNLOCKED) != THINLOCK_UNLOCKED) { +# else /* _WIN64 */ + while (InterlockedCompareExchange(&m_Data, THINLOCK_LOCKED, THINLOCK_UNLOCKED) != THINLOCK_UNLOCKED) { +# endif /* _WIN64 */ +#else /* _WIN32 */ + while (!__sync_bool_compare_and_swap(&m_Data, THINLOCK_UNLOCKED, THINLOCK_LOCKED)) { +#endif /* _WIN32 */ + if (m_Data > THINLOCK_LOCKED) { + LockNative(); + return; + } + + contended = true; + + Spin(it); + it++; + } + + if (contended) + MakeNative(); + } + + void MakeNative(void); + void DestroyNative(void); + + void LockNative(void); + void UnlockNative(void); + + inline void Unlock(void) + { +#ifdef _WIN32 +# ifdef _WIN64 + if (InterlockedCompareExchange64(&m_Data, THINLOCK_UNLOCKED, THINLOCK_LOCKED) != THINLOCK_LOCKED) +# else /* _WIN64 */ + if (InterlockedCompareExchange(&m_Data, THINLOCK_UNLOCKED, THINLOCK_LOCKED) != THINLOCK_LOCKED) +# endif /* _WIN64 */ +#else /* _WIN32 */ + if (!__sync_bool_compare_and_swap(&m_Data, THINLOCK_LOCKED, THINLOCK_UNLOCKED)) +#endif /* _WIN32 */ + UnlockNative(); + } + +#ifdef _DEBUG + static void DebugTimerHandler(void); +#endif /* _DEBUG */ + +private: +#ifdef _WIN32 +# ifdef _WIN64 + LONGLONG m_Data; +# else /* _WIN64 */ + LONG m_Data; +# endif /* _WIN64 */ +#else /* _WIN32 */ + uintptr_t m_Data; +#endif /* _WIN32 */ + +#ifdef _DEBUG + static uintptr_t m_TotalMutexes; + static uintptr_t m_InflatedMutexes; + static uintptr_t m_DeadMutexes; +#endif /* _DEBUG */ +}; + +} + +#endif /* THINMUTEX_H */