icinga2/lib/base/thinmutex.hpp

163 lines
4.3 KiB
C++

/******************************************************************************
* 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 */