Files
tlib/oversampling/WDL/mutex.h
2024-05-24 13:28:31 +02:00

238 lines
5.6 KiB
C++

/*
WDL - mutex.h
Copyright (C) 2005 and later, Cockos Incorporated
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
/*
This file provides a simple class that abstracts a mutex or critical section object.
On Windows it uses CRITICAL_SECTION, on everything else it uses pthread's mutex library.
It simulates the Critical Section behavior on non-Windows, as well (meaning a thread can
safely Enter the mutex multiple times, provided it Leaves the same number of times)
*/
#ifndef _WDL_MUTEX_H_
#define _WDL_MUTEX_H_
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
// define this if you wish to use carbon critical sections on OS X
// #define WDL_MAC_USE_CARBON_CRITSEC
#ifdef WDL_MAC_USE_CARBON_CRITSEC
#include <Carbon/Carbon.h>
#else
#include <pthread.h>
#endif
#endif
#include "wdltypes.h"
#include "wdlatomic.h"
#ifdef _DEBUG
#include <assert.h>
#endif
class WDL_Mutex {
public:
WDL_Mutex()
{
#ifdef _WIN32
InitializeCriticalSection(&m_cs);
#elif defined( WDL_MAC_USE_CARBON_CRITSEC)
MPCreateCriticalRegion(&m_cr);
#elif defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER) && !defined(__linux__)
const pthread_mutex_t tmp = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
m_mutex = tmp;
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
#ifdef __linux__
// todo: macos too?
pthread_mutexattr_setprotocol(&attr,PTHREAD_PRIO_INHERIT);
#endif
pthread_mutex_init(&m_mutex,&attr);
pthread_mutexattr_destroy(&attr);
#endif
}
~WDL_Mutex()
{
#ifdef _WIN32
DeleteCriticalSection(&m_cs);
#elif defined(WDL_MAC_USE_CARBON_CRITSEC)
MPDeleteCriticalRegion(m_cr);
#else
pthread_mutex_destroy(&m_mutex);
#endif
}
void Enter()
{
#ifdef _WIN32
EnterCriticalSection(&m_cs);
#elif defined(WDL_MAC_USE_CARBON_CRITSEC)
MPEnterCriticalRegion(m_cr,kDurationForever);
#else
pthread_mutex_lock(&m_mutex);
#endif
}
void Leave()
{
#ifdef _WIN32
LeaveCriticalSection(&m_cs);
#elif defined(WDL_MAC_USE_CARBON_CRITSEC)
MPExitCriticalRegion(m_cr);
#else
pthread_mutex_unlock(&m_mutex);
#endif
}
private:
#ifdef _WIN32
CRITICAL_SECTION m_cs;
#elif defined(WDL_MAC_USE_CARBON_CRITSEC)
MPCriticalRegionID m_cr;
#else
pthread_mutex_t m_mutex;
#endif
// prevent callers from copying mutexes accidentally
WDL_Mutex(const WDL_Mutex &cp)
{
#ifdef _DEBUG
assert(sizeof(WDL_Mutex) == 0);
#endif
}
WDL_Mutex &operator=(const WDL_Mutex &cp)
{
#ifdef _DEBUG
assert(sizeof(WDL_Mutex) == 0);
#endif
return *this;
}
} WDL_FIXALIGN;
class WDL_MutexLock {
public:
WDL_MutexLock(WDL_Mutex *m) : m_m(m) { if (m) m->Enter(); }
~WDL_MutexLock() { if (m_m) m_m->Leave(); }
// the caller modifies this, make sure it unlocks the mutex first and locks the new mutex!
WDL_Mutex *m_m;
} WDL_FIXALIGN;
class WDL_SharedMutex
{
public:
WDL_SharedMutex() { m_sharedcnt=0; }
~WDL_SharedMutex() { }
void LockExclusive() // note: the calling thread must NOT have any shared locks, or deadlock WILL occur
{
m_mutex.Enter();
#ifdef _WIN32
while (m_sharedcnt>0) Sleep(1);
#else
while (m_sharedcnt>0) usleep(100);
#endif
}
void UnlockExclusive() { m_mutex.Leave(); }
void LockShared()
{
m_mutex.Enter();
wdl_atomic_incr(&m_sharedcnt);
m_mutex.Leave();
}
void UnlockShared()
{
wdl_atomic_decr(&m_sharedcnt);
}
void SharedToExclusive() // assumes a SINGLE shared lock by this thread!
{
m_mutex.Enter();
#ifdef _WIN32
while (m_sharedcnt>1) Sleep(1);
#else
while (m_sharedcnt>1) usleep(100);
#endif
UnlockShared();
}
void ExclusiveToShared() // assumes exclusive locked returns with shared locked
{
// already have exclusive lock
wdl_atomic_incr(&m_sharedcnt);
m_mutex.Leave();
}
private:
WDL_Mutex m_mutex;
volatile int m_sharedcnt;
// prevent callers from copying accidentally
WDL_SharedMutex(const WDL_SharedMutex &cp)
{
#ifdef _DEBUG
assert(sizeof(WDL_SharedMutex) == 0);
#endif
}
WDL_SharedMutex &operator=(const WDL_SharedMutex &cp)
{
#ifdef _DEBUG
assert(sizeof(WDL_SharedMutex) == 0);
#endif
return *this;
}
} WDL_FIXALIGN;
class WDL_MutexLockShared {
public:
WDL_MutexLockShared(WDL_SharedMutex *m) : m_m(m) { if (m) m->LockShared(); }
~WDL_MutexLockShared() { if (m_m) m_m->UnlockShared(); }
private:
WDL_SharedMutex *m_m;
} WDL_FIXALIGN;
class WDL_MutexLockExclusive {
public:
WDL_MutexLockExclusive(WDL_SharedMutex *m) : m_m(m) { if (m) m->LockExclusive(); }
~WDL_MutexLockExclusive() { if (m_m) m_m->UnlockExclusive(); }
private:
WDL_SharedMutex *m_m;
} WDL_FIXALIGN;
#endif