300 lines
6.1 KiB
C++
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
|