Commit 48c3a4c0 authored by Dave Moxey's avatar Dave Moxey

Re-enable thread safe mutexes only if NEKTAR_USE_THREAD_SAFETY is enabled

parent 8d4fd72a
......@@ -169,6 +169,14 @@ OPTION(NEKTAR_USE_MEMORY_POOLS
"Use memory pools to accelerate memory allocation." ON)
MARK_AS_ADVANCED(NEKTAR_USE_MEMORY_POOLS)
# Thread safety
OPTION(NEKTAR_USE_THREAD_SAFETY
"Guarantee thread safety in certain core Nektar++ classes." OFF)
MARK_AS_ADVANCED(NEKTAR_USE_THREAD_SAFETY)
IF (NEKTAR_USE_THREAD_SAFETY)
ADD_DEFINITIONS(-DNEKTAR_USE_THREAD_SAFETY)
ENDIF()
IF (MSVC)
# Needed for M_PI to be visible in visual studio.
ADD_DEFINITIONS(-D_USE_MATH_DEFINES)
......
......@@ -41,6 +41,11 @@
#include <string>
#include <memory>
#ifdef NEKTAR_USE_THREAD_SAFETY
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/locks.hpp>
#endif
#include <LibUtilities/BasicUtils/ErrorUtil.hpp>
namespace Nektar
......@@ -48,6 +53,12 @@ namespace Nektar
namespace LibUtilities
{
#ifdef NEKTAR_USE_THREAD_SAFETY
// Generate parameter typenames with default type of 'none'
typedef boost::unique_lock<boost::shared_mutex> WriteLock;
typedef boost::shared_lock<boost::shared_mutex> ReadLock;
#endif
/**
* @class NekFactory
*
......@@ -121,7 +132,7 @@ public:
typedef std::map<tKey, ModuleEntry, tPredicator> TMapFactory;
public:
NekFactory() {}
NekFactory() = default;
/**
* @brief Create an instance of the class referred to by \c idKey.
......@@ -134,6 +145,10 @@ public:
*/
tBaseSharedPtr CreateInstance(tKey idKey, tParam... args)
{
#ifdef NEKTAR_USE_THREAD_SAFETY
ReadLock vReadLock(m_mutex);
#endif
// Now try and find the key in the map.
auto it = getMapFactory()->find(idKey);
......@@ -142,6 +157,9 @@ public:
if (it != getMapFactory()->end())
{
ModuleEntry *tmp = &(it->second);
#ifdef NEKTAR_USE_THREAD_SAFETY
vReadLock.unlock();
#endif
if (tmp->m_func)
{
......@@ -183,6 +201,10 @@ public:
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator,
tDescription pDesc = "")
{
#ifdef NEKTAR_USE_THREAD_SAFETY
WriteLock vWriteLock(m_mutex);
#endif
ModuleEntry e(classCreator, pDesc);
getMapFactory()->insert(std::pair<tKey,ModuleEntry>(idKey, e));
return idKey;
......@@ -194,6 +216,10 @@ public:
*/
bool ModuleExists(tKey idKey)
{
#ifdef NEKTAR_USE_THREAD_SAFETY
ReadLock vReadLock(m_mutex);
#endif
// Now try and find the key in the map.
auto it = getMapFactory()->find(idKey);
......@@ -210,6 +236,10 @@ public:
*/
void PrintAvailableClasses(std::ostream& pOut = std::cout)
{
#ifdef NEKTAR_USE_THREAD_SAFETY
ReadLock vReadLock(m_mutex);
#endif
pOut << std::endl << "Available classes: " << std::endl;
for (auto &it : *getMapFactory())
{
......@@ -232,6 +262,10 @@ public:
*/
tKey GetKey(tDescription pDesc)
{
#ifdef NEKTAR_USE_THREAD_SAFETY
ReadLock vReadLock(m_mutex);
#endif
for (auto &it : *getMapFactory())
{
if (it.second.m_desc == pDesc)
......@@ -251,6 +285,10 @@ public:
*/
std::string GetClassDescription(tKey idKey)
{
#ifdef NEKTAR_USE_THREAD_SAFETY
ReadLock vReadLock(m_mutex);
#endif
// Now try and find the key in the map.
auto it = getMapFactory()->find(idKey);
......@@ -275,6 +313,11 @@ private:
NekFactory& operator=(const NekFactory& rhs);
TMapFactory mMapFactory;
#ifdef NEKTAR_USE_THREAD_SAFETY
boost::shared_mutex m_mutex;
#endif
};
}
......
......@@ -40,6 +40,11 @@
#include <memory>
#include <functional>
#ifdef NEKTAR_USE_THREAD_SAFETY
#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/locks.hpp>
#endif
#include <LibUtilities/BasicUtils/ErrorUtil.hpp>
namespace Nektar
......@@ -48,6 +53,11 @@ namespace Nektar
{
using namespace std;
#ifdef NEKTAR_USE_THREAD_SAFETY
typedef boost::unique_lock<boost::shared_mutex> WriteLock;
typedef boost::shared_lock<boost::shared_mutex> ReadLock;
#endif
template <typename KeyType>
struct defOpLessCreator
{
......@@ -110,6 +120,9 @@ namespace Nektar
{
if (!whichPool.empty())
{
#ifdef NEKTAR_USE_THREAD_SAFETY
ReadLock v_rlock(m_mutex); // reading static members
#endif
auto iter = m_ValueContainerPool.find(whichPool);
if (iter != m_ValueContainerPool.end())
{
......@@ -118,6 +131,17 @@ namespace Nektar
}
else
{
#ifdef NEKTAR_USE_THREAD_SAFETY
v_rlock.unlock();
// now writing static members. Apparently
// upgrade_lock has less desirable properties than
// just dropping read lock, grabbing write lock.
// write will block until all reads are done, but
// reads cannot be acquired if write lock is
// blocking. In this context writes are supposed to
// be rare.
WriteLock v_wlock(m_mutex);
#endif
m_values = ValueContainerShPtr(new ValueContainer);
m_ValueContainerPool[whichPool] = m_values;
if (m_managementEnabledContainerPool.find(whichPool) == m_managementEnabledContainerPool.end())
......@@ -222,6 +246,9 @@ namespace Nektar
{
if (!whichPool.empty())
{
#ifdef NEKTAR_USE_THREAD_SAFETY
WriteLock v_wlock(m_mutex);
#endif
auto x = m_ValueContainerPool.find(whichPool);
ASSERTL1(x != m_ValueContainerPool.end(),
"Could not find pool " + whichPool);
......@@ -229,6 +256,10 @@ namespace Nektar
}
else
{
#ifdef NEKTAR_USE_THREAD_SAFETY
WriteLock v_wlock(m_mutex);
#endif
for (auto &x : m_ValueContainerPool)
{
x.second->clear();
......@@ -251,6 +282,10 @@ namespace Nektar
{
if (!whichPool.empty())
{
#ifdef NEKTAR_USE_THREAD_SAFETY
WriteLock v_wlock(m_mutex);
#endif
auto x = m_managementEnabledContainerPool.find(whichPool);
if (x != m_managementEnabledContainerPool.end())
{
......@@ -267,6 +302,9 @@ namespace Nektar
{
if (!whichPool.empty())
{
#ifdef NEKTAR_USE_THREAD_SAFETY
WriteLock v_wlock(m_mutex);
#endif
auto x = m_managementEnabledContainerPool.find(whichPool);
if (x != m_managementEnabledContainerPool.end())
{
......@@ -289,9 +327,16 @@ namespace Nektar
static FlagContainerPool m_managementEnabledContainerPool;
CreateFuncType m_globalCreateFunc;
CreateFuncContainer m_keySpecificCreateFuncs;
#ifdef NEKTAR_USE_THREAD_SAFETY
static boost::shared_mutex m_mutex;
#endif
};
template <typename KeyType, typename ValueT, typename opLessCreator> typename NekManager<KeyType, ValueT, opLessCreator>::ValueContainerPool NekManager<KeyType, ValueT, opLessCreator>::m_ValueContainerPool;
template <typename KeyType, typename ValueT, typename opLessCreator> typename NekManager<KeyType, ValueT, opLessCreator>::FlagContainerPool NekManager<KeyType, ValueT, opLessCreator>::m_managementEnabledContainerPool;
#ifdef NEKTAR_USE_THREAD_SAFETY
template <typename KeyType, typename ValueT, typename opLessCreator>
typename boost::shared_mutex NekManager<KeyType, ValueT, opLessCreator>::m_mutex;
#endif
}
}
......
......@@ -131,12 +131,20 @@ namespace Vmath
#undef EPS
#undef RNMX
#ifdef NEKTAR_USE_THREAD_SAFETY
static boost::mutex mutex;
#endif
template LIB_UTILITIES_EXPORT Nektar::NekDouble ran2 (long* idum);
/// \brief Fills a vector with white noise.
template<class T> void FillWhiteNoise( int n, const T eps, T *x,
const int incx, int outseed)
{
#ifdef NEKTAR_USE_THREAD_SAFETY
// Protect the static vars here and in ran2
boost::mutex::scoped_lock l(mutex);
#endif
// Define static variables for generating random numbers
static int iset = 0;
static T gset;
......
......@@ -44,6 +44,10 @@
#include <LibUtilities/BasicUtils/ErrorUtil.hpp>
#include <LibUtilities/LibUtilitiesDeclspec.h>
#ifdef NEKTAR_USE_THREAD_SAFETY
#include <boost/thread/mutex.hpp>
#endif
#include <cstring>
namespace Nektar
......@@ -80,8 +84,9 @@ namespace Nektar
m_pool(),
m_blockSize(ByteSize)
{
// We can do the new in the constructor list because the thread specific
// pointer doesn't have a supporting constructor.
// We can't do the new in the constructor list because the
// thread specific pointer doesn't have a supporting
// constructor.
m_pool = new boost::pool<>(m_blockSize);
}
......@@ -96,6 +101,9 @@ namespace Nektar
/// \throw std::bad_alloc if memory is exhausted.
void* Allocate()
{
#ifdef NEKTAR_USE_THREAD_SAFETY
boost::mutex::scoped_lock l(m_mutex);
#endif
void* result = m_pool->malloc();
#if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
......@@ -111,6 +119,9 @@ namespace Nektar
/// from this pool. Doing this will result in undefined behavior.
void Deallocate(const void* p)
{
#ifdef NEKTAR_USE_THREAD_SAFETY
boost::mutex::scoped_lock l(m_mutex);
#endif
#if defined(NEKTAR_DEBUG) || defined(NEKTAR_FULLDEBUG)
// The idea here is to fill the returned memory with some known
// pattern, then detect that pattern on the allocate. If the
......@@ -129,6 +140,9 @@ namespace Nektar
//boost::thread_specific_ptr<boost::pool<> > m_pool;
boost::pool<>* m_pool;
size_t m_blockSize;
#ifdef NEKTAR_USE_THREAD_SAFETY
boost::mutex m_mutex;
#endif
};
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment