pwxlib
0.8.9
Tools Library for C++ Development
|
Template for an element of a doubly linked list or ring of variable types. More...
#include <pwxTDoubleElement.h>
Public Types | |
typedef VElement | base_t |
Base type of this element. | |
typedef TDoubleElement< 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 | |
TDoubleElement (data_t *data_, void(*destroy_)(data_t *data_)) noexcept | |
default constructor More... | |
TDoubleElement (data_t *data_) noexcept | |
explicit constructor More... | |
TDoubleElement (const elem_t &src) noexcept | |
copy ctor More... | |
virtual | ~TDoubleElement () 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... | |
elem_t * | getPrev () const noexcept |
returns a pointer to the prev element or nullptr if there is none. More... | |
void | insertBefore (elem_t *new_next, store_t *store) |
insert an element before another More... | |
void | insertNext (elem_t *new_next, store_t *new_store) |
insert an element after this element. More... | |
void | insertPrev (elem_t *new_prev, store_t *new_store) |
insert an element before this element. More... | |
virtual void | remove () noexcept |
Remove this element from a list. More... | |
elem_t * | removeNext () noexcept |
remove the next element from a list. More... | |
elem_t * | removePrev () noexcept |
remove the previous element from a list. More... | |
void | setNext (elem_t *new_next) noexcept |
set the next pointer to another element. More... | |
void | setPrev (elem_t *new_prev) noexcept |
set the prev 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. | |
neighbor_t | prev = ATOMIC_VAR_INIT(nullptr) |
The previous element in the list or nullptr if this is the head. | |
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 doubly 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::TDoubleList and pwx::TDoubleRing.
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. The previous element in the list can be retrieved using the public foo->prev pointer.
If you plan to use this type in a multi-threaded environment, you can use the getNext(), getPrev(), setNext() and setPrev() functions to manipulate the next and prev pointers. 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 or insertPrev() to have it inserted before the called element safely.
To remove an element from a list, you can use remove(), removeNext() and removePrev() to have it removed safely.
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
See TSingleElement for the general discussion on multi threaded environments.
Critical work flows
The following work flows can be troublesome if multiple threads perform concurrent tasks on an element. Additionally to the tasks below, the tasks described in TSingleElement apply as well.
Task | Problematic action | Solution |
---|---|---|
Retrieve previous element | Remove this element | Elements know when they are removed. getPrev() then delivers the previously stored pointer, if any. |
Retrieve previous element | Move element to different container | This is not detectable, so never move an element. Remove and copy insert it! |
Insert an element before this | Either element destroyed by another thread | insertPrev() will lock both this and the new previous 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 previous element | The previous element gets removed or another element is inserted between the two elements by another thread | removePrev() will try to lock both elements after one another in a Release->Yield->Lock cycle until both are locked or the previous element changes. In the latter event the method does not remove the element, as it is gone. If the previous element goes away before it can be removed, a pwx::CException is thrown. |
Remove this element | Another thread removes this element, too | remove() will check whether the element is still valid once all locks are acquired. If the element got removed already, the method simply does nothing. |
|
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. Further it will notice all elements holding the same pointer of its presence. 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 done 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 get 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 will use atomic::load() and is therefore safe to use in a multi-threaded environment.
Referenced by pwx::TSet< data_t >::isSubsetOf(), and pwx::TSet< data_t >::TSet().
|
inlinenoexcept |
returns a pointer to the prev element or nullptr if there is none.
This method will use atomic::load() and is therefore safe to use in a multi-threaded environment.
|
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 the first element of a container.
if new_next is not nullptr or this element, the insertion will be done by its insertPrev() method.
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.
[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::TDoubleElement< data_t >::insertPrev(), and PWX_TRY_PWX_FURTHER.
Referenced by pwx::TSet< data_t >::protInsert().
|
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 and prev pointer of the inserted element, and the prev pointer of the old next element safely, too, in a multi-threaded environment.
If any of this, the current next or the new element are marked as destroyed, a pwx::CException is thrown. Such a condition implies that there is something seriously wrong.
If this element is marked as removed, it is simply assumed to be a single element in a container and will be marked as not removed by the insert.
On the other hand, 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 |
References pwx::CLockable::destroyed(), pwx::VElement::insert(), pwx::TDoubleElement< data_t >::next, pwx::TDoubleElement< data_t >::prev, PWX_DOUBLE_LOCK_GUARD, PWX_DOUBLE_LOCK_GUARD_RESET, PWX_LOCK_GUARD, PWX_THROW, pwx::TDoubleElement< data_t >::setNext(), and pwx::TDoubleElement< data_t >::setPrev().
Referenced by pwx::TSet< data_t >::protInsert().
|
inline |
insert an element before this element.
This is an extra method to not only set the prev pointer of this element, but the next and prev pointer of the inserted element, and the next pointer of the old previous element safely, too, in a multi-threaded environment.
If any of this, the current previous or the new element are marked as destroyed, a pwx::CException is thrown. Such a condition implies that there is something seriously wrong.
If this element is marked as removed, it is simply assumed to be a single element in a container and will be marked as not removed by the insert.
On the other hand, if new_prev is either this element or nullptr, the method simply does nothing.
[in] | new_prev | target where the prev pointer should point at. |
[in] | new_store | optional pointer to the CThreadElementStore that will handle this element |
References pwx::CLockable::destroyed(), pwx::VElement::insert(), pwx::TDoubleElement< data_t >::next, pwx::TDoubleElement< data_t >::prev, PWX_DOUBLE_LOCK_GUARD, PWX_DOUBLE_LOCK_GUARD_RESET, PWX_LOCK_GUARD, PWX_THROW, pwx::TDoubleElement< data_t >::setNext(), and pwx::TDoubleElement< data_t >::setPrev().
Referenced by pwx::TDoubleElement< data_t >::insertBefore().
|
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 |
Remove this element from a list.
Whenever you remove an element from a container you should call this method to tell it that it has been removed. Both the next and previous elements will be notified and the pointers to them set to nullptr.
Reimplemented from pwx::VElement.
References pwx::TDoubleElement< data_t >::next, pwx::TDoubleElement< data_t >::prev, PWX_TRIPLE_LOCK_GUARD, PWX_TRIPLE_LOCK_GUARD_RESET, pwx::TDoubleElement< data_t >::setNext(), and pwx::TDoubleElement< data_t >::setPrev().
Referenced by pwx::TDoubleElement< data_t >::removeNext(), and pwx::TDoubleElement< data_t >::removePrev().
|
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::TDoubleElement< data_t >::remove().
|
inlinenoexcept |
remove the previous element from a list.
This method removes the predecessor of this element from a list in a thread safe way.
References pwx::TDoubleElement< data_t >::remove().
|
inlinenoexcept |
set the next pointer to another element.
This method will use atomic::store() and is therefore safe to use in a multi-threaded environment.
Important: This method will *NOT* change the prev pointer of new_next!
[in] | new_next | target where the next pointer should point at. |
Referenced by pwx::TDoubleElement< data_t >::insertNext(), pwx::TDoubleElement< data_t >::insertPrev(), and pwx::TDoubleElement< data_t >::remove().
|
inlinenoexcept |
set the prev pointer to another element.
This method will use atomic::store() is therefore safe to use in a multi-threaded environment.
Important: This method will *NOT* change the next pointer of new_prev!
[in] | new_prev | target where the prev pointer should point at. |
Referenced by pwx::TDoubleElement< data_t >::insertNext(), pwx::TDoubleElement< data_t >::insertPrev(), and pwx::TDoubleElement< data_t >::remove().
|
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().