Commit df279e99 by Tobias Fuchs

containers_cpp: interface of IndexedObjectPool conforming to ObjectPool

parent d3a090c3
......@@ -33,9 +33,9 @@ namespace embb {
namespace containers {
namespace internal {
template<typename T, class IndexPool, class Allocator>
template<typename Type, class IndexPool, class Allocator>
template<typename RAI>
IndexedObjectPool<T, IndexPool, Allocator>::
IndexedObjectPool<Type, IndexPool, Allocator>::
IndexedObjectPool(RAI first, RAI last) :
size_(static_cast<size_t>(std::distance(first, last))),
indexPool(internal::ReturningTrueIterator(0),
......@@ -50,9 +50,9 @@ IndexedObjectPool(RAI first, RAI last) :
}
}
template<typename T, class IndexPool, class Allocator>
IndexedObjectPool<T, IndexPool, Allocator >::
IndexedObjectPool(size_t size, const T & defaultInstance) :
template<typename Type, class IndexPool, class Allocator>
IndexedObjectPool<Type, IndexPool, Allocator >::
IndexedObjectPool(size_t size, const Type & defaultInstance) :
size_(size),
indexPool(internal::ReturningTrueIterator(0),
internal::ReturningTrueIterator(size_)) {
......@@ -66,42 +66,103 @@ IndexedObjectPool(size_t size, const T & defaultInstance) :
}
}
template<typename T, class IndexPool, class Allocator>
IndexedObjectPool<T, IndexPool, Allocator >::
template<typename Type, class IndexPool, class Allocator>
IndexedObjectPool<Type, IndexPool, Allocator >::
~IndexedObjectPool() {
allocator.deallocate(elements, size_);
}
template<typename T, class IndexPool, class Allocator>
int IndexedObjectPool<T, IndexPool, Allocator >::
Allocate(T & element) {
// Reserve a pool index:
bool reservedFlag;
int index = indexPool.Allocate(reservedFlag);
// Assign element to be allocated at pool index.
// Index returned from index pool is -1 if no index
// is available.
if (index >= 0) {
element = elements[index];
}
return index;
}
template<typename T, class IndexPool, class Allocator>
void IndexedObjectPool<T, IndexPool, Allocator >::
template<typename Type, class IndexPool, class Allocator>
void IndexedObjectPool<Type, IndexPool, Allocator >::
Free(int elementIndex) {
// Call the referenced element's destructor:
elements[elementIndex].~T();
elements[elementIndex].~Type();
// Release index of the element for reuse:
indexPool.Free(true, elementIndex);
}
template<typename T, class IndexPool, class Allocator>
T & IndexedObjectPool<T, IndexPool, Allocator >::
operator[](size_t elementIndex) {
template<typename Type, class IndexPool, class Allocator>
Type & IndexedObjectPool<Type, IndexPool, Allocator >::
operator[](int elementIndex) {
return elements[elementIndex];
}
template<class Type, typename ValuePool, class ObjectAllocator>
int IndexedObjectPool<Type, ValuePool, ObjectAllocator>::AllocateRaw(
Type * & newElement) {
newElement = NULL;
// Reserve a pool index:
bool val;
int allocated_index = indexPool.Allocate(val);
if (allocated_index >= 0) {
// Return pointer to reserved element:
Type * ret_pointer = &(elements[allocated_index]);
newElement = ret_pointer;
}
return allocated_index;
}
template<typename Type, class IndexPool, class Allocator>
int IndexedObjectPool<Type, IndexPool, Allocator >::
Allocate() {
Type * raw_object = NULL;
int element_index = AllocateRaw(raw_object);
if (element_index >= 0 && raw_object != NULL) {
new (raw_object)Type();
}
return element_index;
}
template<typename Type, class IndexPool, class Allocator>
template<typename Param1>
int IndexedObjectPool<Type, IndexPool, Allocator >::Allocate(
Param1 const & param1) {
Type * raw_object = NULL;
int element_index = AllocateRaw(raw_object);
if (element_index >= 0 && raw_object != NULL) {
new (raw_object)Type(param1);
}
return element_index;
}
template<typename Type, class IndexPool, class Allocator>
template<typename Param1, typename Param2>
int IndexedObjectPool<Type, IndexPool, Allocator >::Allocate(
Param1 const & param1, Param2 const & param2) {
Type * raw_object = NULL;
int element_index = AllocateRaw(raw_object);
if (element_index >= 0 && raw_object != NULL) {
new (raw_object)Type(param1, param2);
}
return element_index;
}
template<typename Type, class IndexPool, class Allocator>
template<typename Param1, typename Param2, typename Param3>
int IndexedObjectPool<Type, IndexPool, Allocator >::Allocate(
Param1 const & param1, Param2 const & param2,
Param3 const & param3) {
Type * raw_object = NULL;
int element_index = AllocateRaw(raw_object);
if (element_index >= 0 && raw_object != NULL) {
new (raw_object)Type(param1, param2, param3);
}
return element_index;
}
template<typename Type, class IndexPool, class Allocator>
template<typename Param1, typename Param2, typename Param3, typename Param4>
int IndexedObjectPool<Type, IndexPool, Allocator >::Allocate(
Param1 const & param1, Param2 const & param2,
Param3 const & param3, Param4 const & param4) {
Type * raw_object = NULL;
int element_index = AllocateRaw(raw_object);
if (element_index >= 0 && raw_object != NULL) {
new (raw_object)Type(param1, param2, param3, param4);
}
return element_index;
}
} // namespace internal
} // namespace containers
} // namespace embb
......
......@@ -38,14 +38,14 @@ namespace containers {
namespace internal {
template<
typename T,
typename Type,
class IndexPool = LockFreeTreeValuePool<bool, false>,
class Allocator = embb::base::Allocator<T>
>
class IndexedObjectPool {
private:
const size_t size_;
T * elements;
Type * elements;
Allocator allocator;
IndexPool indexPool;
IndexedObjectPool();
......@@ -53,10 +53,13 @@ class IndexedObjectPool {
IndexedObjectPool(const IndexedObjectPool&);
// Prevent assignment
IndexedObjectPool& operator=(const IndexedObjectPool&);
// Allocates pool index and resolves pointer to reserved element
int AllocateRaw(Type * & newElement);
public:
/**
* \see value_pool_concept
* Creates an indexed object pool, initializing all pool elements
* with elements from given range.
*
* \notthreadsafe
*
......@@ -73,8 +76,9 @@ class IndexedObjectPool {
);
/**
* \see value_pool_concept
*
* Creates an indexed object pool of size \c capacity, initializing
* all pool elements with given default instance.
*
* \notthreadsafe
*
* \memory dynamically allocates
......@@ -84,7 +88,7 @@ class IndexedObjectPool {
IndexedObjectPool(
size_t size,
/**< [IN] Number of elements the pool is filled with */
const T & defaultInstance
const Type & defaultInstance
/**< [IN] Default instance to initialize pool elements with */
);
......@@ -94,16 +98,6 @@ class IndexedObjectPool {
~IndexedObjectPool();
/**
* Request element and index from pool.
*
* \return index of element
*
* \see object_pool_concept
*
*/
int Allocate(T & element);
/**
* Return element and index to the pool.
*
* \see value_pool_concept
......@@ -117,7 +111,38 @@ class IndexedObjectPool {
* \see object_pool_concept
*
*/
T & operator[](size_t elementIndex);
Type & operator[](int elementIndex);
#ifdef DOXYGEN
/**
* Allocates an element from the pool.
*
* If the underlying value pool is wait-free/lock-free, this operation is
* also wait-free/lock-free, respectively.
*
* \return Index of the allocated object if successful, otherwise \c -1.
*
* \param ... Arguments of arbitrary type, passed to the object's constructor
*/
int Allocate(...);
#else
int Allocate();
template<typename Param1>
int Allocate(Param1 const& param1);
template<typename Param1, typename Param2>
int Allocate(Param1 const& param1, Param2 const& param2);
template<typename Param1, typename Param2, typename Param3>
int Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3);
template<typename Param1, typename Param2, typename Param3, typename Param4>
int Allocate(Param1 const& param1, Param2 const& param2,
Param3 const& param3, Param4 const& param4);
#endif
};
} // namespace internal
......
......@@ -90,7 +90,7 @@ bool WaitFreeMPMCQueueNode<Type>::CASNext(
}
template<typename Type>
bool WaitFreeMPMCQueueNode<Type>::Next_IsNull() const {
bool WaitFreeMPMCQueueNode<Type>::NextIsNull() const {
return next_idx.Load() == UndefinedIndex;
}
......@@ -112,8 +112,8 @@ const uint32_t WaitFreeMPMCQueueNode<Type>::UndefinedIndex = 0x3fffffff;
} // namespace internal
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::OperationDesc::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::OperationDesc::
OperationDesc(
bool pending,
bool enqueue,
......@@ -133,8 +133,8 @@ OperationDesc(
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::OperationDesc::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::OperationDesc::
OperationDesc(index_t raw) : Raw(raw) {
Pending = (raw & PENDING_FLAG_MASK) ? true : false;
Enqueue = (raw & ENQUEUE_FLAG_MASK) ? true : false;
......@@ -142,8 +142,8 @@ OperationDesc(index_t raw) : Raw(raw) {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
WaitFreeMPMCQueue(size_t capacity)
: max_size_(capacity),
// Disable "this is used in base member initializer" warning.
......@@ -178,9 +178,10 @@ WaitFreeMPMCQueue(size_t capacity)
"Maximum size of queue exceeded");
}
// Allocate sentinel node:
Node_t sentinelNode;
assert(sentinelNode.NextPoolIdx() == Node_t::UndefinedIndex);
int sentinelNodePoolIndex = nodePool.Allocate(sentinelNode);
int sentinelNodePoolIndex = nodePool.Allocate();
assert(
nodePool[sentinelNodePoolIndex].NextPoolIdx() ==
Node_t::UndefinedIndex);
if (sentinelNodePoolIndex < 0) {
EMBB_THROW(embb::base::NoMemoryException,
"Allocation of sentinel node failed");
......@@ -209,8 +210,8 @@ WaitFreeMPMCQueue(size_t capacity)
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
~WaitFreeMPMCQueue() {
// Dequeue until queue is empty:
Type val;
......@@ -221,8 +222,8 @@ WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
inline bool WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
inline bool WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
LoadAccessorThreadIndex(index_t & retIndexValue) {
unsigned int tmpIndexValue; // For conversion size32_t <-> unsigned int
if (embb_internal_thread_index(&tmpIndexValue) == EMBB_SUCCESS) {
......@@ -238,8 +239,8 @@ LoadAccessorThreadIndex(index_t & retIndexValue) {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
inline size_t WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
inline size_t WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
RetiredListMaxSize(size_t nThreads) {
return static_cast<size_t>(
1.25 *
......@@ -247,8 +248,8 @@ RetiredListMaxSize(size_t nThreads) {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
bool WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
bool WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
TryEnqueue(Type const & element) {
index_t accessorId = Node_t::UndefinedIndex;
if (!LoadAccessorThreadIndex(accessorId)) {
......@@ -256,14 +257,10 @@ TryEnqueue(Type const & element) {
"Invalid thread ID.");
}
// Register new node in pool:
Node_t poolNode;
int nodeIndex = nodePool.Allocate(poolNode);
int nodeIndex = nodePool.Allocate(element, accessorId);
if (nodeIndex < 0) {
return false; // Queue is at capacity
}
// Initialize node in pool:
Node_t newNode(element, accessorId);
nodePool[static_cast<index_t>(nodeIndex)] = newNode;
OperationDesc enqOp(
true, // pending
true, // enqueue
......@@ -276,8 +273,8 @@ TryEnqueue(Type const & element) {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
bool WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
bool WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
TryDequeue(Type & retElement) {
index_t accessorId = static_cast<index_t>(-1);
if (!LoadAccessorThreadIndex(accessorId)) {
......@@ -313,11 +310,11 @@ TryDequeue(Type & retElement) {
retElement = Type();
return false;
}
Node_t & node = nodePool[nodeIdx];
Node_t & node = nodePool[static_cast<int>(nodeIdx)];
assert(node.DequeueAID().Load() == accessorId);
// Return value of node next to node dequeued in this operation:
index_t nextNodeIdx = node.NextPoolIdx();
retElement = nodePool[nextNodeIdx].Value();
retElement = nodePool[static_cast<int>(nextNodeIdx)].Value();
// Value is safe. Mark node as non-pending and available for reclamation by
// setting this operation's node index to UndefinedIndex:
OperationDesc noOp(
......@@ -338,14 +335,14 @@ TryDequeue(Type & retElement) {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
void WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
void WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
HelpEnqueue(unsigned int accessorId) {
while (IsPending(accessorId)) {
index_t lastIdx = tailIdx.Load();
// Guard tail:
hp.GuardPointer(0, lastIdx);
Node_t & lastNode = nodePool[lastIdx];
Node_t & lastNode = nodePool[static_cast<int>(lastIdx)];
index_t nextIdx = lastNode.NextPoolIdx();
if (lastIdx == tailIdx.Load()) {
// Last node still is tail
......@@ -369,17 +366,17 @@ HelpEnqueue(unsigned int accessorId) {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
void WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
void WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
HelpFinishEnqueue() {
// Load node pointed at by tail:
index_t lastIdx = tailIdx.Load();
// Guard tail:
hp.GuardPointer(0, lastIdx);
// Load tail->next:
Node_t & lastNode = nodePool[lastIdx];
Node_t & lastNode = nodePool[static_cast<int>(lastIdx)];
index_t nextIdx = lastNode.NextPoolIdx();
Node_t & nextNode = nodePool[nextIdx];
Node_t & nextNode = nodePool[static_cast<int>(nextIdx)];
// tail->next not undefined => unfinished ENQ
if (nextIdx != Node_t::UndefinedIndex) {
// Load accessor id from last (non-tail) element in list:
......@@ -408,15 +405,15 @@ HelpFinishEnqueue() {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
void WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
void WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
HelpDequeue(index_t accessorId) {
while (IsPending(accessorId)) {
index_t firstIdx = headIdx.Load();
// Guard head:
hp.GuardPointer(0, firstIdx);
// Order matters for these assignments:
Node_t & first = nodePool[firstIdx];
Node_t & first = nodePool[static_cast<int>(firstIdx)];
index_t lastIdx = tailIdx.Load();
index_t nextIdx = first.NextPoolIdx();
// Guard head->next:
......@@ -499,19 +496,19 @@ HelpDequeue(index_t accessorId) {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
void WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
void WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
HelpFinishDequeue() {
index_t firstIdx = headIdx.Load();
// Guard head:
hp.GuardPointer(0, firstIdx);
Node_t & first = nodePool[firstIdx];
Node_t & first = nodePool[static_cast<int>(firstIdx)];
index_t nextIdx = first.NextPoolIdx();
// Guard and head->next
// Actually not necessary, as head->next will only change from Undefined
// to a node index value, but not back to Undefined.
hp.GuardPointer(1, nextIdx);
if (nextIdx != nodePool[firstIdx].NextPoolIdx()) {
if (nextIdx != nodePool[static_cast<int>(firstIdx)].NextPoolIdx()) {
return;
}
index_t accessorId = first.DequeueAID().Load();
......@@ -542,8 +539,8 @@ HelpFinishDequeue() {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
void WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
void WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
Help() {
// Fairness guarantee in every thread:
// "Every other thread will help my operation before helping its
......@@ -572,8 +569,8 @@ Help() {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
void WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
void WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
DeleteNodeCallback(index_t releasedNodeIndex) {
if (!NodeIsPending(releasedNodeIndex)) {
nodePool.Free(static_cast<int>(releasedNodeIndex));
......@@ -581,16 +578,16 @@ DeleteNodeCallback(index_t releasedNodeIndex) {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
inline size_t WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
inline size_t WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
GetCapacity() {
return max_size_;
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
inline bool WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
inline bool WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
NodeIsPending(index_t nodeIdx) {
for (unsigned int accessorId = 0; accessorId < num_states; ++accessorId) {
if (OperationDesc(operationDescriptions[accessorId].Load()).NodeIndex ==
......@@ -602,8 +599,8 @@ NodeIsPending(index_t nodeIdx) {
}
template<
typename Type, class NodeAllocator, class OpAllocator, class ValuePool >
inline bool WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool>::
typename Type, class ValuePool, class NodeAllocator, class OpAllocator >
inline bool WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator>::
IsPending(unsigned int accessorId) {
OperationDesc opDesc(operationDescriptions[accessorId].Load());
return opDesc.Pending;
......
......@@ -127,7 +127,7 @@ class WaitFreeMPMCQueueNode {
/**< [IN] New pointer value to set */
);
inline bool Next_IsNull() const;
inline bool NextIsNull() const;
inline uint32_t EnqueueAID() const;
......@@ -155,18 +155,18 @@ class WaitFreeMPMCQueueNode {
*/
template<
typename Type,
class ValuePool =
WaitFreeArrayValuePool<bool, false>,
class NodeAllocator =
embb::base::AllocatorCacheAligned<internal::WaitFreeMPMCQueueNode<Type> >,
class OpAllocator =
embb::base::AllocatorCacheAligned<embb::base::Atomic<uint32_t> >,
class ValuePool =
WaitFreeArrayValuePool<bool, false>
embb::base::AllocatorCacheAligned<embb::base::Atomic<uint32_t> >
>
class WaitFreeMPMCQueue {
private:
typedef internal::WaitFreeMPMCQueueNode<Type> Node_t;
typedef typename internal::WaitFreeMPMCQueueNode<Type>::index_t index_t;
typedef WaitFreeMPMCQueue<Type, NodeAllocator, OpAllocator, ValuePool> self_t;
typedef WaitFreeMPMCQueue<Type, ValuePool, NodeAllocator, OpAllocator> self_t;
typedef internal::IndexedObjectPool<
internal::WaitFreeMPMCQueueNode<Type>, ValuePool, NodeAllocator> NodePool_t;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment