pwxlib
0.8.9
Tools Library for C++ Development
|
Template for an element of a singly linked list or ring of variable types. More...
#include <pwxTSingleElement.h>
Public Types | |
typedef VElement | base_t |
Base type of this element. | |
typedef TSingleElement< data_t > | elem_t |
Type of this element. | |
typedef std::shared_ptr< data_t > | share_t |
data_t wrapped in std::shared_ptr | |
typedef std::atomic< elem_t * > | neighbor_t |
elem_t* wrapped in std::atomic | |
typedef base_t::store_t | store_t |
The element store type to register this element with. | |
typedef std::mutex | lock_t |
Use standard mutex if no spinlocks are used. | |
Public Member Functions | |
TSingleElement (data_t *data_, void(*destroy_)(data_t *data_)) noexcept | |
default constructor More... | |
TSingleElement (data_t *data_) noexcept | |
explicit constructor More... | |
TSingleElement (const elem_t &src) noexcept | |
copy ctor More... | |
virtual | ~TSingleElement () noexcept |
destructor More... | |
int32_t | compare (const data_t &other) const noexcept |
compare this element with some data and return -1, 0, +1 More... | |
int32_t | compare (const elem_t *const other) const noexcept |
compare this element with another and return -1, 0, +1 More... | |
elem_t * | getNext () const noexcept |
returns a pointer to the next element or nullptr if there is none. More... | |
void | insertBefore (elem_t *new_next, store_t *new_store) |
insert an element before another More... | |
void | insertNext (elem_t *new_next, store_t *new_store) |
insert an element after this element. More... | |
virtual void | remove () noexcept |
tell the element that it has been removed. More... | |
elem_t * | removeNext () noexcept |
remove the next element from a list. More... | |
void | setNext (elem_t *new_next) noexcept |
set the next pointer to another element. More... | |
elem_t & | operator= (const elem_t &src) noexcept |
assignment operator More... | |
data_t & | operator* () |
dereferencing an element returns a reference to the stored data More... | |
const data_t & | operator* () const |
dereferencing a constant element returns a constant reference to the stored data More... | |
bool | operator== (const data_t &data_) const noexcept |
return true if this element has the data data More... | |
bool | operator!= (const data_t &data_) const noexcept |
return true if this element has differne data than data More... | |
virtual void | disable_thread_safety () noexcept |
turn off locking More... | |
virtual void | enable_thread_safety () noexcept |
turn on locking More... | |
virtual void | insert (store_t *new_store) noexcept |
mark as inserted More... | |
bool | inserted () const noexcept |
return true if marked as inserted More... | |
uint32_t | nr () const noexcept |
return current number More... | |
bool | removed () const noexcept |
return true if marked as removed More... | |
bool | beThreadSafe () const noexcept |
true if thread safety is turned on More... | |
void | beThreadSafe (bool doLock) noexcept |
set thread safety to doLock More... | |
bool | clear_locks () noexcept |
remove all locks More... | |
bool | destroyed () const noexcept |
if true the object will no longer lock More... | |
void | do_locking (bool doLock) noexcept |
set thread safety to doLock More... | |
bool | is_locked () const noexcept |
return true if this object is locked More... | |
bool | is_locking () const noexcept |
true if thread safety is turned on More... | |
void | lock () noexcept |
lock this object More... | |
uint32_t | lock_count () const noexcept |
number of locks this thread holds on this object More... | |
bool | try_lock () noexcept |
try to lock and return at once More... | |
void | unlock () noexcept |
unlock this object More... | |
Public Attributes | |
share_t | data |
The data this list element points to, wrapped in a shared_ptr. | |
neighbor_t | next = ATOMIC_VAR_INIT(nullptr) |
The next element in the list or nullptr if this is the tail. | |
aui32_t | eNr = ATOMIC_VAR_INIT(0) |
Number of the element. | |
Protected Attributes | |
abool_t | isDestroyed |
Should be set to true by the destructors of deriving classes. | |
mord_t | memOrdLoad |
to be used with atomic::load() | |
mord_t | memOrdStore |
to be used with atomic::store() | |
Template for an element of a singly linked list or ring of variable types.
This is a very simple and basic type to wrap a pointer of variable type into an object that is used with pwx::TSingleList and pwx::TSingleRing.
The constructor takes an optional destroy(T*) function pointer that is used to destroy the data when the element is deleted. If no such function was set, the standard delete operator is used instead.
The data pointer itself is wrapped into an std::shared_ptr. It is therefore completely safe to copy TSingleElement instances.
The data pointer itself is public. You can use foo->data.get() to access it. Further the operator* is overloaded and **foo will result in a reference to the data.
The next element in the list can be retrieved using the public foo->next pointer.
If you plan to use this type in a multi-threaded environment, you can use the getNext() and setNext() functions to manipulate the next pointer. See below for more on multi threaded usage.
To insert any element into a list you can use insertNext() to have it inserted after the called element safely. If you just added an element to a container you can use insert() to tell the element that it has been inserted into a container.
To remove an element from a list, you can use removeNext() to have the element after this to be removed safely. If there is no other element to call removeNext() from, you can use an elements remove() function to tell it that it has been removed from its container. This will set the next pointer to nullptr as well.
It is recommended that you use the much more advanced std::list unless you need to store a very large number of elements and can not live with the downside of every element having to be copied into the std::list.
Notes on multi threaded environments
If you plan to use an element in a strictly single-threaded way, you can use the function disable_thread_safety() inherited from VElement to disable the locking mechanism and have the getter and setter methods be less restrictive. You can then use insertNext() / removeNext() without the locking overhead. However, as the locking is enabled by default, it might be more convenient to simply use the next pointers directly.
Critical work flows
The following work flows can be troublesome if multiple threads perform concurrent tasks on an element:
Task | Problematic action | Solution |
---|---|---|
Retrieve next element | Remove this element | Elements know when they are removed. getNext() then delivers the previously stored pointer, if any. |
Retrieve next element | Move element to different container | This is not detectable, so never move an element. Remove and copy insert it! |
Insert an element after this | Either element destroyed by another thread | insertNext() will lock both this and the new next element. Further it checks whether any is destroyed and only inserts the element if both are not marked as destroyed. If either is marked as destroyed, a pwx::CException is thrown, as those conditions imply serious bugs. |
Remove the next element | The next element gets removed or another element is inserted between the two elements by another thread | removeNext() will try to lock both elements after one another in a Release->Yield->Lock cycle until both are locked or the next element changes. In the latter event the method does not remove the element, as it is gone. If the next element goes away before it can be removed, a pwx::CException is thrown. |
|
inlinenoexcept |
default constructor
The default constructor sets both the data pointer and the destroy method.
[in] | data_ | A pointer to the data this list element is to hold. |
[in] | destroy_ | A pointer to a function that is to be used to destroy the data |
|
inlineexplicitnoexcept |
explicit constructor
Delegating ctor that calls the default ctor with destroy_ being the nullptr
[in] | data_ | A pointer to the data this list element is to hold. |
|
inlinenoexcept |
copy ctor
The copy ctor creates a stand-alone element without neighbors copying the data pointer and destroy method from src. As the data is wrapped in a shared_ptr, data will not get deleted unless the last reference is gone.
Important: Whether the element does locking or not is not copied. It will silently be turned on by default!
[in] | src | reference to the element to copy. |
|
virtualnoexcept |
destructor
The destructor invokes a lock on the instance to allow other threads to react before the object itself is gone.
Because of the usage of shared_ptr wrapping the data this is only deleted if, and only if, this is the very last element referencing this data.
|
noexceptinherited |
true if thread safety is turned on
return true if thread safety mode is turned on
Referenced by pwx::private_::CThreadElementStore::clear(), pwx::private_::CThreadElementStore::curr(), pwx::private_::CThreadElementStore::disable_thread_safety(), pwx::VTHashBase< key_t, data_t, THashElement< key_t, data_t > >::disable_thread_safety(), pwx::private_::CThreadElementStore::enable_thread_safety(), pwx::VTHashBase< key_t, data_t, THashElement< key_t, data_t > >::enable_thread_safety(), pwx::VTHashBase< key_t, data_t, THashElement< key_t, data_t > >::operator+=(), and pwx::VTHashBase< key_t, data_t, THashElement< key_t, data_t > >::operator=().
|
noexceptinherited |
set thread safety to doLock
set thread safety mode to doLock This is just an alias for do_locking().
|
noexceptinherited |
remove all locks
clear all locks from this thread.
If this thread is the current owner of the lock, and if there are locks in place, they are all cleared.
If this thread is not the owner, the method simply returns false.
References CURRENT_THREAD_ID.
|
inlinenoexcept |
compare this element with some data and return -1, 0, +1
This is a convenient method that safely compares this element to some data. If this elements data is larger than the other, the method returns 1. If both are equal it returns 0 and 1 if the other data is larger.
This element gets locked and checked against destruction and nullptr data.
[in] | other | reference to the data to compare with |
|
inlinenoexcept |
compare this element with another and return -1, 0, +1
This is a convenient method that safely compares this element to another. If this elements data is larger than the others data, the method returns 1. If both are equal it returns 0 and 1 if the other elements data is larger.
Both elements get locked and checked against destruction and nullptr data.
[in] | other | pointer to the element to compare with |
|
noexceptinherited |
if true the object will no longer lock
returns true if the data was destroyed
The destructor of TSingleElement and TDoubleElement will try to get a final lock on the element when it is destroyed. If another thread acquires a lock between the data destruction and this final dtor lock, destroyed() will return "true".
References pwx::CLockable::isDestroyed, and pwx::CLockable::memOrdLoad.
Referenced by pwx::TSingleElement< data_t >::insertBefore(), pwx::THashElement< size_t, curr_t >::insertNext(), pwx::TDoubleElement< data_t >::insertNext(), pwx::TSingleElement< data_t >::insertNext(), and pwx::TDoubleElement< data_t >::insertPrev().
|
virtualnoexceptinherited |
turn off locking
disable thread safety
This method disables all thread safety measures.
Warning: It is completely unchecked whether the element is used by more than one thread. If concurrent threads work with this element while this method is called, the outcome is unpredictable.
References pwx::CLockable::do_locking().
|
noexceptinherited |
set thread safety to doLock
switch whether to really use locking or not.
With this method you can switch the locking mechanics on/off for objects to be used in concurrency or strictly single threaded. The default is to turn locking on.
[in] | doLock | true to turn locking on, false to turn it off. |
References CURRENT_THREAD_ID.
Referenced by pwx::VElement::disable_thread_safety(), and pwx::VElement::enable_thread_safety().
|
virtualnoexceptinherited |
turn on locking
enable thread safety
This method enables all thread safety measures.
References pwx::CLockable::do_locking().
|
inlinenoexcept |
returns a pointer to the next element or nullptr if there is none.
This method uses atomic::load() and is therefore safe to use in a multi-threaded environment.
Referenced by pwx::TSingleElement< data_t >::removeNext().
|
virtualnoexceptinherited |
mark as inserted
mark this element as being inserted
This method should be called by all deriving element classes upon insertion to mark this element as being inserted.
Additionally this method will store the given pointer to the handling CThreadElementStore and invalidate itself there upon removal.
[in] | new_store | pointer to the CThreadElementStore that might handle this element from now on. |
Referenced by pwx::THashElement< size_t, curr_t >::insertNext(), pwx::TDoubleElement< data_t >::insertNext(), pwx::TSingleElement< data_t >::insertNext(), and pwx::TDoubleElement< data_t >::insertPrev().
|
inline |
insert an element before another
This is a special insertion method that is to be used if this element is to become the new head of a container. In this special case there is no element to use insertNext() from, so this method does the handling.
If either this or the new_next element is marked as destroyed, a pwx::CException is thrown. Such a condition implies that there is something seriously wrong.
if new_next is either nullptr or this element, the element will only be marked as inserted.
[in] | new_next | target where the next pointer should point at. |
[in] | new_store | optional pointer to the CThreadElementStore that will handle this element |
References pwx::CLockable::destroyed(), PWX_DOUBLE_LOCK_GUARD, and PWX_THROW.
|
noexceptinherited |
return true if marked as inserted
return true if the element is a member of a container
For this to work derived elements and containers using these have to use insert()/remove() accordingly.
References pwx::CLockable::memOrdLoad.
|
inline |
insert an element after this element.
This is an extra method to not only set the next pointer of this element, but the next pointer of the inserted element safely, too, in a multi-threaded environment.
If either this or the new element is marked as destroyed, a pwx::CException is thrown. Such a condition implies that there is something seriously wrong.
If new_next is either this element or nullptr, the method simply does nothing.
[in] | new_next | target where the next pointer should point at. |
[in] | new_store | optional pointer to the CThreadElementStore that will handle this element from now on |
References pwx::CLockable::destroyed(), pwx::VElement::insert(), pwx::TSingleElement< data_t >::next, PWX_DOUBLE_LOCK_GUARD, PWX_THROW, and pwx::TSingleElement< data_t >::setNext().
|
noexceptinherited |
return true if this object is locked
return true if this object is currently locked
References pwx::CLockable::memOrdLoad.
|
noexceptinherited |
true if thread safety is turned on
return true if the locking is turned on.
|
noexceptinherited |
lock this object
lock
Lock this object for the current thread if locking is enabled.
References CURRENT_THREAD_ID, pwx::CLockable::isDestroyed, and pwx::CLockable::memOrdLoad.
Referenced by pwx::private_::CThreadElementStore::curr().
|
noexceptinherited |
number of locks this thread holds on this object
return the number of locks on this object this thread has
References CURRENT_THREAD_ID.
|
noexceptinherited |
return current number
return the current number of the element in a thread safe way
References pwx::VElement::eNr, and pwx::CLockable::memOrdLoad.
|
inlinenoexcept |
return true if this element has differne data than data
[in] | data_ | const reference of the data to check |
|
inline |
dereferencing an element returns a reference to the stored data
If the data pointer is nullptr, a pwx::CException with the name "NullDataException" is thrown.
|
inline |
dereferencing a constant element returns a constant reference to the stored data
If the data pointer is nullptr, a pwx::CException with the name "NullDataException" is thrown.
|
inlinenoexcept |
assignment operator
The assignment operator copies over the element and the destroy method. This element will stay where it is, and not change its position.
[in] | src | const reference of the element to copy |
References PWX_DOUBLE_LOCK_GUARD.
|
inlinenoexcept |
return true if this element has the data data
[in] | data_ | const reference of the data to check |
|
inlinevirtualnoexcept |
tell the element that it has been removed.
Whenever you remove an element from a container you should call this method to tell it that it has been removed. The next pointer of the element will be set to nullptr by this method.
Reimplemented from pwx::VElement.
References PWX_LOCK_GUARD.
Referenced by pwx::TSingleElement< data_t >::removeNext().
|
noexceptinherited |
return true if marked as removed
return true if the element is not a member of a container
For this to work derived elements and containers using these have to use insert()/remove() accordingly.
References pwx::CLockable::memOrdLoad.
|
inlinenoexcept |
remove the next element from a list.
This method removes the successor of this element from a list in a thread safe way.
References pwx::TSingleElement< data_t >::getNext(), pwx::TSingleElement< data_t >::next, PWX_DOUBLE_LOCK_GUARD, PWX_DOUBLE_LOCK_GUARD_RESET, and pwx::TSingleElement< data_t >::remove().
|
inlinenoexcept |
set the next pointer to another element.
This method uses atomic::store() and is therefore safe to use in a multi-threaded environment.
[in] | new_next | target where the next pointer should point at. |
Referenced by pwx::TSingleElement< data_t >::insertNext().
|
noexceptinherited |
try to lock and return at once
try_lock
Try to lock this object.
References CURRENT_THREAD_ID, pwx::CLockable::isDestroyed, and pwx::CLockable::memOrdLoad.
Referenced by pwx::try_locks().
|
noexceptinherited |
unlock this object
unlock
If locking is disabled or if the current thread does not hold the lock, nothing happens. Otherwise the last lock is released.
References CURRENT_THREAD_ID.
Referenced by pwx::private_::CThreadElementStore::curr(), pwx::try_locks(), and pwx::unlock_all().