diff --git a/containers_cpp/include/embb/containers/internal/indexed_object_pool-inl.h b/containers_cpp/include/embb/containers/internal/indexed_object_pool-inl.h index 9399129..efe7182 100644 --- a/containers_cpp/include/embb/containers/internal/indexed_object_pool-inl.h +++ b/containers_cpp/include/embb/containers/internal/indexed_object_pool-inl.h @@ -33,9 +33,9 @@ namespace embb { namespace containers { namespace internal { -template +template template -IndexedObjectPool:: +IndexedObjectPool:: IndexedObjectPool(RAI first, RAI last) : size_(static_cast(std::distance(first, last))), indexPool(internal::ReturningTrueIterator(0), @@ -50,9 +50,9 @@ IndexedObjectPool(RAI first, RAI last) : } } -template -IndexedObjectPool:: -IndexedObjectPool(size_t size, const T & defaultInstance) : +template +IndexedObjectPool:: +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 -IndexedObjectPool:: +template +IndexedObjectPool:: ~IndexedObjectPool() { allocator.deallocate(elements, size_); } -template -int IndexedObjectPool:: -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 -void IndexedObjectPool:: +template +void IndexedObjectPool:: 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 -T & IndexedObjectPool:: -operator[](size_t elementIndex) { +template +Type & IndexedObjectPool:: +operator[](int elementIndex) { return elements[elementIndex]; } +template +int IndexedObjectPool::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 +int IndexedObjectPool:: +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 +template +int IndexedObjectPool::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 +template +int IndexedObjectPool::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 +template +int IndexedObjectPool::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 +template +int IndexedObjectPool::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 diff --git a/containers_cpp/include/embb/containers/internal/indexed_object_pool.h b/containers_cpp/include/embb/containers/internal/indexed_object_pool.h index 0a0f57c..cb4587e 100644 --- a/containers_cpp/include/embb/containers/internal/indexed_object_pool.h +++ b/containers_cpp/include/embb/containers/internal/indexed_object_pool.h @@ -38,14 +38,14 @@ namespace containers { namespace internal { template< - typename T, + typename Type, class IndexPool = LockFreeTreeValuePool, class Allocator = embb::base::Allocator > 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 + int Allocate(Param1 const& param1); + + template + int Allocate(Param1 const& param1, Param2 const& param2); + + template + int Allocate(Param1 const& param1, Param2 const& param2, + Param3 const& param3); + + template + int Allocate(Param1 const& param1, Param2 const& param2, + Param3 const& param3, Param4 const& param4); +#endif }; } // namespace internal diff --git a/containers_cpp/include/embb/containers/internal/wait_free_mpmc_queue-inl.h b/containers_cpp/include/embb/containers/internal/wait_free_mpmc_queue-inl.h index b84c719..4b3f48f 100644 --- a/containers_cpp/include/embb/containers/internal/wait_free_mpmc_queue-inl.h +++ b/containers_cpp/include/embb/containers/internal/wait_free_mpmc_queue-inl.h @@ -90,7 +90,7 @@ bool WaitFreeMPMCQueueNode::CASNext( } template -bool WaitFreeMPMCQueueNode::Next_IsNull() const { +bool WaitFreeMPMCQueueNode::NextIsNull() const { return next_idx.Load() == UndefinedIndex; } @@ -112,8 +112,8 @@ const uint32_t WaitFreeMPMCQueueNode::UndefinedIndex = 0x3fffffff; } // namespace internal template< - typename Type, class NodeAllocator, class OpAllocator, class ValuePool > -WaitFreeMPMCQueue::OperationDesc:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +WaitFreeMPMCQueue::OperationDesc:: OperationDesc( bool pending, bool enqueue, @@ -133,8 +133,8 @@ OperationDesc( } template< - typename Type, class NodeAllocator, class OpAllocator, class ValuePool > -WaitFreeMPMCQueue::OperationDesc:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +WaitFreeMPMCQueue::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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +WaitFreeMPMCQueue:: 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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +WaitFreeMPMCQueue:: ~WaitFreeMPMCQueue() { // Dequeue until queue is empty: Type val; @@ -221,8 +222,8 @@ WaitFreeMPMCQueue:: } template< - typename Type, class NodeAllocator, class OpAllocator, class ValuePool > -inline bool WaitFreeMPMCQueue:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +inline bool WaitFreeMPMCQueue:: 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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +inline size_t WaitFreeMPMCQueue:: RetiredListMaxSize(size_t nThreads) { return static_cast( 1.25 * @@ -247,8 +248,8 @@ RetiredListMaxSize(size_t nThreads) { } template< - typename Type, class NodeAllocator, class OpAllocator, class ValuePool > -bool WaitFreeMPMCQueue:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +bool WaitFreeMPMCQueue:: 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(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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +bool WaitFreeMPMCQueue:: TryDequeue(Type & retElement) { index_t accessorId = static_cast(-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(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(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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +void WaitFreeMPMCQueue:: 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(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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +void WaitFreeMPMCQueue:: 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(lastIdx)]; index_t nextIdx = lastNode.NextPoolIdx(); - Node_t & nextNode = nodePool[nextIdx]; + Node_t & nextNode = nodePool[static_cast(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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +void WaitFreeMPMCQueue:: 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(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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +void WaitFreeMPMCQueue:: HelpFinishDequeue() { index_t firstIdx = headIdx.Load(); // Guard head: hp.GuardPointer(0, firstIdx); - Node_t & first = nodePool[firstIdx]; + Node_t & first = nodePool[static_cast(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(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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +void WaitFreeMPMCQueue:: 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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +void WaitFreeMPMCQueue:: DeleteNodeCallback(index_t releasedNodeIndex) { if (!NodeIsPending(releasedNodeIndex)) { nodePool.Free(static_cast(releasedNodeIndex)); @@ -581,16 +578,16 @@ DeleteNodeCallback(index_t releasedNodeIndex) { } template< - typename Type, class NodeAllocator, class OpAllocator, class ValuePool > -inline size_t WaitFreeMPMCQueue:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +inline size_t WaitFreeMPMCQueue:: GetCapacity() { return max_size_; } template< - typename Type, class NodeAllocator, class OpAllocator, class ValuePool > -inline bool WaitFreeMPMCQueue:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +inline bool WaitFreeMPMCQueue:: 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:: + typename Type, class ValuePool, class NodeAllocator, class OpAllocator > +inline bool WaitFreeMPMCQueue:: IsPending(unsigned int accessorId) { OperationDesc opDesc(operationDescriptions[accessorId].Load()); return opDesc.Pending; diff --git a/containers_cpp/include/embb/containers/wait_free_mpmc_queue.h b/containers_cpp/include/embb/containers/wait_free_mpmc_queue.h index b789d84..33557cd 100644 --- a/containers_cpp/include/embb/containers/wait_free_mpmc_queue.h +++ b/containers_cpp/include/embb/containers/wait_free_mpmc_queue.h @@ -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, class NodeAllocator = embb::base::AllocatorCacheAligned >, class OpAllocator = - embb::base::AllocatorCacheAligned >, - class ValuePool = - WaitFreeArrayValuePool + embb::base::AllocatorCacheAligned > > class WaitFreeMPMCQueue { private: typedef internal::WaitFreeMPMCQueueNode Node_t; typedef typename internal::WaitFreeMPMCQueueNode::index_t index_t; - typedef WaitFreeMPMCQueue self_t; + typedef WaitFreeMPMCQueue self_t; typedef internal::IndexedObjectPool< internal::WaitFreeMPMCQueueNode, ValuePool, NodeAllocator> NodePool_t;