/**
* @file aithreadsafe.h
* @brief Implementation of AIThreadSafe, AIReadAccessConst, AIReadAccess and AIWriteAccess.
*
* Copyright (c) 2010, Aleric Inglewood.
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see .
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution.
*
* CHANGELOG
* and additional copyright holders.
*
* 31/03/2010
* Initial version, written by Aleric Inglewood @ SL
*
* 14/03/2012
* Added AIThreadSafeSingleThread and friends.
* Added AIAccessConst (and derived AIAccess from it) to allow read
* access to a const AIThreadSafeSimple.
*/
// This file defines wrapper template classes for arbitrary types T
// adding locking to the instance and shielding it from access
// without first being locked.
//
// Locking and getting access works by creating a temporary (local)
// access object that takes the wrapper class as argument. Creating
// the access object obtains the lock, while destructing it releases
// the lock.
//
// There are three types of wrapper classes:
// AIThreadSafe, AIThreadSafeSimple and AIThreadSafeSingleThread.
//
// AIThreadSafe is for use with the access classes:
// AIReadAccessConst, AIReadAccess and AIWriteAccess.
//
// AIThreadSafeSimple is for use with the access classes:
// AIAccessConst and AIAccess.
//
// AIThreadSafeSingleThread is for use with the access classes:
// AISTAccessConst and AISTAccess.
//
// AIReadAccessConst provides read access to a const AIThreadSafe.
// AIReadAccess provides read access to a non-const AIThreadSafe.
// AIWriteAccess provides read/write access to a non-const AIThreadSafe.
//
// AIAccessConst provides read access to a const AIThreadSafeSimple.
// AIAccess provides read/write access to a non-const AIThreadSafeSimple.
//
// AISTAccessConst provides read access to a const AIThreadSafeSingleThread.
// AISTAccess provides read/write access to a non-const AIThreadSafeSingleThread.
//
// Thus, AIThreadSafe is to protect objects with a read/write lock,
// AIThreadSafeSimple is to protect objects with a single mutex,
// and AIThreadSafeSingleThread doesn't do any locking but makes sure
// (in Debug mode) that the wrapped object is only accessed by one thread.
//
// Each wrapper class allows its wrapped object to be constructed
// with arbitrary parameters by using operator new with placement;
// for example, to instantiate a class Foo with read/write locking:
//
// AIThreadSafe foo(new (foo.memory()) Foo(param1, param2, ...));
//
// Each wrapper class has a derived class that end on 'DC' (which
// stand for Default Constructed): AIThreadSafeDC, AIThreadSafeSimpleDC
// and AIThreadSafeSingleThreadDC. The default constructors of those
// wrapper classes cause the wrapped instance to be default constructed
// as well. They also provide a general one-parameter constructor.
// For example:
//
// AIThreadSafeDC foo; // Default constructed Foo.
// AIThreadSafeDC foo(3.4); // Foo with one constructor parameter.
//
#ifndef AITHREADSAFE_H
#define AITHREADSAFE_H
#include
#include "llthread.h"
#include "llerror.h"
// g++ 4.2.x (and before?) have the bug that when you try to pass a temporary
// to a function taking a const reference, it still calls the copy constructor.
// Define this to hack around that.
// Note that the chosen solution ONLY works for copying an AI*Access object that
// is passed to a function: the lifetime of the copied object must not be longer
// than the original (or at least, it shouldn't be used anymore after the
// original is destructed). This will be guaranteed if the code also compiles
// on a compiler that doesn't need this hack.
#define AI_NEED_ACCESS_CC (defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ < 3)) || (__GNUC__ < 4)))
template struct AIReadAccessConst;
template struct AIReadAccess;
template struct AIWriteAccess;
template struct AIAccessConst;
template struct AIAccess;
template struct AISTAccessConst;
template struct AISTAccess;
template
class AIThreadSafeBits
{
private:
// AIThreadSafe is a wrapper around an instance of T.
// Because T might not have a default constructor, it is constructed
// 'in place', with placement new, in the memory reserved here.
//
// Make sure that the memory that T will be placed in is properly
// aligned by using an array of long's.
long mMemory[(sizeof(T) + sizeof(long) - 1) / sizeof(long)];
public:
// The wrapped objects are constructed in-place with placement new *outside*
// of this object (by AITHREADSAFE macro(s) or derived classes).
// However, we are responsible for the destruction of the wrapped object.
~AIThreadSafeBits() { ptr()->~T(); }
// Only for use by AITHREADSAFE, see below.
void* memory() const { return const_cast(&mMemory[0]); }
// Cast a T* back to AIThreadSafeBits. This is the inverse of memory().
template
static AIThreadSafeBits* wrapper_cast(T2* ptr)
{ return reinterpret_cast*>(reinterpret_cast(ptr) - offsetof(AIThreadSafeBits, mMemory[0])); }
template
static AIThreadSafeBits const* wrapper_cast(T2 const* ptr)
{ return reinterpret_cast const*>(reinterpret_cast(ptr) - offsetof(AIThreadSafeBits, mMemory[0])); }
protected:
// Accessors.
T const* ptr() const { return reinterpret_cast(mMemory); }
T* ptr() { return reinterpret_cast(mMemory); }
};
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread, allowing concurrent readers.
*
* Use AITHREADSAFE to define instances of any type, and use AIReadAccessConst,
* AIReadAccess and AIWriteAccess to get access to the instance.
*
* For example,
*
*
* class Foo { public: Foo(int, int); };
*
* AITHREADSAFE(Foo, foo, (2, 3));
*
* AIReadAccess foo_r(foo);
* // Use foo_r-> for read access.
*
* AIWriteAccess foo_w(foo);
* // Use foo_w-> for write access.
*
*
* If foo is constant, you have to use AIReadAccessConst.
*
* It is possible to pass access objects to a function that
* downgrades the access, for example:
*
*
* void readfunc(AIReadAccess const& access);
*
* AIWriteAccess foo_w(foo);
* readfunc(foo_w); // readfunc will perform read access to foo_w.
*
*
* If AIReadAccess is non-const, you can upgrade the access by creating
* an AIWriteAccess object from it. For example:
*
*
* AIWriteAccess foo_w(foo_r);
*
*
* This API is Robust(tm). If you try anything that could result in problems,
* it simply won't compile. The only mistake you can still easily make is
* to obtain write access to an object when it is not needed, or to unlock
* an object in between accesses while the state of the object should be
* preserved. For example:
*
*
* // This resets foo to point to the first file and then returns that.
* std::string filename = AIWriteAccess(foo)->get_first_filename();
*
* // WRONG! The state between calling get_first_filename and get_next_filename should be preserved!
*
* AIWriteAccess foo_w(foo); // Wrong. The code below only needs read-access.
* while (!filename.empty())
* {
* something(filename);
* filename = foo_w->next_filename();
* }
*
*
* Correct would be
*
*
* AIReadAccess foo_r(foo);
* std::string filename = AIWriteAccess(foo_r)->get_first_filename();
* while (!filename.empty())
* {
* something(filename);
* filename = foo_r->next_filename();
* }
*
*
*/
template
class AIThreadSafe : public AIThreadSafeBits
{
protected:
// Only these may access the object (through ptr()).
friend struct AIReadAccessConst;
friend struct AIReadAccess;
friend struct AIWriteAccess;
// Locking control.
AIRWLock mRWLock;
// For use by AIThreadSafeDC
AIThreadSafe(void) { }
AIThreadSafe(LLAPRPool& parent) : mRWLock(parent) { }
public:
// Only for use by AITHREADSAFE, see below.
AIThreadSafe(T* object)
{
#if !LL_WINDOWS
llassert(object == AIThreadSafeBits::ptr());
#endif
}
#if LL_DEBUG
// Can only be locked when there still exists an AIAccess object that
// references this object and will access it upon destruction.
// If the assertion fails, make sure that such AIAccess object is
// destructed before the deletion of this object.
~AIThreadSafe() { llassert(!mRWLock.isLocked()); }
#endif
};
/**
* @brief Instantiate an static, global or local object of a given type wrapped in AIThreadSafe, using an arbitrary constructor.
*
* For example, instead of doing
*
*
* Foo foo(x, y);
* static Bar bar(0.1, true);
*
*
* One can instantiate a thread-safe instance with
*
*
* AITHREADSAFE(Foo, foo, (x, y));
* static AITHREADSAFE(Bar, bar, (0.1, true));
*
*
* Note: This macro does not allow to allocate such object on the heap.
* If that is needed, have a look at AIThreadSafeDC.
*/
#define AITHREADSAFE(type, var, paramlist) AIThreadSafe var(new (var.memory()) type paramlist)
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread.
*
* This class is the same as an AIThreadSafe wrapper, except that it can only
* be used for default constructed objects, or constructed with one parameter.
*
* For example, instead of
*
*
* Foo foo;
* Bar bar(3);
*
*
* One would use
*
*
* AIThreadSafeDC foo;
* AIThreadSafeDC bar(3);
*
*
* The advantage over AITHREADSAFE is that this object can be allocated with
* new on the heap. For example:
*
*
* AIThreadSafeDC* ptr = new AIThreadSafeDC;
*
*
* which is not possible with AITHREADSAFE.
*/
template
class AIThreadSafeDC : public AIThreadSafe
{
public:
// Construct a wrapper around a default constructed object.
AIThreadSafeDC(void) { new (AIThreadSafe::ptr()) T; }
// Allow an arbitrary parameter to be passed for construction.
template AIThreadSafeDC(T2 const& val) { new (AIThreadSafe::ptr()) T(val); }
};
/**
* @brief Read lock object and provide read access.
*/
template
struct AIReadAccessConst
{
//! Internal enum for the lock-type of the AI*Access object.
enum state_type
{
readlocked, //!< A AIReadAccessConst or AIReadAccess.
read2writelocked, //!< A AIWriteAccess constructed from a AIReadAccess.
writelocked, //!< A AIWriteAccess constructed from a AIThreadSafe.
write2writelocked //!< A AIWriteAccess constructed from (the AIReadAccess base class of) a AIWriteAccess.
};
//! Construct a AIReadAccessConst from a constant AIThreadSafe.
AIReadAccessConst(AIThreadSafe const& wrapper)
: mWrapper(const_cast&>(wrapper)),
mState(readlocked)
#if AI_NEED_ACCESS_CC
, mIsCopyConstructed(false)
#endif
{
mWrapper.mRWLock.rdlock();
}
//! Destruct the AI*Access object.
// These should never be dynamically allocated, so there is no need to make this virtual.
~AIReadAccessConst()
{
#if AI_NEED_ACCESS_CC
if (mIsCopyConstructed) return;
#endif
if (mState == readlocked)
mWrapper.mRWLock.rdunlock();
else if (mState == writelocked)
mWrapper.mRWLock.wrunlock();
else if (mState == read2writelocked)
mWrapper.mRWLock.wr2rdlock();
}
//! Access the underlaying object for read access.
T const* operator->() const { return mWrapper.ptr(); }
//! Access the underlaying object for read access.
T const& operator*() const { return *mWrapper.ptr(); }
protected:
//! Constructor used by AIReadAccess.
AIReadAccessConst(AIThreadSafe& wrapper, state_type state)
: mWrapper(wrapper), mState(state)
#if AI_NEED_ACCESS_CC
, mIsCopyConstructed(false)
#endif
{ }
AIThreadSafe& mWrapper; //!< Reference to the object that we provide access to.
state_type const mState; //!< The lock state that mWrapper is in.
#if AI_NEED_ACCESS_CC
bool mIsCopyConstructed;
public:
AIReadAccessConst(AIReadAccessConst const& orig) : mWrapper(orig.mWrapper), mState(orig.mState), mIsCopyConstructed(true) { }
#else
private:
// Disallow copy constructing directly.
AIReadAccessConst(AIReadAccessConst const&);
#endif
};
/**
* @brief Read lock object and provide read access, with possible promotion to write access.
*/
template
struct AIReadAccess : public AIReadAccessConst
{
typedef typename AIReadAccessConst::state_type state_type;
using AIReadAccessConst::readlocked;
//! Construct a AIReadAccess from a non-constant AIThreadSafe.
AIReadAccess(AIThreadSafe& wrapper) : AIReadAccessConst(wrapper, readlocked) { this->mWrapper.mRWLock.rdlock(); }
protected:
//! Constructor used by AIWriteAccess.
AIReadAccess(AIThreadSafe& wrapper, state_type state) : AIReadAccessConst(wrapper, state) { }
friend struct AIWriteAccess;
};
/**
* @brief Write lock object and provide read/write access.
*/
template
struct AIWriteAccess : public AIReadAccess
{
using AIReadAccessConst::readlocked;
using AIReadAccessConst::read2writelocked;
using AIReadAccessConst::writelocked;
using AIReadAccessConst::write2writelocked;
//! Construct a AIWriteAccess from a non-constant AIThreadSafe.
AIWriteAccess(AIThreadSafe& wrapper) : AIReadAccess(wrapper, writelocked) { this->mWrapper.mRWLock.wrlock();}
//! Promote read access to write access.
explicit AIWriteAccess(AIReadAccess& access)
: AIReadAccess(access.mWrapper, (access.mState == readlocked) ? read2writelocked : write2writelocked)
{
if (this->mState == read2writelocked)
{
this->mWrapper.mRWLock.rd2wrlock();
}
}
//! Access the underlaying object for (read and) write access.
T* operator->() const { return this->mWrapper.ptr(); }
//! Access the underlaying object for (read and) write access.
T& operator*() const { return *this->mWrapper.ptr(); }
};
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread.
*
* Use AITHREADSAFESIMPLE to define instances of any type, and use AIAccess
* to get access to the instance.
*
* For example,
*
*
* class Foo { public: Foo(int, int); };
*
* AITHREADSAFESIMPLE(Foo, foo, (2, 3));
*
* AIAccess foo_w(foo);
* // Use foo_w-> for read and write access.
*
*
* See also AIThreadSafe
*/
template
class AIThreadSafeSimple : public AIThreadSafeBits
{
protected:
// Only this one may access the object (through ptr()).
friend struct AIAccessConst;
friend struct AIAccess;
// Locking control.
LLMutex mMutex;
friend struct AIRegisteredStateMachinesList;
// For use by AIThreadSafeSimpleDC and AIRegisteredStateMachinesList.
AIThreadSafeSimple(void) { }
AIThreadSafeSimple(LLAPRPool& parent) : mMutex(parent) { }
public:
// Only for use by AITHREADSAFESIMPLE, see below.
AIThreadSafeSimple(T* object) { llassert(object == AIThreadSafeBits::ptr()); }
#if LL_DEBUG
// Can only be locked when there still exists an AIAccess object that
// references this object and will access it upon destruction.
// If the assertion fails, make sure that such AIAccess object is
// destructed before the deletion of this object.
~AIThreadSafeSimple() { llassert(!mMutex.isLocked()); }
#endif
};
/**
* @brief Instantiate an static, global or local object of a given type wrapped in AIThreadSafeSimple, using an arbitrary constructor.
*
* For example, instead of doing
*
*
* Foo foo(x, y);
* static Bar bar(0.1, true);
*
*
* One can instantiate a thread-safe instance with
*
*
* AITHREADSAFESIMPLE(Foo, foo, (x, y));
* static AITHREADSAFESIMPLE(Bar, bar, (0.1, true));
*
*
* Note: This macro does not allow to allocate such object on the heap.
* If that is needed, have a look at AIThreadSafeSimpleDC.
*/
#define AITHREADSAFESIMPLE(type, var, paramlist) AIThreadSafeSimple var(new (var.memory()) type paramlist)
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread.
*
* This class is the same as an AIThreadSafeSimple wrapper, except that it can only
* be used for default constructed objects, or constructed with one parameter.
*
* For example, instead of
*
*
* Foo foo;
* Bar bar(0.1);
*
*
* One would use
*
*
* AIThreadSafeSimpleDC foo;
* AIThreadSafeSimpleDC bar(0.1);
*
*
* The advantage over AITHREADSAFESIMPLE is that this object can be allocated with
* new on the heap. For example:
*
*
* AIThreadSafeSimpleDC* ptr = new AIThreadSafeSimpleDC;
*
*
* which is not possible with AITHREADSAFESIMPLE.
*/
template
class AIThreadSafeSimpleDC : public AIThreadSafeSimple
{
public:
// Construct a wrapper around a default constructed object.
AIThreadSafeSimpleDC(void) { new (AIThreadSafeSimple::ptr()) T; }
// Allow an arbitrary parameter to be passed for construction.
template AIThreadSafeSimpleDC(T2 const& val) { new (AIThreadSafeSimple::ptr()) T(val); }
protected:
// For use by AIThreadSafeSimpleDCRootPool
AIThreadSafeSimpleDC(LLAPRRootPool& parent) : AIThreadSafeSimple(parent) { new (AIThreadSafeSimple::ptr()) T; }
};
// Helper class for AIThreadSafeSimpleDCRootPool to assure initialization of
// the root pool before constructing AIThreadSafeSimpleDC.
class AIThreadSafeSimpleDCRootPool_pbase
{
protected:
LLAPRRootPool mRootPool;
private:
template friend class AIThreadSafeSimpleDCRootPool;
AIThreadSafeSimpleDCRootPool_pbase(void) { }
};
/**
* @brief A wrapper class for objects that need to be accessed by more than one thread.
*
* The same as AIThreadSafeSimpleDC except that this class creates its own LLAPRRootPool
* for the internally used mutexes and condition, instead of using the current threads
* root pool. The advantage of this is that it can be used for objects that need to
* be accessed from the destructors of global objects (after main). The disadvantage
* is that it's less efficient to use your own root pool, therefore its use should be
* restricted to those cases where it is absolutely necessary.
*/
template
class AIThreadSafeSimpleDCRootPool : private AIThreadSafeSimpleDCRootPool_pbase, public AIThreadSafeSimpleDC
{
public:
// Construct a wrapper around a default constructed object, using memory allocated
// from the operating system for the internal APR objects (mutexes and conditional),
// as opposed to allocated from the current threads root pool.
AIThreadSafeSimpleDCRootPool(void) :
AIThreadSafeSimpleDCRootPool_pbase(),
AIThreadSafeSimpleDC(mRootPool) { }
};
/**
* @brief Write lock object and provide read access.
*/
template
struct AIAccessConst
{
//! Construct a AIAccessConst from a constant AIThreadSafeSimple.
AIAccessConst(AIThreadSafeSimple const& wrapper) : mWrapper(const_cast&>(wrapper))
#if AI_NEED_ACCESS_CC
, mIsCopyConstructed(false)
#endif
{
this->mWrapper.mMutex.lock();
}
//! Access the underlaying object for (read and) write access.
T const* operator->() const { return this->mWrapper.ptr(); }
//! Access the underlaying object for (read and) write access.
T const& operator*() const { return *this->mWrapper.ptr(); }
~AIAccessConst()
{
#if AI_NEED_ACCESS_CC
if (mIsCopyConstructed) return;
#endif
this->mWrapper.mMutex.unlock();
}
protected:
AIThreadSafeSimple& mWrapper; //!< Reference to the object that we provide access to.
#if AI_NEED_ACCESS_CC
bool mIsCopyConstructed;
public:
AIAccessConst(AIAccessConst const& orig) : mWrapper(orig.mWrapper), mIsCopyConstructed(true) { }
#else
private:
// Disallow copy constructing directly.
AIAccessConst(AIAccessConst const&);
#endif
};
/**
* @brief Write lock object and provide read/write access.
*/
template
struct AIAccess : public AIAccessConst
{
//! Construct a AIAccess from a non-constant AIThreadSafeSimple.
AIAccess(AIThreadSafeSimple& wrapper) : AIAccessConst(wrapper) { }
//! Access the underlaying object for (read and) write access.
T* operator->() const { return this->mWrapper.ptr(); }
//! Access the underlaying object for (read and) write access.
T& operator*() const { return *this->mWrapper.ptr(); }
};
/**
* @brief A wrapper class for objects that should only be accessed by a single thread.
*
* Use AITHREADSAFESINGLETHREAD to define instances of any type, and use AISTAccess
* to get access to the instance.
*
* For example,
*
*
* class Foo { public: Foo(int, int); };
*
* AITHREADSAFESINGLETHREAD(Foo, foo, (2, 3));
*
* AISTAccess foo_w(foo);
* // Use foo_w-> for read and write access.
*
*/
template
class AIThreadSafeSingleThread : public AIThreadSafeBits
{
protected:
// Only these one may access the object (through ptr()).
friend struct AISTAccessConst;
friend struct AISTAccess;
// For use by AIThreadSafeSingleThreadDC.
AIThreadSafeSingleThread(void)
#ifdef LL_DEBUG
: mAccessed(false)
#endif
{ }
#ifdef LL_DEBUG
mutable bool mAccessed;
mutable AIThreadID mTheadID;
void accessed(void) const
{
if (!mAccessed)
{
mAccessed = true;
mTheadID.reset();
}
else
{
llassert_always(mTheadID.equals_current_thread());
}
}
#endif
public:
// Only for use by AITHREADSAFESINGLETHREAD, see below.
AIThreadSafeSingleThread(T* object)
#ifdef LL_DEBUG
: mAccessed(false)
#endif
{
llassert(object == AIThreadSafeBits::ptr());
}
private:
// Disallow copying or assignments.
AIThreadSafeSingleThread(AIThreadSafeSingleThread const&);
void operator=(AIThreadSafeSingleThread const&);
};
/**
* @brief A wrapper class for objects that should only be accessed by a single thread.
*
* This class is the same as an AIThreadSafeSingleThread wrapper, except that it can only
* be used for default constructed objects, or constructed with one parameter.
*
* For example, instead of
*
*
* Foo foo;
* Bar bar(0.1);
*
*
* One would use
*
*
* AIThreadSafeSingleThreadDC foo;
* AIThreadSafeSingleThreadDC bar(0.1);
*
*
* The advantage over AITHREADSAFESINGLETHREAD is that this object can be allocated with
* new on the heap. For example:
*
*
* AIThreadSafeSingleThreadDC* ptr = new AIThreadSafeSingleThreadDC;
*
*
* which is not possible with AITHREADSAFESINGLETHREAD.
*
* This class is primarily intended to test if some (member) variable needs locking,
* during development (in debug mode), and is therefore more flexible in that it
* automatically converts to the underlying type, can be assigned to and can be
* written to an ostream, as if it wasn't wrapped at all. This is to reduce the
* impact on the source code.
*/
template
class AIThreadSafeSingleThreadDC : public AIThreadSafeSingleThread
{
public:
// Construct a wrapper around a default constructed object.
AIThreadSafeSingleThreadDC(void) { new (AIThreadSafeSingleThread::ptr()) T; }
// Allow an arbitrary parameter to be passed for construction.
template AIThreadSafeSingleThreadDC(T2 const& val) { new (AIThreadSafeSingleThread::ptr()) T(val); }
// Allow assigning with T.
AIThreadSafeSingleThreadDC& operator=(T const& val) { AIThreadSafeSingleThread::accessed(); *AIThreadSafeSingleThread::ptr() = val; return *this; }
// Allow writing to an ostream.
friend std::ostream& operator<<(std::ostream& os, AIThreadSafeSingleThreadDC const& wrapped_val) { wrapped_val.accessed(); return os << *wrapped_val.ptr(); }
// Automatic conversion to T.
operator T&(void) { AIThreadSafeSingleThread::accessed(); return *AIThreadSafeSingleThread::ptr(); }
operator T const&(void) const { AIThreadSafeSingleThread::accessed(); return *AIThreadSafeSingleThread::ptr(); }
};
/**
* @brief Instantiate a static, global or local object of a given type wrapped in AIThreadSafeSingleThread, using an arbitrary constructor.
*
* For example, instead of doing
*
*
* Foo foo(x, y);
* static Bar bar;
*
*
* One can instantiate a thread-safe instance with
*
*
* AITHREADSAFESINGLETHREAD(Foo, foo, (x, y));
* static AITHREADSAFESINGLETHREAD(Bar, bar, );
*
*
* Note: This macro does not allow to allocate such object on the heap.
* If that is needed, have a look at AIThreadSafeSingleThreadDC.
*/
#define AITHREADSAFESINGLETHREAD(type, var, paramlist) AIThreadSafeSingleThread var(new (var.memory()) type paramlist)
/**
* @brief Access single threaded object for read access.
*/
template
struct AISTAccessConst
{
//! Construct a AISTAccessConst from a constant AIThreadSafeSingleThread.
AISTAccessConst(AIThreadSafeSingleThread const& wrapper) : mWrapper(const_cast&>(wrapper))
{
#if LL_DEBUG
wrapper.accessed();
#endif
}
//! Access the underlaying object for read access.
T const* operator->() const { return this->mWrapper.ptr(); }
//! Access the underlaying object for read write access.
T const& operator*() const { return *this->mWrapper.ptr(); }
protected:
AIThreadSafeSingleThread& mWrapper; //!< Reference to the object that we provide access to.
#if AI_NEED_ACCESS_CC
public:
AISTAccessConst(AISTAccessConst const& orig) : mWrapper(orig.mWrapper) { }
#else
private:
// Disallow copy constructing directly.
AISTAccessConst(AISTAccessConst const&);
#endif
};
/**
* @brief Access single threaded object for read/write access.
*/
template
struct AISTAccess : public AISTAccessConst
{
//! Construct a AISTAccess from a non-constant AIThreadSafeSingleThread.
AISTAccess(AIThreadSafeSingleThread& wrapper) : AISTAccessConst(wrapper)
{
#if LL_DEBUG
wrapper.accessed();
#endif
}
//! Access the underlaying object for (read and) write access.
T* operator->() const { return this->mWrapper.ptr(); }
//! Access the underlaying object for (read and) write access.
T& operator*() const { return *this->mWrapper.ptr(); }
};
#endif