-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #142 from oblivioncth/feature/tss
Add ThreadSafeSingleton. Easy singleton pattern with thread-safe guard.
- Loading branch information
Showing
6 changed files
with
154 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//! [0] | ||
class MySingleton : Qx::ThreadSafeSingleton<MySingleton> | ||
{ | ||
QX_THREAD_SAFE_SINGLETON(MySingleton); | ||
private: | ||
std::string mData; | ||
MySingleton() = default; // Generally should be private | ||
|
||
public: | ||
doStuffSafely() { mData = "I'm for sure set while not being read!"; } | ||
checkStuffSafely() { return mData; // Not being written to when returned } | ||
} | ||
|
||
//... | ||
|
||
void functionInArbitraryThread() | ||
{ | ||
auto singleton = MySingleton::instance(); | ||
// This function now has a exclusive access to MySingleton (i.e. a mutex lock is established) | ||
singleton->doStuffSafely(); | ||
|
||
// Unlocked when 'singleton' goes out of scope, or is manually unlocked. | ||
} | ||
|
||
void functionInAnotherThread() | ||
{ | ||
// Safely lock and read. It's guarenteed that no other thread is using MySingleton | ||
// after the instance is obtained. | ||
auto singleton = MySingleton::instance(); | ||
std::string info = singleton->checkStuffSafely(); | ||
//... | ||
} | ||
//! [0] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#ifndef QX_THREADSAFE_SINGLETON | ||
#define QX_THREADSAFE_SINGLETON | ||
|
||
// Intra-component Includes | ||
#include "qx/core/qx-exclusiveaccess.h" | ||
|
||
// Extra-component Includes | ||
#include "qx/utility/qx-concepts.h" | ||
|
||
class QMutex; | ||
class QRecursiveMutex; | ||
|
||
namespace Qx | ||
{ | ||
|
||
template<class Singleton, typename Mutex = QMutex> | ||
requires any_of<Mutex, QMutex, QRecursiveMutex> | ||
class ThreadSafeSingleton | ||
{ | ||
//-Class Members--------------------------------------------------------------------------------------------- | ||
private: | ||
// Needs to be static so it can be locked before the the singleton is created, or else a race in instance() could occur. | ||
static inline constinit Mutex smMutex; | ||
|
||
//-Constructor---------------------------------------------------------------------------------------------- | ||
protected: | ||
ThreadSafeSingleton() = default; | ||
|
||
//-Class Functions---------------------------------------------------------------------------------------------- | ||
public: | ||
static Qx::ExclusiveAccess<Singleton, QMutex> instance() | ||
{ | ||
static Singleton s; | ||
return Qx::ExclusiveAccess(&s, &smMutex); // Provides locked access to singleton, that unlocks when destroyed | ||
} | ||
}; | ||
|
||
//-Macros---------------------------------------------------------------------------------------------------------- | ||
// Macro to be used in all derivatives | ||
#define QX_THREAD_SAFE_SINGLETON(Singleton) friend ThreadSafeSingleton<Singleton> | ||
|
||
} | ||
|
||
#endif // QX_THREADSAFE_SINGLETON |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
namespace Qx | ||
{ | ||
//=============================================================================================================== | ||
// ThreadSafeSingleton | ||
//=============================================================================================================== | ||
|
||
/*! | ||
* @class ThreadSafeSingleton qx/core/qx-threadsafesingleton.h | ||
* @ingroup qx-core | ||
* | ||
* @brief The ThreadSafeSingleton template class provides access to a static singleton instance in a thread-safe | ||
* manner. | ||
* | ||
* This class allows one to easily utilize the singleton pattern while ensuring that access to the global shared | ||
* instance remains thread-safe. This is achieved by providing basic scaffolding through a base class from which | ||
* the actual singleton should derive. The instance() method provides a mutex protected pointer to the | ||
* singleton instance via Qx::ExclusiveAccess. By declaring the final class' constructor as private, and accessing | ||
* the class through instance(), one can be certain that the instance is only ever being used by one thread at a | ||
* time. | ||
* | ||
* If code in your singleton calls external code that in turn results in instance() being called again | ||
* from within the same thread, be sure to use QRecursiveMutex when instantiating this template or else you | ||
* may cause a dead-lock. | ||
* | ||
* The following is a complete example for using Qx::ThreadSafeSingleton: | ||
* | ||
* @snippet qx-threadsafesingleton.cpp 0 | ||
*/ | ||
|
||
//-Constructor---------------------------------------------------------------------------------------------- | ||
//Protected: | ||
/*! | ||
* @fn ThreadSafeSingleton<Singleton, Mutex>::ThreadSafeSingleton() | ||
* | ||
* Constructs a ThreadSafeSingleton. | ||
* | ||
* @sa instance(). | ||
*/ | ||
|
||
//-Class Functions---------------------------------------------------------------------------------------------- | ||
//Public: | ||
/*! | ||
* @fn Qx::ExclusiveAccess<Singleton, QMutex> ThreadSafeSingleton<Singleton, Mutex>::instance() | ||
* | ||
* Returns a handle to the singleton instance. | ||
*/ | ||
|
||
//-Macros---------------------------------------------------------------------------------------------------------- | ||
/*! | ||
* @def QX_THREAD_SAFE_SINGLETON(Singleton) | ||
* | ||
* This macro must be used within the class definition of any singleton that derived from this class, similar to | ||
* Q_OBJECT for classes derived from QObject. | ||
* | ||
* The argument is to be the name of the derived class itself (similar to CRTP). | ||
*/ | ||
|
||
} |