rawTherapee/rtgui/threadutils.h
2023-09-10 14:29:17 -07:00

300 lines
6.1 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee 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 3 of the License, or
* (at your option) any later version.
*
* RawTherapee 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 RawTherapee. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
// Uncomment this if you want to bypass the CMakeList options and force the values, but do not commit!
//#undef TRACE_MYRWMUTEX
//#define TRACE_MYRWMUTEX 1
//#undef STRICT_MUTEX
//#define STRICT_MUTEX 1
#include <mutex>
#include <thread>
#include <condition_variable>
#include "../rtengine/noncopyable.h"
#if STRICT_MUTEX && NDEBUG
using MyMutexBase = std::mutex;
#else
using MyMutexBase = std::recursive_mutex;
#endif
/**
* @brief Custom implementation to replace Glib::Threads::Mutex.
*
* Glib::Threads::Mutex shows different behaviour on Windows (recursive) and Linux (non-recursive).
* We therefore use a custom implementation that is optionally recursive and instrumented.
* It will behave like Glib::Threads::RecMutex (STRICT_MUTEX=0) or Glib::Threads::Mutex (STRICT_MUTEX=1).
* Debug builds with strict mutexes, will emit a message and crash immediately upon recursive locking.
*/
class MyMutex :
public rtengine::NonCopyable,
private MyMutexBase
{
public:
class MyLock;
void lock ();
bool trylock ();
void unlock ();
#if STRICT_MUTEX && !NDEBUG
MyMutex();
private:
bool locked;
bool checkLock (bool noError = false);
void checkUnlock ();
#endif
};
class MyMutex::MyLock :
public rtengine::NonCopyable
{
public:
explicit MyLock (MyMutex& mutex);
~MyLock ();
void acquire ();
bool try_acquire ();
void release ();
private:
MyMutex& mutex;
bool locked;
};
/**
* @brief Custom implementation to replace Glib::Threads::RWLock
*/
class MyRWMutex :
public rtengine::NonCopyable
{
public:
friend class MyReaderLock;
friend class MyWriterLock;
MyRWMutex();
private:
#if TRACE_MYRWMUTEX
std::thread::id ownerThread;
const char* lastWriterFile;
int lastWriterLine;
#endif
std::mutex mutex;
std::condition_variable cond;
std::size_t writerCount;
std::size_t readerCount;
};
/**
* @brief Custom implementation to replace Glib::Threads::RWLock::ReaderLock
*/
class MyReaderLock :
public rtengine::NonCopyable
{
public:
~MyReaderLock ();
#if !TRACE_MYRWMUTEX
explicit MyReaderLock (MyRWMutex& mutex);
void acquire ();
void release ();
#else
explicit MyReaderLock (MyRWMutex& mutex, const char* file, int line);
void acquire (const char* file, int line);
void release (const char* file, int line);
#endif
private:
MyRWMutex& mutex;
bool locked;
};
/**
* @brief Custom implementation to replace Glib::Threads::RWLock::WriterLock
*/
class MyWriterLock :
public rtengine::NonCopyable
{
public:
~MyWriterLock ();
#if !TRACE_MYRWMUTEX
explicit MyWriterLock (MyRWMutex& mutex);
void acquire ();
void release ();
#else
MyWriterLock (MyRWMutex& mutex, const char* file, int line);
void acquire (const char* file, int line);
void release (const char* file, int line);
#endif
private:
MyRWMutex& mutex;
bool locked;
};
inline void MyMutex::lock ()
{
MyMutexBase::lock ();
#if STRICT_MUTEX && !NDEBUG
checkLock ();
#endif
}
inline bool MyMutex::trylock ()
{
if (MyMutexBase::try_lock ()) {
#if STRICT_MUTEX && !NDEBUG
return checkLock(true);
#else
return true;
#endif
}
return false;
}
inline void MyMutex::unlock ()
{
#if STRICT_MUTEX && !NDEBUG
checkUnlock ();
#endif
MyMutexBase::unlock ();
}
inline MyMutex::MyLock::MyLock (MyMutex& mutex)
: mutex (mutex)
, locked (true)
{
mutex.lock();
}
inline MyMutex::MyLock::~MyLock ()
{
if (locked) {
mutex.unlock ();
}
}
inline void MyMutex::MyLock::acquire ()
{
mutex.lock ();
locked = true;
}
inline bool MyMutex::MyLock::try_acquire ()
{
return locked = mutex.trylock ();
}
inline void MyMutex::MyLock::release ()
{
mutex.unlock ();
locked = false;
}
#if !TRACE_MYRWMUTEX
inline MyReaderLock::MyReaderLock (MyRWMutex& mutex)
: mutex (mutex)
, locked (false)
{
acquire ();
}
inline MyWriterLock::MyWriterLock (MyRWMutex& mutex)
: mutex (mutex)
, locked (false)
{
acquire ();
}
inline MyReaderLock::~MyReaderLock ()
{
if (locked) {
release ();
}
}
inline MyWriterLock::~MyWriterLock ()
{
if (locked) {
release ();
}
}
#else
inline MyReaderLock::MyReaderLock (MyRWMutex& mutex, const char* file, int line)
: mutex (mutex)
, locked (false)
{
acquire (file, line);
}
inline MyWriterLock::MyWriterLock (MyRWMutex& mutex, const char* file, int line)
: mutex (mutex)
, locked (false)
{
acquire (file, line);
}
inline MyReaderLock::~MyReaderLock ()
{
if (locked) {
release (__FILE__, __LINE__);
}
}
inline MyWriterLock::~MyWriterLock ()
{
if (locked) {
release (__FILE__, __LINE__);
}
}
#endif
#if TRACE_MYRWMUTEX
#define MYREADERLOCK(ln, e) MyReaderLock ln(e, __FILE__, __LINE__);
#define MYWRITERLOCK(ln, e) MyWriterLock ln(e, __FILE__, __LINE__);
#define MYREADERLOCK_ACQUIRE(ln) ln.acquire(__FILE__, __LINE__);
#define MYWRITERLOCK_ACQUIRE(ln) ln.acquire(__FILE__, __LINE__);
#define MYREADERLOCK_RELEASE(ln) ln.release(__FILE__, __LINE__);
#define MYWRITERLOCK_RELEASE(ln) ln.release(__FILE__, __LINE__);
#else
#define MYREADERLOCK(ln, e) MyReaderLock ln(e);
#define MYWRITERLOCK(ln, e) MyWriterLock ln(e);
#define MYREADERLOCK_ACQUIRE(ln) ln.acquire();
#define MYWRITERLOCK_ACQUIRE(ln) ln.acquire();
#define MYREADERLOCK_RELEASE(ln) ln.release();
#define MYWRITERLOCK_RELEASE(ln) ln.release();
#endif