diff --git a/rtgui/CMakeLists.txt b/rtgui/CMakeLists.txt index 5f5dbc1c1..1adc6ddd4 100644 --- a/rtgui/CMakeLists.txt +++ b/rtgui/CMakeLists.txt @@ -21,7 +21,7 @@ set (BASESOURCEFILES preferences.cc profilepanel.cc saveasdlg.cc saveformatpanel.cc soundman.cc splash.cc thumbnail.cc tonecurve.cc toolbar.cc - guiutils.cc zoompanel.cc toolpanelcoord.cc + guiutils.cc threadutils.cc zoompanel.cc toolpanelcoord.cc thumbbrowserentrybase.cc batchqueueentry.cc batchqueue.cc lwbutton.cc lwbuttonset.cc batchqueuebuttonset.cc browserfilter.cc exiffiltersettings.cc diff --git a/rtgui/guiutils.cc b/rtgui/guiutils.cc index 9ca9af3e9..d3af3c597 100644 --- a/rtgui/guiutils.cc +++ b/rtgui/guiutils.cc @@ -29,11 +29,6 @@ using namespace std; -#if TRACE_MYRWMUTEX==1 && !defined NDEBUG -unsigned int MyReaderLock::readerLockCounter = 0; -unsigned int MyWriterLock::writerLockCounter = 0; -#endif - Glib::RefPtr MyExpander::inconsistentPBuf; Glib::RefPtr MyExpander::enabledPBuf; Glib::RefPtr MyExpander::disabledPBuf; diff --git a/rtgui/threadutils.cc b/rtgui/threadutils.cc new file mode 100644 index 000000000..7ba296081 --- /dev/null +++ b/rtgui/threadutils.cc @@ -0,0 +1,292 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2016 Adam Reichold + * + * 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 . + */ +#include "threadutils.h" + +#include +#include + +#ifdef WIN32 +#include +#endif + +#if STRICT_MUTEX && !NDEBUG + +void MyMutex::checkLock () +{ + if (locked) { + std::cerr << "MyMutex already locked!" << std::endl; + +#ifdef WIN32 + DebugBreak (); +#else + raise (SIGTRAP); +#endif + } + + locked = true; +} + +void MyMutex::checkUnlock () +{ + if (!locked) { + std::cerr << "MyMutex already unlocked!" << std::endl; + +#ifdef WIN32 + DebugBreak (); +#else + raise (SIGTRAP); +#endif + } + + locked = false; +} + +#endif + +#if !TRACE_MYRWMUTEX + +void MyReaderLock::acquire () +{ + if (locked) { + return; + } + + Glib::Threads::Mutex::Lock lock (mutex.mutex); + + if (mutex.writerCount == 0) { + // There's no writer operating, we can increment the writer count which will lock writers. + ++mutex.writerCount; + } else if (mutex.readerCount == 0) { + // The writer count is non null, but a reader can be the owner of the writer lock, + // which will be the case if the reader count is not zero too. + while (mutex.writerCount != 0) { + mutex.cond.wait(mutex.mutex); + } + + // Then, we can increment the writer count. + ++mutex.writerCount; + } + + // Finally, we can increment the reader count as well. + ++mutex.readerCount; + + locked = true; +} + +void MyReaderLock::release () +{ + if (!locked) { + return; + } + + Glib::Threads::Mutex::Lock lock (mutex.mutex); + + // decrement the writer number first... + --mutex.readerCount; + + if (mutex.readerCount == 0) { + // ...if no more reader, we decrement the writer count as well... + --mutex.writerCount; + + // ...and signal the next waiting reader/writer that it's free + mutex.cond.broadcast (); + } + + locked = false; +} + +void MyWriterLock::acquire () +{ + if (locked) { + return; + } + + Glib::Threads::Mutex::Lock lock (mutex.mutex); + + // The writer count is not zero, so we have to wait for it to be zero again... + while (mutex.writerCount != 0) { + mutex.cond.wait (mutex.mutex); + } + + // ...then we can increment the writer count. + ++mutex.writerCount; + + locked = true; +} + +void MyWriterLock::release () +{ + if (!locked) { + return; + } + + Glib::Threads::Mutex::Lock lock (mutex.mutex); + + // Decrement the writer number first... + if (--mutex.writerCount == 0) { + // ...and if the writer count is zero again, we can wake up the next writer or reader. + mutex.cond.broadcast (); + } + + locked = false; +} + +#else + +namespace +{ + +std::ostream& trace (const char* file, int line) +{ + const auto currentThread = Glib::Threads::Thread::self (); + + return std::cout << currentThread << ":" << file << ":" << line << ": "; +} + +} + +void MyReaderLock::acquire (const char* file, int line) +{ + if (locked) { + trace (file, line) << "MyReaderLock is already locked." << std::endl; + return; + } + + trace (file, line) << "Acquiring MyReaderLock..." << std::endl; + + Glib::Threads::Mutex::Lock lock (mutex.mutex); + + if (mutex.writerCount == 0) { + // There's no writer operating, we can increment the writer count which will lock writers. + ++mutex.writerCount; + } else if (mutex.readerCount == 0) { + // The writer count is non null, but a reader can be the owner of the writer lock, + // which will be the case if the reader count is not zero too. + while (mutex.writerCount != 0) { + trace (file, line) << "Waiting for current owner of MyWriterLock..." << std::endl + << "\tOwner thread: " << mutex.ownerThread << std::endl + << "\tLast writer file: " << mutex.lastWriterFile << std::endl + << "\tLast writer line: " << mutex.lastWriterLine << std::endl; + + mutex.cond.wait(mutex.mutex); + } + + // Then, we can increment the writer count. + ++mutex.writerCount; + + mutex.ownerThread = Glib::Threads::Thread::self (); + mutex.lastWriterFile = file; + mutex.lastWriterLine = line; + } + + // Finally, we can increment the reader count as well. + ++mutex.readerCount; + + trace (file, line) << "MyReaderLock is now locked, reader count is " << mutex.readerCount << ", writer count is " << mutex.writerCount << "." << std::endl; + locked = true; +} + +void MyReaderLock::release (const char* file, int line) +{ + if (!locked) { + trace (file, line) << "MyReaderLock is already unlocked." << std::endl; + return; + } + + trace (file, line) << "Releasing MyReaderLock..." << std::endl; + + Glib::Threads::Mutex::Lock lock (mutex.mutex); + + // decrement the writer number first... + --mutex.readerCount; + + if (mutex.readerCount == 0) { + // ...if no more reader, we decrement the writer count as well... + --mutex.writerCount; + + // ...and signal the next waiting reader/writer that it's free + mutex.cond.broadcast (); + + mutex.ownerThread = nullptr; + mutex.lastWriterFile = ""; + mutex.lastWriterLine = 0; + } + + trace (file, line) << "MyReaderLock is now unlocked, reader count is " << mutex.readerCount << ", writer count is " << mutex.writerCount << "." << std::endl; + locked = false; +} + +void MyWriterLock::acquire (const char* file, int line) +{ + if (locked) { + trace (file, line) << "MyWriterLock is already locked." << std::endl; + return; + } + + trace (file, line) << "Acquiring MyWriterLock..." << std::endl; + + Glib::Threads::Mutex::Lock lock (mutex.mutex); + + // The writer count is not zero, so we have to wait for it to be zero again... + while (mutex.writerCount != 0) { + trace (file, line) << "Waiting for current owner of MyWriterLock..." << std::endl + << "\tOwner thread: " << mutex.ownerThread << std::endl + << "\tLast writer file: " << mutex.lastWriterFile << std::endl + << "\tLast writer line: " << mutex.lastWriterLine << std::endl; + + mutex.cond.wait (mutex.mutex); + } + + // ...then we can increment the writer count. + ++mutex.writerCount; + + mutex.ownerThread = Glib::Threads::Thread::self (); + mutex.lastWriterFile = file; + mutex.lastWriterLine = line; + + trace (file, line) << "MyWriterLock is now locked, reader count is " << mutex.readerCount << ", writer count is " << mutex.writerCount << "." << std::endl; + locked = true; +} + +void MyWriterLock::release (const char* file, int line) +{ + if (!locked) { + trace (file, line) << "MyWriterLock is already unlocked." << std::endl; + return; + } + + trace (file, line) << "Releasing MyWriterLock..." << std::endl; + + Glib::Threads::Mutex::Lock lock (mutex.mutex); + + // Decrement the writer number first... + if (--mutex.writerCount == 0) { + // ...and if the writer count is zero again, we can wake up the next writer or reader. + mutex.cond.broadcast (); + + mutex.ownerThread = nullptr; + mutex.lastWriterFile = ""; + mutex.lastWriterLine = 0; + } + + trace (file, line) << "MyWriterLock is now unlocked, reader count is " << mutex.readerCount << ", writer count is " << mutex.writerCount << "." << std::endl; + locked = false; +} + +#endif diff --git a/rtgui/threadutils.h b/rtgui/threadutils.h index e8ef2f29e..d48bfcf01 100644 --- a/rtgui/threadutils.h +++ b/rtgui/threadutils.h @@ -16,30 +16,10 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ - - #ifndef _THREADUTILS_ #define _THREADUTILS_ -#include -#include // for raise() -#include - -#ifdef WIN32 -#include -#endif - - -#ifdef NDEBUG -// We don't trace mutex -#undef TRACE_MYRWMUTEX -#define TRACE_MYRWMUTEX 0 -#endif - - -// Uncomment this if you want to bypass the CMakeList options and force the values -// Of course, DO NOT COMMIT! - +// Uncomment this if you want to bypass the CMakeList options and force the values, but do not commit! //#undef PROTECT_VECTORS //#define PROTECT_VECTORS 1 //#undef TRACE_MYRWMUTEX @@ -47,650 +27,285 @@ //#undef STRICT_MUTEX //#define STRICT_MUTEX 1 -/** - * @brief Custom Mutex to replace Glib::Threads::Mutex, which behave differently on windows (recursive) and linux (non-recursive), by a recursive and "debugable" one - * - * This implementation will behave like a Glib::Threads::RecMutex (STRICT_MUTEX=0) or a Glib::Threads::Mutex (STRICT_MUTEX=1), but in this case, the application will - * crash instead of freezing. - * - * In Debug builds, a printf will let you know that the MyMutex was already locked - * - * The default and recommended mode is STRICT_MUTEX=1 - */ +#include -#ifdef WIN32 -class MyMutex : public Glib::RecMutex -{ +#if STRICT_MUTEX && NDEBUG +using MyMutexBase = Glib::Threads::Mutex; #else -class MyMutex : public Glib::Threads::RecMutex +using MyMutexBase = Glib::Threads::RecMutex; +#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 : private MyMutexBase { -#endif - -#if STRICT_MUTEX || !defined(NDEBUG) -private: - bool alreadyLocked; -#endif - public: class MyLock; -#if STRICT_MUTEX || !defined(NDEBUG) - MyMutex() : alreadyLocked(false) {} -#else - MyMutex() {} -#endif + MyMutex () = default; + MyMutex (const MyMutex&) = delete; + MyMutex& operator= (const MyMutex&) = delete; - void lock() - { -#ifdef WIN32 - Glib::RecMutex::lock(); -#else - Glib::Threads::RecMutex::lock(); -#endif -#if STRICT_MUTEX || !defined(NDEBUG) + void lock (); + bool trylock (); + void unlock (); - if (alreadyLocked) { -#ifndef NDEBUG - std::cout << "Warning: MyMutex already locked!" << std::endl; // breakpoint +#if STRICT_MUTEX && !NDEBUG +private: + bool locked = false; + void checkLock (); + void checkUnlock (); #endif -#if STRICT_MUTEX -#ifndef NDEBUG -#ifdef WIN32 - DebugBreak(); -#else - raise(SIGTRAP); -#endif -#else - raise(SIGINT); -#endif -#endif - } - - alreadyLocked = true; -#endif - } - - bool trylock() - { -#ifdef WIN32 - - if (Glib::RecMutex::trylock()) -#else - if (Glib::Threads::RecMutex::trylock()) -#endif - { -#if STRICT_MUTEX || !defined(NDEBUG) - - if (alreadyLocked) { -#ifndef NDEBUG - std::cout << "Warning: MyMutex already locked!" << std::endl; // breakpoint -#endif -#if STRICT_MUTEX -#ifndef NDEBUG -#ifdef WIN32 - DebugBreak(); -#else - raise(SIGTRAP); -#endif -#else - raise(SIGINT); -#endif -#endif - } - - alreadyLocked = true; -#endif - return true; - } - - return false; - } - - // Warning: the base class of MyMutex is RecMutex, but the mutex is said "unlocked" on first occurrence of "unlock", to avoid overhead. - void unlock() - { -#if STRICT_MUTEX || !defined(NDEBUG) - alreadyLocked = false; -#endif -#ifdef WIN32 - Glib::RecMutex::unlock(); -#else - Glib::Threads::RecMutex::unlock(); -#endif - } }; - -// Class copied from the Glibmm source code, to provide a workaround of the behavior's difference between Linux and Windows class MyMutex::MyLock { public: - explicit inline MyLock(MyMutex& mutex) : mutex_ (mutex), locked_ (true) - { - mutex_.lock(); - } -#ifdef WIN32 - inline MyLock(MyMutex& mutex, Glib::NotLock) : mutex_ (mutex), locked_ (false) {} - inline MyLock(MyMutex& mutex, Glib::TryLock) : mutex_ (mutex), locked_ (mutex.trylock()) {} -#else - inline MyLock(MyMutex& mutex, Glib::Threads::NotLock) : mutex_ (mutex), locked_ (false) {} - inline MyLock(MyMutex& mutex, Glib::Threads::TryLock) : mutex_ (mutex), locked_ (mutex.trylock()) {} -#endif - inline ~MyLock() - { - if(locked_) { - mutex_.unlock(); - } - } + explicit MyLock (MyMutex& mutex); + MyLock (MyMutex& mutex, Glib::Threads::NotLock); + MyLock (MyMutex& mutex, Glib::Threads::TryLock); - inline void acquire() - { - mutex_.lock(); - locked_ = true; - } - inline bool try_acquire() - { - locked_ = mutex_.trylock(); - return locked_; - } - inline void release() - { - mutex_.unlock(); - locked_ = false; - } - inline bool locked() const - { - return locked_; - } + ~MyLock (); + + MyLock (const MyLock&) = delete; + MyLock& operator= (const MyLock&) = delete; + + void acquire (); + bool try_acquire (); + void release (); private: - MyMutex& mutex_; - bool locked_; - - // noncopyable - MyLock(const MyMutex::Lock&); - MyMutex::Lock& operator=(const MyMutex::Lock&); + MyMutex& mutex; + bool locked; }; - /** - * @brief Custom RWLock with debugging feature, to replace the buggy Glib::RWLock (can have negative reader_count value!) - * - * It may be slower, but thread safe! + * @brief Custom implementation to replace Glib::Threads::RWLock */ class MyRWMutex { public: -#ifdef WIN32 - Glib::Mutex handlerMutex; // Having a recursive or non-recursive mutex is not important here, so we can use Glib::Mutex - Glib::Cond access; -#else - Glib::Threads::Mutex handlerMutex; // Having a recursive or non-recursive mutex is not important here, so we can use Glib::Threads::Mutex - Glib::Threads::Cond access; -#endif - size_t writerCount; - size_t readerCount; -#if TRACE_MYRWMUTEX - Glib::ustring lastWriterFile; - int lastWriterLine; - // Unfortunately, ownerThread may not be the culprit of a deadlock, it can be another concurrent Reader... - void* ownerThread; + MyRWMutex () = default; + MyRWMutex (const MyRWMutex&) = delete; + MyRWMutex& operator= (const MyRWMutex&) = delete; - MyRWMutex() : writerCount(0), readerCount(0), lastWriterLine(0), ownerThread(NULL) {} -#else - MyRWMutex() : writerCount(0), readerCount(0) {} + friend class MyReaderLock; + friend class MyWriterLock; + +private: + Glib::Threads::Mutex mutex; + Glib::Threads::Cond cond; + + std::size_t writerCount = 0; + std::size_t readerCount = 0; + +#if TRACE_MYRWMUTEX + Glib::Threads::Thread* ownerThread = nullptr; + const char* lastWriterFile = ""; + int lastWriterLine = 0; #endif }; /** - * @brief Custom ReaderLock with debugging feature, to replace the buggy Glib::RWLock (can have negative reader_count value!) - * + * @brief Custom implementation to replace Glib::Threads::RWLock::ReaderLock */ class MyReaderLock { +public: + ~MyReaderLock (); - MyRWMutex& rwMutex; + MyReaderLock (const MyReaderLock&) = delete; + MyReaderLock& operator= (const MyReaderLock&) = delete; + +#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; - -#if TRACE_MYRWMUTEX - static unsigned int readerLockCounter; - int locknumber; - -public: - inline MyReaderLock(MyRWMutex& mutex, const char* name, const char* file, const int line) : rwMutex(mutex), locked(false), locknumber(0) -#else -public: - inline MyReaderLock(MyRWMutex & mutex) : rwMutex(mutex) -#endif - - { - // to operate safely - rwMutex.handlerMutex.lock(); - -#if TRACE_MYRWMUTEX - locknumber = readerLockCounter++; - void* thread = Glib::Thread::self(); - std::cout << thread << "/" << locknumber << ":" << name << " / " << file << " : " << line << " - locking - R"; -#endif - - if (!rwMutex.writerCount) { - // There's no writer operating, we can increment the writer count which will lock writers - ++rwMutex.writerCount; -#if TRACE_MYRWMUTEX - std::cout << " ++ new owner"; -#endif - } else { - // The writer count is non null, but we can be the owner of the writer lock - // It will be the case if the reader count is non null too. - if (!rwMutex.readerCount) { - // the mutex is in real write mode, we're waiting to see it null -#if TRACE_MYRWMUTEX - std::cout << " waiting..." << std::endl << "Current writer owner: " << rwMutex.lastWriterFile << " : " << rwMutex.lastWriterLine << std::endl; -#endif - - while (rwMutex.writerCount) { - rwMutex.access.wait(rwMutex.handlerMutex); - } - - ++rwMutex.writerCount; -#if TRACE_MYRWMUTEX - rwMutex.lastWriterFile = file; - rwMutex.lastWriterLine = line; - rwMutex.ownerThread = thread; - std::cout << thread << "/" << locknumber << ":" << name << " / " << file << " : " << line << " - locking - R ++ new owner"; -#endif - } - } - - // then we can increment the reader count - ++rwMutex.readerCount; - -#if TRACE_MYRWMUTEX - std::cout << " - ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl; -#endif - - rwMutex.handlerMutex.unlock(); - - locked = true; - } -#if TRACE_MYRWMUTEX - // locks the MyRWMutex with Read access if this MyReaderLock has not already locked it, otherwise return safely - inline void acquire(const char* file, const int line) -#else - // locks the MyRWMutex with Read access if this MyReaderLock has not already locked it, otherwise return safely - inline void acquire() -#endif - { -#if TRACE_MYRWMUTEX - void* thread = Glib::Thread::self(); -#endif - - if (!locked) { - // to operate safely - rwMutex.handlerMutex.lock(); - -#if TRACE_MYRWMUTEX - std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - R (lock)"; -#endif - - if (!rwMutex.writerCount) { - // There's no writer operating, we can increment the writer count which will lock writers - ++rwMutex.writerCount; -#if TRACE_MYRWMUTEX - std::cout << " ++ new owner"; -#endif - } else { - // The writer count is non null, but a reader can be the owner of the writer lock, - // it will be the case if the reader count is non null too. - if (!rwMutex.readerCount) { - // the mutex is in real write mode, we're waiting to see it null -#if TRACE_MYRWMUTEX - std::cout << " waiting..." << std::endl << "Current writer owner: " << rwMutex.lastWriterFile << " : " << rwMutex.lastWriterLine << std::endl; -#endif - - while (rwMutex.writerCount) { - rwMutex.access.wait(rwMutex.handlerMutex); - } - - ++rwMutex.writerCount; -#if TRACE_MYRWMUTEX - rwMutex.lastWriterFile = file; - rwMutex.lastWriterLine = line; - rwMutex.ownerThread = thread; - std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - R (lock) ++ new owner"; -#endif - } - } - - // then we can increment the reader count - ++rwMutex.readerCount; - -#if TRACE_MYRWMUTEX - std::cout << " - ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl; -#endif - - rwMutex.handlerMutex.unlock(); - - locked = true; - } - -#if TRACE_MYRWMUTEX - else { - std::cout << thread << "/" << locknumber << " / already locked by this object - R (lock)" << std::endl; - } - -#endif - } - inline ~MyReaderLock() - { -#if TRACE_MYRWMUTEX - void* thread = Glib::Thread::self(); -#endif - - if (locked) { - // to operate safely - rwMutex.handlerMutex.lock(); - - // decrement the writer number first - --rwMutex.readerCount; - -#if TRACE_MYRWMUTEX - std::cout << thread << "/" << locknumber << " / unlocking - R - ReaderCount: " << rwMutex.readerCount; -#endif - - if (!rwMutex.readerCount) { - // no more reader, so we decrement the writer count - --rwMutex.writerCount; -#if TRACE_MYRWMUTEX - rwMutex.lastWriterFile = ""; - rwMutex.lastWriterLine = 0; - rwMutex.ownerThread = NULL; - std::cout << " -- new owner possible!" << " >>> ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount; -#endif - // and signal the next waiting reader/writer that it's free - rwMutex.access.broadcast(); - } - -#if TRACE_MYRWMUTEX - std::cout << std::endl; -#endif - - rwMutex.handlerMutex.unlock(); - } - -#if TRACE_MYRWMUTEX - else { - std::cout << thread << "/" << locknumber << " / already unlocked by this object - R" << std::endl; - } - -#endif - } -#if TRACE_MYRWMUTEX - // releases the MyRWMutex with Write access if this MyWriterLock has already locked it, otherwise return safely - inline void release(const char* file, const int line) -#else - // releases the MyRWMutex with Write access if this MyWriterLock has already locked it, otherwise return safely - inline void release() -#endif - { -#if TRACE_MYRWMUTEX - void* thread = Glib::Thread::self(); -#endif - - if (locked) { - // to operate safely - rwMutex.handlerMutex.lock(); - - // decrement the writer number first - --rwMutex.readerCount; - -#if TRACE_MYRWMUTEX - std::cout << thread << "/" << locknumber << " / unlocking - R (release) - ReaderCount: " << rwMutex.readerCount; -#endif - - if (!rwMutex.readerCount) { - // no more reader, so we decrement the writer count - --rwMutex.writerCount; -#if TRACE_MYRWMUTEX - rwMutex.lastWriterFile = ""; - rwMutex.lastWriterLine = 0; - rwMutex.ownerThread = NULL; - std::cout << " -- new owner possible!" << " >>> ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount; -#endif - // and signal the next waiting reader/writer that it's free - rwMutex.access.broadcast(); - } - -#if TRACE_MYRWMUTEX - std::cout << std::endl; -#endif - - rwMutex.handlerMutex.unlock(); - - locked = false; - } - -#if TRACE_MYRWMUTEX - else { - std::cout << thread << "/" << locknumber << " / already unlocked - R (release)" << std::endl; - } - -#endif - } }; /** - * @brief Custom WriterLock with debugging feature, to replace the buggy Glib::RWLock (can have negative reader_count value!) - * + * @brief Custom implementation to replace Glib::Threads::RWLock::WriterLock */ class MyWriterLock { +public: + ~MyWriterLock (); - MyRWMutex& rwMutex; + MyWriterLock (const MyWriterLock&) = delete; + MyWriterLock& operator= (const MyWriterLock&) = delete; + +#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; - -#if TRACE_MYRWMUTEX - static unsigned int writerLockCounter; - int locknumber; -public: - inline MyWriterLock(MyRWMutex& mutex, const char* name, const char* file, const int line) : rwMutex(mutex), locked(false), locknumber(0) -#else -public: - inline MyWriterLock(MyRWMutex & mutex) : rwMutex(mutex) -#endif - { - // to operate safely - rwMutex.handlerMutex.lock(); - -#if TRACE_MYRWMUTEX - locknumber = writerLockCounter++; - void* thread = Glib::Thread::self(); - std::cout << thread << "/" << locknumber << ":" << name << " / " << file << " : " << line << " - locking - W"; -#endif - - if (rwMutex.writerCount) { - // The writer count is non null, so we have to wait for it to be null again -#if TRACE_MYRWMUTEX - std::cout << " waiting..." << std::endl << "Current writer owner: " << rwMutex.lastWriterFile << " : " << rwMutex.lastWriterLine << std::endl; -#endif - - while (rwMutex.writerCount) { - rwMutex.access.wait(rwMutex.handlerMutex); - } - -#if TRACE_MYRWMUTEX - std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - W"; -#endif - } - - // then we can increment the writer count - ++rwMutex.writerCount; - -#if TRACE_MYRWMUTEX - rwMutex.lastWriterFile = file; - rwMutex.lastWriterLine = line; - rwMutex.ownerThread = thread; - std::cout << " ++ new owner <<< ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl; -#endif - - rwMutex.handlerMutex.unlock(); - - locked = true; - } -#if TRACE_MYRWMUTEX - // locks the MyRWMutex with Read access if this MyReaderLock has not already locked it, otherwise return safely - inline void acquire(const char* file, const int line) -#else - // locks the MyRWMutex with Read access if this MyReaderLock has not already locked it, otherwise return safely - inline void acquire() -#endif - { -#if TRACE_MYRWMUTEX - void* thread = Glib::Thread::self(); -#endif - - if (!locked) { - // to operate safely - rwMutex.handlerMutex.lock(); - -#if TRACE_MYRWMUTEX - std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - W (lock)"; -#endif - - if (rwMutex.writerCount) { - // The writer count is non null, so we have to wait for it to be null again -#if TRACE_MYRWMUTEX - std::cout << " waiting..." << std::endl << "Current writer owner: " << rwMutex.lastWriterFile << " : " << rwMutex.lastWriterLine << std::endl; -#endif - - while (rwMutex.writerCount) { - rwMutex.access.wait(rwMutex.handlerMutex); - } - -#if TRACE_MYRWMUTEX - std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - W (lock)"; -#endif - } - - // then we can increment the reader count - ++rwMutex.writerCount; - -#if TRACE_MYRWMUTEX - rwMutex.lastWriterFile = file; - rwMutex.lastWriterLine = line; - rwMutex.ownerThread = thread; - std::cout << " ++ new owner <<< ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl; -#endif - - rwMutex.handlerMutex.unlock(); - - locked = true; - } - -#if TRACE_MYRWMUTEX - else { - std::cout << thread << "/" << locknumber << " / already locked by this object - W (lock)" << std::endl; - } - -#endif - } - inline ~MyWriterLock() - { -#if TRACE_MYRWMUTEX - void* thread = Glib::Thread::self(); -#endif - - if (locked) { - // to operate safely - rwMutex.handlerMutex.lock(); - - // decrement the writer number first - --rwMutex.writerCount; - -#if TRACE_MYRWMUTEX - std::cout << thread << "/" << locknumber << " / unlocking - W"; -#endif - - if (!rwMutex.writerCount) { -#if TRACE_MYRWMUTEX - rwMutex.lastWriterFile = ""; - rwMutex.lastWriterLine = 0; - rwMutex.ownerThread = NULL; - std::cout << " -- new owner possible!"; -#endif - // The writer count is null again, so we can wake up the next writer or reader - rwMutex.access.broadcast(); - } - -#if TRACE_MYRWMUTEX - std::cout << " <<< ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl; -#endif - - rwMutex.handlerMutex.unlock(); - } - -#if TRACE_MYRWMUTEX - else { - std::cout << thread << "/" << locknumber << " / already unlocked by this object - W" << std::endl; - } - -#endif - } -#if TRACE_MYRWMUTEX - // releases the MyRWMutex with Write access if this MyWriterLock has already locked it, otherwise return safely - inline void release(const char* file, const int line) -#else - // releases the MyRWMutex with Write access if this MyWriterLock has already locked it, otherwise return safely - inline void release() -#endif - { -#if TRACE_MYRWMUTEX - void* thread = Glib::Thread::self(); -#endif - - if (locked) { - // to operate safely - rwMutex.handlerMutex.lock(); - - // decrement the writer number first - --rwMutex.writerCount; - -#if TRACE_MYRWMUTEX - std::cout << thread << "/" << locknumber << " / unlocking - W (release)"; -#endif - - if (!rwMutex.writerCount) { -#if TRACE_MYRWMUTEX - rwMutex.lastWriterFile = ""; - rwMutex.lastWriterLine = 0; - rwMutex.ownerThread = NULL; - std::cout << " -- new owner possible!"; -#endif - // The writer count is null again, so we can wake up the next writer or reader - rwMutex.access.broadcast(); - } - -#if TRACE_MYRWMUTEX - std::cout << " <<< ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl; -#endif - - rwMutex.handlerMutex.unlock(); - - locked = false; - } - -#if TRACE_MYRWMUTEX - else { - std::cout << thread << "/" << locknumber << " / already unlocked by this object - W (release)" << std::endl; - } - -#endif - } }; +inline void MyMutex::lock () +{ + MyMutexBase::lock (); + +#if STRICT_MUTEX && !NDEBUG + checkLock (); +#endif +} + +inline bool MyMutex::trylock () +{ + if (MyMutexBase::trylock ()) { +#if STRICT_MUTEX && !NDEBUG + checkLock (); +#endif + + return true; + } + + 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 (MyMutex& mutex, Glib::Threads::NotLock) + : mutex (mutex) + , locked (false) +{ +} + +inline MyMutex::MyLock::MyLock (MyMutex& mutex, Glib::Threads::TryLock) + : mutex (mutex) + , locked (mutex.trylock ()) +{ +} + +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, #e, __FILE__, __LINE__); -#define MYWRITERLOCK(ln, e) MyWriterLock ln(e, #e, __FILE__, __LINE__); +#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__);