[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
This commit is contained in:
Don Ho 2015-06-12 22:10:37 +02:00
parent acb713d9e6
commit 9ad71107e9
5 changed files with 545 additions and 6 deletions

View File

@ -25,19 +25,24 @@
// along with this program; if not, write to the Free Software // along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include "LongRunningOperation.h" #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() LongRunningOperation::LongRunningOperation()
{ {
//_operationMutex.lock(); Mutex::ClassLevelLockable<LongRunningOperation>::mutex.lock();
} }
LongRunningOperation::~LongRunningOperation() LongRunningOperation::~LongRunningOperation()
{ {
//_operationMutex.unlock(); Mutex::ClassLevelLockable<LongRunningOperation>::mutex.unlock();
} }

View File

@ -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 <cassert>
#include <iostream>
#include <cerrno>
#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

View File

@ -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 <windows.h>
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 T>
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<class T> Mutex Mutex::ClassLevelLockable<T>::mutex;
} // namespace Yuni
# include "mutex.hxx"

View File

@ -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

View File

@ -139,6 +139,7 @@ copy ..\src\contextMenu.xml ..\bin\contextMenu.xml
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\src\MISC\Common\LongRunningOperation.cpp" /> <ClCompile Include="..\src\MISC\Common\LongRunningOperation.cpp" />
<ClCompile Include="..\src\MISC\Common\mutex.cpp" />
<ClCompile Include="..\src\WinControls\AboutDlg\AboutDlg.cpp" /> <ClCompile Include="..\src\WinControls\AboutDlg\AboutDlg.cpp" />
<ClCompile Include="..\src\WinControls\AnsiCharPanel\ansiCharPanel.cpp" /> <ClCompile Include="..\src\WinControls\AnsiCharPanel\ansiCharPanel.cpp" />
<ClCompile Include="..\src\ScitillaComponent\AutoCompletion.cpp" /> <ClCompile Include="..\src\ScitillaComponent\AutoCompletion.cpp" />
@ -409,6 +410,8 @@ copy ..\src\contextMenu.xml ..\bin\contextMenu.xml
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\src\MISC\Common\LongRunningOperation.h" /> <ClInclude Include="..\src\MISC\Common\LongRunningOperation.h" />
<ClInclude Include="..\src\MISC\Common\mutex.h" />
<ClInclude Include="..\src\MISC\Common\mutex.hxx" />
<ClInclude Include="..\src\ScitillaComponent\resource.h" /> <ClInclude Include="..\src\ScitillaComponent\resource.h" />
<ClInclude Include="..\src\WinControls\AboutDlg\AboutDlg.h" /> <ClInclude Include="..\src\WinControls\AboutDlg\AboutDlg.h" />
<ClInclude Include="..\src\WinControls\AnsiCharPanel\ansiCharPanel.h" /> <ClInclude Include="..\src\WinControls\AnsiCharPanel\ansiCharPanel.h" />