From 9ad71107e98149dc3e3e628e839d79f042437eca Mon Sep 17 00:00:00 2001 From: Don Ho Date: Fri, 12 Jun 2015 22:10:37 +0200 Subject: [PATCH] [BUG_FIXED] Lock some operations for data integrality. Use mutex of Yuni library to lock critical operation for the compatibility of windows xp sp2. For more info of Yuni library: https://github.com/libyuni --- .../src/MISC/Common/LongRunningOperation.cpp | 17 +- PowerEditor/src/MISC/Common/mutex.cpp | 181 ++++++++++++++ PowerEditor/src/MISC/Common/mutex.h | 234 ++++++++++++++++++ PowerEditor/src/MISC/Common/mutex.hxx | 116 +++++++++ PowerEditor/visual.net/notepadPlus.vcxproj | 3 + 5 files changed, 545 insertions(+), 6 deletions(-) create mode 100644 PowerEditor/src/MISC/Common/mutex.cpp create mode 100644 PowerEditor/src/MISC/Common/mutex.h create mode 100644 PowerEditor/src/MISC/Common/mutex.hxx diff --git a/PowerEditor/src/MISC/Common/LongRunningOperation.cpp b/PowerEditor/src/MISC/Common/LongRunningOperation.cpp index e359b933..38c34597 100644 --- a/PowerEditor/src/MISC/Common/LongRunningOperation.cpp +++ b/PowerEditor/src/MISC/Common/LongRunningOperation.cpp @@ -25,19 +25,24 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - #include "LongRunningOperation.h" +#include "mutex.h" + +using namespace Yuni; + -// Due to retro-compatibility reason (with xp sp2), we use ::CreateMutex() instead of std::recursive_mutex -// TODO : use Windows Mutex to lock/unlock operations LongRunningOperation::LongRunningOperation() { - //_operationMutex.lock(); + Mutex::ClassLevelLockable::mutex.lock(); } + LongRunningOperation::~LongRunningOperation() { - //_operationMutex.unlock(); -} \ No newline at end of file + Mutex::ClassLevelLockable::mutex.unlock(); +} + + + diff --git a/PowerEditor/src/MISC/Common/mutex.cpp b/PowerEditor/src/MISC/Common/mutex.cpp new file mode 100644 index 00000000..69e72366 --- /dev/null +++ b/PowerEditor/src/MISC/Common/mutex.cpp @@ -0,0 +1,181 @@ +// YUNI's default license is the GNU Lesser Public License (LGPL), with some +// exclusions (see below). This basically means that you can get the full source +// code for nothing, so long as you adhere to a few rules. +// +// Under the LGPL you may use YUNI for any purpose you wish, and modify it if you +// require, as long as you: +// +// Pass on the (modified) YUNI source code with your software, with original +// copyrights intact : +// * If you distribute electronically, the source can be a separate download +// (either from your own site if you modified YUNI, or to the official YUNI +// website if you used an unmodified version) – just include a link in your +// documentation +// * If you distribute physical media, the YUNI source that you used to build +// your application should be included on that media +// Make it clear where you have customised it. +// +// In addition to the LGPL license text, the following exceptions / clarifications +// to the LGPL conditions apply to YUNI: +// +// * Making modifications to YUNI configuration files, build scripts and +// configuration headers such as yuni/platform.h in order to create a +// customised build setup of YUNI with the otherwise unmodified source code, +// does not constitute a derived work +// * Building against YUNI headers which have inlined code does not constitute a +// derived work +// * Code which subclasses YUNI classes outside of the YUNI libraries does not +// form a derived work +// * Statically linking the YUNI libraries into a user application does not make +// the user application a derived work. +// * Using source code obsfucation on the YUNI source code when distributing it +// is not permitted. +// As per the terms of the LGPL, a "derived work" is one for which you have to +// distribute source code for, so when the clauses above define something as not +// a derived work, it means you don't have to distribute source code for it. +// However, the original YUNI source code with all modifications must always be +// made available. + +#include "mutex.h" +#include +#include +#include + +#if YUNI_ATOMIC_MUST_USE_MUTEX != 0 +#warning Atomic types must ue mutex. the implementation should be checked YUNI_OS_GCC_VERSION +#endif + + + +namespace Yuni +{ + + enum // anonymous + { + /*! + ** \brief The spin count for the critical section object + ** + ** On single-processor systems, the spin count is ignored and the critical section + ** spin count is set to 0 (zero). On multiprocessor systems, if the critical section + ** is unavailable, the calling thread spinsdwSpinCount times before performing a + ** wait operation on a semaphore associated with the critical section. If the critical + ** section becomes free during the spin operation, the calling thread avoids the + ** wait operation. + ** \see http://msdn.microsoft.com/en-us/library/ms683476%28v=vs.85%29.aspx + */ + spinCount = 3000, + }; + + + + inline void Mutex::destroy() + { + # ifndef YUNI_NO_THREAD_SAFE + # ifdef YUNI_OS_WINDOWS + DeleteCriticalSection(&pSection); + # else + switch (::pthread_mutex_destroy(&pLock)) + { + case 0: // Ok good + { + break; + } + // If an error happens, we will let the program continue but + // it can becaome ugly around here... + case EBUSY: + { + std::cerr << "\nattempt to destroy a mutex while it is locked or referenced\n"; + assert(false and "attempt to destroy a mutex while it is locked or referenced"); + break; + } + default: + { + std::cerr << "\nfailed to destroy a mutex\n"; + assert(false and "\nfailed to destroy a mutex\n"); + break; + } + } + ::pthread_mutexattr_destroy(&pAttr); + # endif + # endif // no thread safe + } + + + inline void Mutex::copy(const Mutex& rhs) + { + # ifndef YUNI_NO_THREAD_SAFE + # ifdef YUNI_OS_WINDOWS + InitializeCriticalSectionAndSpinCount(&pSection, spinCount); + (void) rhs; // unused + # else + ::pthread_mutexattr_init(&pAttr); + int type; // = PTHREAD_MUTEX_NORMAL; + if (0 == ::pthread_mutexattr_gettype(&rhs.pAttr, &type)) + { + if (PTHREAD_MUTEX_RECURSIVE == type) + { + # if defined(YUNI_OS_DARWIN) or defined(YUNI_OS_FREEBSD) or defined(YUNI_OS_SOLARIS) or defined(YUNI_OS_SUNOS) or defined(YUNI_OS_HAIKU) or defined(YUNI_OS_CYGWIN) + ::pthread_mutexattr_settype(&pAttr, PTHREAD_MUTEX_RECURSIVE); + # else + ::pthread_mutexattr_settype(&pAttr, PTHREAD_MUTEX_RECURSIVE_NP); + # endif + } + } + ::pthread_mutex_init(& pLock, &pAttr); + # endif + # else + (void) rhs; // unused + # endif // no thread safe + } + + + + Mutex::Mutex(const Mutex& rhs) + { + copy(rhs); + } + + + Mutex::~Mutex() + { + destroy(); + } + + + Mutex::Mutex(bool recursive) + { + # ifndef YUNI_NO_THREAD_SAFE + # ifdef YUNI_OS_WINDOWS + (void) recursive; // already recursive on Windows + InitializeCriticalSectionAndSpinCount(&pSection, spinCount); + # else + ::pthread_mutexattr_init(&pAttr); + if (recursive) + { + # if defined(YUNI_OS_DARWIN) or defined(YUNI_OS_FREEBSD) or defined(YUNI_OS_SOLARIS) or defined(YUNI_OS_SUNOS) or defined(YUNI_OS_HAIKU) or defined(YUNI_OS_CYGWIN) + ::pthread_mutexattr_settype(&pAttr, PTHREAD_MUTEX_RECURSIVE); + # else + ::pthread_mutexattr_settype(&pAttr, PTHREAD_MUTEX_RECURSIVE_NP); + # endif + } + ::pthread_mutex_init(&pLock, &pAttr); + # endif + # else + (void) recursive; + # endif + } + + + Mutex& Mutex::operator = (const Mutex& rhs) + { + // We will recreate the mutex + destroy(); + copy(rhs); + return *this; + } + + + + + +} // namespace Yuni diff --git a/PowerEditor/src/MISC/Common/mutex.h b/PowerEditor/src/MISC/Common/mutex.h new file mode 100644 index 00000000..44a5c15c --- /dev/null +++ b/PowerEditor/src/MISC/Common/mutex.h @@ -0,0 +1,234 @@ +// YUNI's default license is the GNU Lesser Public License (LGPL), with some +// exclusions (see below). This basically means that you can get the full source +// code for nothing, so long as you adhere to a few rules. +// +// Under the LGPL you may use YUNI for any purpose you wish, and modify it if you +// require, as long as you: +// +// Pass on the (modified) YUNI source code with your software, with original +// copyrights intact : +// * If you distribute electronically, the source can be a separate download +// (either from your own site if you modified YUNI, or to the official YUNI +// website if you used an unmodified version) – just include a link in your +// documentation +// * If you distribute physical media, the YUNI source that you used to build +// your application should be included on that media +// Make it clear where you have customised it. +// +// In addition to the LGPL license text, the following exceptions / clarifications +// to the LGPL conditions apply to YUNI: +// +// * Making modifications to YUNI configuration files, build scripts and +// configuration headers such as yuni/platform.h in order to create a +// customised build setup of YUNI with the otherwise unmodified source code, +// does not constitute a derived work +// * Building against YUNI headers which have inlined code does not constitute a +// derived work +// * Code which subclasses YUNI classes outside of the YUNI libraries does not +// form a derived work +// * Statically linking the YUNI libraries into a user application does not make +// the user application a derived work. +// * Using source code obsfucation on the YUNI source code when distributing it +// is not permitted. +// As per the terms of the LGPL, a "derived work" is one for which you have to +// distribute source code for, so when the clauses above define something as not +// a derived work, it means you don't have to distribute source code for it. +// However, the original YUNI source code with all modifications must always be +// made available. + +#pragma once +#define YUNI_OS_WINDOWS +#define YUNI_HAS_CPP_MOVE +#define YUNI_DECL + +#include + + + +namespace Yuni +{ + + /*! + ** \brief Mechanism to avoid the simultaneous use of a common resource + ** + ** \ingroup Threads + */ + class YUNI_DECL Mutex final + { + public: + /*! + ** \brief A class-level locking mechanism + ** + ** A class-level locking operation locks all objects in a given class during that operation + */ + template + class ClassLevelLockable + { + public: + //! A dedicated mutex for the class T + static Mutex mutex; + + }; // class ClassLevelLockable + + + public: + //! \name Constructor & Destructor + //@{ + /*! + ** \brief Default constructor + ** + ** Recursive by default to keep homogeneous behavior between + ** platforms. + */ + explicit Mutex(bool recursive = true); + /*! + ** \brief Copy constructor + ** + ** This constructor does actually nothing but it allows the compilation + ** of other classes which would implement a copy constructor + */ + Mutex(const Mutex&); + + # ifdef YUNI_HAS_CPP_MOVE + // an OS's native mutex must have invariant address and thus can not be moved + Mutex(Mutex&&) = delete; + #endif + + /*! + ** \brief Destructor + */ + ~Mutex(); + //@} + + //! \name Lock & Unlock + //@{ + /*! + ** \brief Lock the mutex + */ + void lock(); + + /*! + ** \brief Try to lock the mutex + ** + ** \return True if the mutex has been locked, false otherwise + */ + bool trylock(); + + /*! + ** \brief Release the lock + */ + void unlock(); + //@} + + # ifndef YUNI_NO_THREAD_SAFE + # ifndef YUNI_OS_WINDOWS + //! \name Native + //@{ + //! Get the original PThread mutex + ::pthread_mutex_t& pthreadMutex(); + //! Get the original PThread mutex (const) + const ::pthread_mutex_t& pthreadMutex() const; + //@} + # endif + # endif + + + //! \name Operators + //@{ + //! Operator = (do nothing) + Mutex& operator = (const Mutex&); + # ifdef YUNI_HAS_CPP_MOVE + // an OS's native mutex must have invariant address and thus can not be moved + Mutex& operator = (Mutex&&) = delete; + #endif + //@} + + + private: + //! Destroy the current mutex + inline void destroy(); + //! Create the mutex with settings from another mutex + inline void copy(const Mutex& rhs); + + private: + # ifndef YUNI_NO_THREAD_SAFE + # ifdef YUNI_OS_WINDOWS + //! The critical section + CRITICAL_SECTION pSection; + # else + //! The PThread mutex + ::pthread_mutex_t pLock; + ::pthread_mutexattr_t pAttr; + # endif + # endif + + }; // class Mutex + + + + + /*! + ** \brief Locks a mutex in the constructor and unlocks it in the destructor (RAII). + ** + ** This class is especially usefull for `get` accessor` and/or returned values + ** which have to be thread-safe. + ** This is a very common C++ idiom, known as "Resource Acquisition Is Initialization" (RAII). + ** + ** \code + ** class Foo + ** { + ** public: + ** Foo() : pValue(42) {} + ** ~Foo() {} + ** int getValue() + ** { + ** MutexLocker locker(pMutex); + ** return pValue; + ** } + ** void setValue(const int i) + ** { + ** pMutex.lock(); + ** pValue = i; + ** pMutex.unlock(); + ** } + ** private: + ** int pValue; + ** Mutex pMutex; + ** }; + ** \endcode + */ + class MutexLocker final + { + public: + //! \name Constructor & Destructor + //@{ + /*! + ** \brief Constructor + ** + ** \param m The mutex to lock + */ + MutexLocker(Mutex& m); + //! Destructor + ~MutexLocker(); + //@} + + MutexLocker& operator = (const MutexLocker&) = delete; + + private: + //! Reference to the real mutex + Mutex& pMutex; + + }; // MutexLocker + + + + + //! All mutexes for each class + template Mutex Mutex::ClassLevelLockable::mutex; + + + + +} // namespace Yuni + +# include "mutex.hxx" diff --git a/PowerEditor/src/MISC/Common/mutex.hxx b/PowerEditor/src/MISC/Common/mutex.hxx new file mode 100644 index 00000000..2d0c3810 --- /dev/null +++ b/PowerEditor/src/MISC/Common/mutex.hxx @@ -0,0 +1,116 @@ +// YUNI's default license is the GNU Lesser Public License (LGPL), with some +// exclusions (see below). This basically means that you can get the full source +// code for nothing, so long as you adhere to a few rules. + +// Under the LGPL you may use YUNI for any purpose you wish, and modify it if you +// require, as long as you: +// +// Pass on the (modified) YUNI source code with your software, with original +// copyrights intact : +// * If you distribute electronically, the source can be a separate download +// (either from your own site if you modified YUNI, or to the official YUNI +// website if you used an unmodified version) – just include a link in your +// documentation +// * If you distribute physical media, the YUNI source that you used to build +// your application should be included on that media +// Make it clear where you have customised it. +// +// In addition to the LGPL license text, the following exceptions / clarifications +// to the LGPL conditions apply to YUNI: +// +// * Making modifications to YUNI configuration files, build scripts and +// configuration headers such as yuni/platform.h in order to create a +// customised build setup of YUNI with the otherwise unmodified source code, +// does not constitute a derived work +// * Building against YUNI headers which have inlined code does not constitute a +// derived work +// * Code which subclasses YUNI classes outside of the YUNI libraries does not +// form a derived work +// * Statically linking the YUNI libraries into a user application does not make +// the user application a derived work. +// * Using source code obsfucation on the YUNI source code when distributing it +// is not permitted. +// As per the terms of the LGPL, a "derived work" is one for which you have to +// distribute source code for, so when the clauses above define something as not +// a derived work, it means you don't have to distribute source code for it. +// However, the original YUNI source code with all modifications must always be +// made available. + +#pragma once +#include "mutex.h" + + + +namespace Yuni +{ + + inline void Mutex::lock() + { + # ifndef YUNI_NO_THREAD_SAFE + # ifdef YUNI_OS_WINDOWS + EnterCriticalSection(&pSection); + # else + ::pthread_mutex_lock(&pLock); + # endif + # endif + } + + + inline bool Mutex::trylock() + { + # ifndef YUNI_NO_THREAD_SAFE + # ifdef YUNI_OS_WINDOWS + return (0 != TryEnterCriticalSection(&pSection)); + # else + return (0 == ::pthread_mutex_trylock(&pLock)); + # endif + # else + return false; + # endif + } + + + inline void Mutex::unlock() + { + # ifndef YUNI_NO_THREAD_SAFE + # ifdef YUNI_OS_WINDOWS + LeaveCriticalSection(&pSection); + # else + ::pthread_mutex_unlock(&pLock); + # endif + # endif + } + + + # ifndef YUNI_NO_THREAD_SAFE + # ifndef YUNI_OS_WINDOWS + inline pthread_mutex_t& Mutex::pthreadMutex() + { + return pLock; + } + + inline const pthread_mutex_t& Mutex::pthreadMutex() const + { + return pLock; + } + # endif + # endif + + + + inline MutexLocker::MutexLocker(Mutex& m) : + pMutex(m) + { + m.lock(); + } + + + inline MutexLocker::~MutexLocker() + { + pMutex.unlock(); + } + + + + +} // namespace Yuni diff --git a/PowerEditor/visual.net/notepadPlus.vcxproj b/PowerEditor/visual.net/notepadPlus.vcxproj index e0ebab77..1636afc7 100644 --- a/PowerEditor/visual.net/notepadPlus.vcxproj +++ b/PowerEditor/visual.net/notepadPlus.vcxproj @@ -139,6 +139,7 @@ copy ..\src\contextMenu.xml ..\bin\contextMenu.xml + @@ -409,6 +410,8 @@ copy ..\src\contextMenu.xml ..\bin\contextMenu.xml + +