Implement support for thin mutexes

refs #7622
This commit is contained in:
Gunnar Beutner 2013-12-17 12:23:19 +01:00
parent 684ad2641a
commit 187d443447
7 changed files with 274 additions and 17 deletions

View File

@ -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}

View File

@ -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
)

View File

@ -22,6 +22,7 @@
#include "base/i2-base.hpp"
#include "base/debug.hpp"
#include "base/thinmutex.hpp"
#include <boost/thread/thread.hpp>
#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;
};

View File

@ -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;
}
}

View File

@ -40,7 +40,7 @@ public:
private:
const Object *m_Object;
Object::MutexType::scoped_lock m_Lock;
bool m_Locked;
};
}

94
lib/base/thinmutex.cpp Normal file
View File

@ -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 <boost/thread.hpp>
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<Timer>();
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<LONGLONG>(mtx), THINLOCK_LOCKED);
# else /* _WIN64 */
InterlockedCompareExchange(&m_Data, reinterpret_cast<LONG>(mtx), THINLOCK_LOCKED);
# endif /* _WIN64 */
#else /* _WIN32 */
__sync_bool_compare_and_swap(&m_Data, THINLOCK_LOCKED, reinterpret_cast<uintptr_t>(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<boost::mutex *>(m_Data);
}
void ThinMutex::LockNative(void)
{
boost::mutex *mtx = reinterpret_cast<boost::mutex *>(m_Data);
mtx->lock();
}
void ThinMutex::UnlockNative(void)
{
boost::mutex *mtx = reinterpret_cast<boost::mutex *>(m_Data);
mtx->unlock();
}

162
lib/base/thinmutex.hpp Normal file
View File

@ -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 <sched.h>
#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 */