-
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.
Add ExclusiveAccess, same as QMutexLocker but also provides data pointer
- Loading branch information
1 parent
f27e1c7
commit 5a750fe
Showing
4 changed files
with
281 additions
and
0 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,41 @@ | ||
//! [0] | ||
class Node; | ||
|
||
class Manager | ||
{ | ||
public: | ||
Manager(); | ||
void registerNode(Node* node); | ||
//... | ||
}; | ||
|
||
class Node | ||
{ | ||
static Qx::ExclusiveAccess<Manager, QMutex> manager() | ||
{ | ||
// RAII | ||
static Manager m; | ||
static QMutex mtx; | ||
return Qx::ExclusiveAccess(&m, &mtx); // Locks the mutex and stays locked since this is moved | ||
} | ||
|
||
public: | ||
Node(); | ||
void registerSelf() | ||
{ | ||
// Nodes could be in any thread, so this allows access to the manager to be synchronized | ||
auto mAccess = manager(); | ||
mAccess->registerNode(this); | ||
// Use access as needed... | ||
|
||
// Mutex is unlocked when mAccess is destroyed | ||
} | ||
}; | ||
|
||
void someFunctionInAThread() | ||
{ | ||
std::shared_ptr<Node> node; | ||
node->registerSelf(); | ||
//... | ||
} | ||
//! [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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
#ifndef QX_EXCLUSIVE_ACCESS_H | ||
#define QX_EXCLUSIVE_ACCESS_H | ||
|
||
// Extra-component Includes | ||
#include "qx/utility/qx-concepts.h" | ||
|
||
class QMutex; | ||
class QRecursiveMutex; | ||
|
||
namespace Qx | ||
{ | ||
|
||
template<typename AccessType, typename Mutex> | ||
requires any_of<Mutex, QMutex, QRecursiveMutex> | ||
class ExclusiveAccess | ||
{ | ||
//-Instance Variables--------------------------------------------------------------------------------------- | ||
private: | ||
AccessType* mAccess; | ||
Mutex* mMutex; | ||
bool mLocked; | ||
|
||
//-Constructor---------------------------------------------------------------------------------------------- | ||
private: | ||
ExclusiveAccess(const ExclusiveAccess&) = delete; | ||
|
||
public: | ||
[[nodiscard]] explicit ExclusiveAccess(AccessType* data, Mutex* mutex) noexcept: | ||
mLocked(false) | ||
{ | ||
mAccess = data; | ||
mMutex = mutex; | ||
if(mMutex) [[likely]] | ||
{ | ||
mMutex->lock(); | ||
mLocked = true; | ||
} | ||
} | ||
|
||
[[nodiscard]] ExclusiveAccess(ExclusiveAccess&& other) noexcept : | ||
mAccess(std::exchange(other.mAccess, nullptr)), | ||
mMutex(std::exchange(other.mMutex, nullptr)), | ||
mLocked(std::exchange(other.mLocked, false)) | ||
{} | ||
|
||
//-Destructor---------------------------------------------------------------------------------------------- | ||
public: | ||
~ExclusiveAccess() | ||
{ | ||
if(mLocked) | ||
unlock(); | ||
} | ||
|
||
//-Instance Functions---------------------------------------------------------------------------------------------- | ||
public: | ||
bool isLocked() const noexcept { return mLocked; } | ||
|
||
void unlock() noexcept | ||
{ | ||
Q_ASSERT(mLocked); | ||
mMutex->unlock(); | ||
mLocked = false; | ||
} | ||
|
||
void relock() noexcept | ||
{ | ||
Q_ASSERT(!mLocked); | ||
mMutex->lock(); | ||
mLocked = true; | ||
} | ||
|
||
void swap(ExclusiveAccess& other) noexcept | ||
{ | ||
using std::swap; // Allows use of specialized swap method for members, if they exist | ||
swap(mAccess, other.mAccess); | ||
swap(mMutex, other.mMutex); | ||
swap(mLocked, other.mLocked); | ||
} | ||
|
||
Mutex* mutex() const { return mMutex; } | ||
|
||
AccessType* access() { return mAccess; } | ||
const AccessType* access() const { return mAccess; } | ||
|
||
AccessType& operator*() { Q_ASSERT(mAccess); return *mAccess; } | ||
const AccessType& operator*() const { Q_ASSERT(mAccess); return *mAccess; } | ||
|
||
AccessType* operator->() { Q_ASSERT(mAccess); return mAccess; } | ||
const AccessType* operator->() const { Q_ASSERT(mAccess); return mAccess; } | ||
|
||
// Move-and-swap, which insures other's data is fully cleared (i.e. it loses exclusive access) due to the move construct | ||
ExclusiveAccess &operator=(ExclusiveAccess&& other) noexcept | ||
{ | ||
ExclusiveAccess moved(std::move(other)); | ||
swap(other); | ||
return *this; | ||
} | ||
}; | ||
|
||
} | ||
|
||
#endif // QX_EXCLUSIVE_ACCESS_H |
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,136 @@ | ||
namespace Qx | ||
{ | ||
//=============================================================================================================== | ||
// ExclusiveAccess | ||
//=============================================================================================================== | ||
|
||
/*! | ||
* @class ExclusiveAccess qx/core/qx-exclusiveaccess.h | ||
* @ingroup qx-core | ||
* | ||
* @brief The ExclusiveAccess template class is a convenience class that simplifies access to resources | ||
* secured by a mutex. | ||
* | ||
* This class is essentially a slightly more capable QMutexLocker and can be used in the same ways; however, | ||
* it couples access to the data protected by the mutex (in cases where there is, or data can easily be | ||
* wrapped within, a single resource) along with the automatic locking/unlocking of QMutexLocker. | ||
* | ||
* This can be useful when exclusive access to data needs to be provided by a class/function: | ||
* | ||
* @snippet qx-exclusiveaccess.cpp 0 | ||
*/ | ||
|
||
//-Constructor---------------------------------------------------------------------------------------------- | ||
//Public: | ||
/*! | ||
* @fn ExclusiveAccess<Type, Mutex>::ExclusiveAccess(AccessType* data, Mutex* mutex) | ||
* | ||
* Constructs an ExclusiveAccess and locks @a mutex. The mutex will be unlocked when the ExclusiveAccess is | ||
* destroyed. If @a mutex is @c nullptr, ExclusiveAccess only provides access to @a data. | ||
* | ||
* @sa QMutexLocker. | ||
*/ | ||
|
||
/*! | ||
* @fn ExclusiveAccess<Type, Mutex>::ExclusiveAccess(ExclusiveAccess&& other) | ||
* | ||
* Move-constructs and ExclusiveAccess from @a other. The mutex, data pointer, and state of @a other is | ||
* transferred to the newly constructed instance. After the move, @a other will no longer manage the mutex, | ||
* nor have a valid data pointer. | ||
* | ||
* @sa QMutexLocker. | ||
*/ | ||
|
||
//-Destructor------------------------------------------------------------------------------------------------ | ||
//Public: | ||
/*! | ||
* @fn ExclusiveAccess<Type, Mutex>::~ExclusiveAccess() | ||
* | ||
* Destroys the ExclusiveAccess and unlocks the mutex provided by the constructor if it's still locked. | ||
*/ | ||
|
||
//-Class Functions---------------------------------------------------------------------------------------------- | ||
//Public: | ||
/*! | ||
* @fn void ExclusiveAccess<Type, Mutex>::isLocked() const | ||
* | ||
* Returns @c true if this ExclusiveAccess is currently locking its associated mutex; otherwise, returns | ||
* @c false. | ||
*/ | ||
|
||
/*! | ||
* @fn void ExclusiveAccess<Type, Mutex>::mutex() const | ||
* | ||
* Returns the mutex on which the ExclusiveAccess is operating. | ||
*/ | ||
|
||
/*! | ||
* @fn void ExclusiveAccess<Type, Mutex>::relock() | ||
* | ||
* Relocks an unlocked ExclusiveAccess. | ||
* | ||
* @sa unlock(). | ||
*/ | ||
|
||
/*! | ||
* @fn void ExclusiveAccess<Type, Mutex>::swap(ExclusiveAccess& other) | ||
* | ||
* Swaps the mutex, data pointer, and state of this ExclusiveAccess with @a other. This operation | ||
* is very fast and never fails. | ||
*/ | ||
|
||
/*! | ||
* @fn void ExclusiveAccess<Type, Mutex>::unlock() | ||
* | ||
* Unlocks this ExclusiveAccess. You can use relock() to lock it again. It does not need to be | ||
* locked when destroyed. | ||
* | ||
* @sa relock(). | ||
*/ | ||
|
||
/*! | ||
* @fn AccessType* ExclusiveAccess<Type, Mutex>::access() | ||
* | ||
* Returns a pointer to the data the ExclusiveAccess is providing access to. | ||
*/ | ||
|
||
/*! | ||
* @fn const AccessType* ExclusiveAccess<Type, Mutex>::access() const | ||
* | ||
* @overload | ||
*/ | ||
|
||
/*! | ||
* @fn AccessType& ExclusiveAccess<Type, Mutex>::operator*() | ||
* | ||
* Returns a reference to the data the ExclusiveAccess is providing access to. | ||
*/ | ||
|
||
/*! | ||
* @fn const AccessType& ExclusiveAccess<Type, Mutex>::operator*() const | ||
* | ||
* @overload | ||
*/ | ||
|
||
/*! | ||
* @fn AccessType* ExclusiveAccess<Type, Mutex>::operator->() | ||
* | ||
* Provides convenient access to the members of @a DataType for the accessible data. | ||
*/ | ||
|
||
/*! | ||
* @fn const AccessType* ExclusiveAccess<Type, Mutex>::operator->() const | ||
* | ||
* @overload | ||
*/ | ||
|
||
/*! | ||
* @fn void ExclusiveAccess<Type, Mutex>::operator=(ExclusiveAccess&& other) | ||
* | ||
* Move-assigns @a other onto this ExclusiveAccess. If this ExclusiveAccess was holding onto a | ||
* locked mutex before the assignment, the mutex will be unlocked. The mutex, data pointer, and | ||
* state of @a other is then transferred to this QMutexLocker. After the move, @a other will | ||
* no longer manage the mutex, nor have a valid data pointer. | ||
*/ | ||
|
||
} |