AIThreadSafe* update.

Updated/added documentation.
Removed AIThreadSafeWindowsHack that annoyed me (fix your compiler).
Don't use 'static' in anonymous namespace.
Use the AIThreadSafe*DC variants for default constructed objects,
as opposed to the AITHREADSAFE* macro's.
This commit is contained in:
Aleric Inglewood
2012-03-16 00:10:07 +01:00
parent d63c54cb78
commit 4391614d6e
9 changed files with 104 additions and 39 deletions

View File

@@ -26,8 +26,65 @@
* *
* 31/03/2010 * 31/03/2010
* Initial version, written by Aleric Inglewood @ SL * 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 it's 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> 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> foo; // Default constructed Foo.
// AIThreadSafeDC<Foo> foo(3.4); // Foo with one constructor parameter.
//
#ifndef AITHREADSAFE_H #ifndef AITHREADSAFE_H
#define AITHREADSAFE_H #define AITHREADSAFE_H
@@ -54,14 +111,6 @@ template<typename T> struct AIAccess;
template<typename T> struct AISTAccessConst; template<typename T> struct AISTAccessConst;
template<typename T> struct AISTAccess; template<typename T> struct AISTAccess;
#if LL_WINDOWS
template<typename T> class AIThreadSafeBits;
template<typename T>
struct AIThreadSafeWindowsHack {
AIThreadSafeWindowsHack(AIThreadSafeBits<T>& var, T* object);
};
#endif
template<typename T> template<typename T>
class AIThreadSafeBits class AIThreadSafeBits
{ {
@@ -84,9 +133,6 @@ public:
void* memory() const { return const_cast<long*>(&mMemory[0]); } void* memory() const { return const_cast<long*>(&mMemory[0]); }
protected: protected:
#if LL_WINDOWS
template<typename T2> friend struct AIThreadSafeWindowsHack;
#endif
// Accessors. // Accessors.
T const* ptr() const { return reinterpret_cast<T const*>(mMemory); } T const* ptr() const { return reinterpret_cast<T const*>(mMemory); }
T* ptr() { return reinterpret_cast<T*>(mMemory); } T* ptr() { return reinterpret_cast<T*>(mMemory); }
@@ -188,6 +234,14 @@ public:
llassert(object == AIThreadSafeBits<T>::ptr()); llassert(object == AIThreadSafeBits<T>::ptr());
#endif #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
}; };
/** /**
@@ -197,48 +251,39 @@ public:
* *
* <code> * <code>
* Foo foo(x, y); * Foo foo(x, y);
* static Bar bar; * static Bar bar(0.1, true);
* </code> * </code>
* *
* One can instantiate a thread-safe instance with * One can instantiate a thread-safe instance with
* *
* <code> * <code>
* AITHREADSAFE(Foo, foo, (x, y)); * AITHREADSAFE(Foo, foo, (x, y));
* static AITHREADSAFE(Bar, bar, ); * static AITHREADSAFE(Bar, bar, (0.1, true));
* </code> * </code>
* *
* Note: This macro does not allow to allocate such object on the heap. * Note: This macro does not allow to allocate such object on the heap.
* If that is needed, have a look at AIThreadSafeDC. * If that is needed, have a look at AIThreadSafeDC.
*/ */
#if LL_WINDOWS
template<typename T>
AIThreadSafeWindowsHack<T>::AIThreadSafeWindowsHack(AIThreadSafeBits<T>& var, T* object)
{
llassert(object == var.ptr());
}
#define AITHREADSAFE(type, var, paramlist) \
AIThreadSafe<type> var(NULL); \
AIThreadSafeWindowsHack<type> dummy_##var(var, new (var.memory()) type paramlist)
#else
#define AITHREADSAFE(type, var, paramlist) AIThreadSafe<type> var(new (var.memory()) type paramlist) #define AITHREADSAFE(type, var, paramlist) AIThreadSafe<type> var(new (var.memory()) type paramlist)
#endif
/** /**
* @brief A wrapper class for objects that need to be accessed by more than one thread. * @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 * This class is the same as an AIThreadSafe wrapper, except that it can only
* be used for default constructed objects. * be used for default constructed objects, or constructed with one parameter.
* *
* For example, instead of * For example, instead of
* *
* <code> * <code>
* Foo foo; * Foo foo;
* Bar bar(3);
* </code> * </code>
* *
* One would use * One would use
* *
* <code> * <code>
* AIThreadSafeDC<Foo> foo; * AIThreadSafeDC<Foo> foo;
* AIThreadSafeDC<Bar> bar(3);
* </code> * </code>
* *
* The advantage over AITHREADSAFE is that this object can be allocated with * The advantage over AITHREADSAFE is that this object can be allocated with
@@ -395,6 +440,7 @@ struct AIWriteAccess : public AIReadAccess<T>
* *
* AIAccess<Foo> foo_w(foo); * AIAccess<Foo> foo_w(foo);
* // Use foo_w-> for read and write access. * // Use foo_w-> for read and write access.
* </code>
* *
* See also AIThreadSafe * See also AIThreadSafe
*/ */
@@ -417,6 +463,14 @@ protected:
public: public:
// Only for use by AITHREADSAFESIMPLE, see below. // Only for use by AITHREADSAFESIMPLE, see below.
AIThreadSafeSimple(T* object) { llassert(object == AIThreadSafeBits<T>::ptr()); } AIThreadSafeSimple(T* object) { llassert(object == AIThreadSafeBits<T>::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
}; };
/** /**
@@ -426,14 +480,14 @@ public:
* *
* <code> * <code>
* Foo foo(x, y); * Foo foo(x, y);
* static Bar bar; * static Bar bar(0.1, true);
* </code> * </code>
* *
* One can instantiate a thread-safe instance with * One can instantiate a thread-safe instance with
* *
* <code> * <code>
* AITHREADSAFESIMPLE(Foo, foo, (x, y)); * AITHREADSAFESIMPLE(Foo, foo, (x, y));
* static AITHREADSAFESIMPLE(Bar, bar, ); * static AITHREADSAFESIMPLE(Bar, bar, (0.1, true));
* </code> * </code>
* *
* Note: This macro does not allow to allocate such object on the heap. * Note: This macro does not allow to allocate such object on the heap.
@@ -445,18 +499,20 @@ public:
* @brief A wrapper class for objects that need to be accessed by more than one thread. * @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 * This class is the same as an AIThreadSafeSimple wrapper, except that it can only
* be used for default constructed objects. * be used for default constructed objects, or constructed with one parameter.
* *
* For example, instead of * For example, instead of
* *
* <code> * <code>
* Foo foo; * Foo foo;
* Bar bar(0.1);
* </code> * </code>
* *
* One would use * One would use
* *
* <code> * <code>
* AIThreadSafeSimpleDC<Foo> foo; * AIThreadSafeSimpleDC<Foo> foo;
* AIThreadSafeSimpleDC<Bar> bar(0.1);
* </code> * </code>
* *
* The advantage over AITHREADSAFESIMPLE is that this object can be allocated with * The advantage over AITHREADSAFESIMPLE is that this object can be allocated with
@@ -590,6 +646,7 @@ struct AIAccess : public AIAccessConst<T>
* *
* AISTAccess<Foo> foo_w(foo); * AISTAccess<Foo> foo_w(foo);
* // Use foo_w-> for read and write access. * // Use foo_w-> for read and write access.
* </code>
*/ */
template<typename T> template<typename T>
class AIThreadSafeSingleThread : public AIThreadSafeBits<T> class AIThreadSafeSingleThread : public AIThreadSafeBits<T>
@@ -639,18 +696,20 @@ public:
* @brief A wrapper class for objects that should only be accessed by a single thread. * @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 * This class is the same as an AIThreadSafeSingleThread wrapper, except that it can only
* be used for default constructed objects. * be used for default constructed objects, or constructed with one parameter.
* *
* For example, instead of * For example, instead of
* *
* <code> * <code>
* Foo foo; * Foo foo;
* Bar bar(0.1);
* </code> * </code>
* *
* One would use * One would use
* *
* <code> * <code>
* AIThreadSafeSingleThreadDC<Foo> foo; * AIThreadSafeSingleThreadDC<Foo> foo;
* AIThreadSafeSingleThreadDC<Bar> bar(0.1);
* </code> * </code>
* *
* The advantage over AITHREADSAFESINGLETHREAD is that this object can be allocated with * The advantage over AITHREADSAFESINGLETHREAD is that this object can be allocated with
@@ -661,6 +720,12 @@ public:
* </code> * </code>
* *
* which is not possible with AITHREADSAFESINGLETHREAD. * 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 underlaying 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<typename T> template<typename T>
class AIThreadSafeSingleThreadDC : public AIThreadSafeSingleThread<T> class AIThreadSafeSingleThreadDC : public AIThreadSafeSingleThread<T>

View File

@@ -269,7 +269,7 @@ U8* LLImageBase::allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 si
// LLImageRaw // LLImageRaw
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
AITHREADSAFESIMPLE(S32, LLImageRaw::sGlobalRawMemory, ); AIThreadSafeSimpleDC<S32> LLImageRaw::sGlobalRawMemory;
S32 LLImageRaw::sRawImageCount = 0; S32 LLImageRaw::sRawImageCount = 0;
S32 LLImageRaw::sRawImageCachedCount = 0; S32 LLImageRaw::sRawImageCachedCount = 0;

View File

@@ -248,7 +248,7 @@ protected:
void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ; void setDataAndSize(U8 *data, S32 width, S32 height, S8 components) ;
public: public:
static AIThreadSafeSimple<S32> sGlobalRawMemory; static AIThreadSafeSimpleDC<S32> sGlobalRawMemory;
static S32 sRawImageCount; static S32 sRawImageCount;
static S32 sRawImageCachedCount; static S32 sRawImageCachedCount;

View File

@@ -87,7 +87,7 @@
BOOL gHackGodmode = FALSE; BOOL gHackGodmode = FALSE;
#endif #endif
AITHREADSAFE(settings_map_type, gSettings,); AIThreadSafeDC<settings_map_type> gSettings;
LLControlGroup gSavedSettings("Global"); // saved at end of session LLControlGroup gSavedSettings("Global"); // saved at end of session
LLControlGroup gSavedPerAccountSettings("PerAccount"); // saved at end of session LLControlGroup gSavedPerAccountSettings("PerAccount"); // saved at end of session
LLControlGroup gColors("Colors"); // saved at end of session LLControlGroup gColors("Colors"); // saved at end of session

View File

@@ -51,7 +51,7 @@ extern BOOL gHackGodmode;
void settings_setup_listeners(); void settings_setup_listeners();
typedef std::map<std::string, LLControlGroup*> settings_map_type; typedef std::map<std::string, LLControlGroup*> settings_map_type;
extern AIThreadSafe<settings_map_type> gSettings; extern AIThreadSafeDC<settings_map_type> gSettings;
// for the graphics settings // for the graphics settings
void create_graphics_group(LLControlGroup& group); void create_graphics_group(LLControlGroup& group);

View File

@@ -65,7 +65,7 @@ AIFilePicker::AIFilePicker(void) : mPluginManager(NULL), mAutoKill(false), mCanc
} }
// static // static
AITHREADSAFESIMPLE(AIFilePicker::context_map_type, AIFilePicker::sContextMap, ); AIThreadSafeSimpleDC<AIFilePicker::context_map_type> AIFilePicker::sContextMap;
// static // static
void AIFilePicker::store_folder(std::string const& context, std::string const& folder) void AIFilePicker::store_folder(std::string const& context, std::string const& folder)

View File

@@ -185,7 +185,7 @@ public:
private: private:
LLPointer<LLViewerPluginManager> mPluginManager; //!< Pointer to the plugin manager. LLPointer<LLViewerPluginManager> mPluginManager; //!< Pointer to the plugin manager.
typedef std::map<std::string, std::string> context_map_type; //!< Type of mContextMap. typedef std::map<std::string, std::string> context_map_type; //!< Type of mContextMap.
static AIThreadSafeSimple<context_map_type> sContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder. static AIThreadSafeSimpleDC<context_map_type> sContextMap; //!< Map context (ie, "snapshot" or "image") to last used folder.
std::string mContext; //!< Some key to indicate the context (remembers the folder per key). std::string mContext; //!< Some key to indicate the context (remembers the folder per key).
bool mAutoKill; //!< True if the default behavior is to delete itself after being finished. bool mAutoKill; //!< True if the default behavior is to delete itself after being finished.

View File

@@ -65,18 +65,18 @@ namespace {
}; };
typedef std::vector<QueueElement> active_statemachines_type; typedef std::vector<QueueElement> active_statemachines_type;
static active_statemachines_type active_statemachines; active_statemachines_type active_statemachines;
typedef std::vector<AIStateMachine*> continued_statemachines_type; typedef std::vector<AIStateMachine*> continued_statemachines_type;
struct cscm_type struct cscm_type
{ {
continued_statemachines_type continued_statemachines; continued_statemachines_type continued_statemachines;
bool calling_mainloop; bool calling_mainloop;
}; };
static AITHREADSAFE(cscm_type, continued_statemachines_and_calling_mainloop, ); AIThreadSafeDC<cscm_type> continued_statemachines_and_calling_mainloop;
} }
// static // static
AITHREADSAFESIMPLE(U64, AIStateMachine::sMaxCount, ); AIThreadSafeSimpleDC<U64> AIStateMachine::sMaxCount;
void AIStateMachine::updateSettings(void) void AIStateMachine::updateSettings(void)
{ {

View File

@@ -224,7 +224,7 @@ class AIStateMachine {
}; };
callback_type* mCallback; //!< Pointer to signal/connection, or NULL when not connected. callback_type* mCallback; //!< Pointer to signal/connection, or NULL when not connected.
static AIThreadSafeSimple<U64> sMaxCount; //!< Number of cpu clocks below which we start a new state machine within the same frame. static AIThreadSafeSimpleDC<U64> sMaxCount; //!< Number of cpu clocks below which we start a new state machine within the same frame.
protected: protected:
//! State of the derived class. Only valid if mState == bs_run. Call set_state to change. //! State of the derived class. Only valid if mState == bs_run. Call set_state to change.