diff --git a/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h b/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h index 05638db..352e6c8 100644 --- a/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h +++ b/containers_cpp/include/embb/containers/internal/wait_free_spsc_queue-inl.h @@ -37,11 +37,19 @@ namespace embb { namespace containers { template -WaitFreeSPSCQueue::WaitFreeSPSCQueue(size_t capacity) : -capacity(capacity), - head_index(0), - tail_index(0) { - queue_array = allocator.allocate(capacity); +size_t WaitFreeSPSCQueue:: +AlignCapacityToPowerOfTwo(size_t capacity) { + size_t result = 1; + while (result < capacity) result <<= 1; + return result; +} + +template +WaitFreeSPSCQueue::WaitFreeSPSCQueue(size_t capacity) + : capacity(AlignCapacityToPowerOfTwo(capacity)), + head_index(0), + tail_index(0) { + queue_array = allocator.allocate(this->capacity); } template @@ -51,7 +59,7 @@ size_t WaitFreeSPSCQueue::GetCapacity() { template bool WaitFreeSPSCQueue::TryEnqueue(Type const & element) { - if (head_index - tail_index == capacity) + if (tail_index - head_index == capacity) return false; queue_array[tail_index % capacity] = element; diff --git a/containers_cpp/include/embb/containers/wait_free_spsc_queue.h b/containers_cpp/include/embb/containers/wait_free_spsc_queue.h index a0f0cdc..f470ed6 100644 --- a/containers_cpp/include/embb/containers/wait_free_spsc_queue.h +++ b/containers_cpp/include/embb/containers/wait_free_spsc_queue.h @@ -145,11 +145,17 @@ class WaitFreeSPSCQueue { */ embb::base::Atomic tail_index; + /** + * Align capacity to the next smallest power of two + */ + static size_t AlignCapacityToPowerOfTwo(size_t capacity); + public: /** - * Creates a queue with the specified capacity. + * Creates a queue with at least the specified capacity. * - * \memory Allocates \c capacity elements of type \c Type. + * \memory Allocates \c 2^k elements of type \c Type, where \k is the + * smallest number such that capacity <= 2^k holds. * * \notthreadsafe * diff --git a/containers_cpp/test/queue_test-inl.h b/containers_cpp/test/queue_test-inl.h index 97207b2..39d350f 100644 --- a/containers_cpp/test/queue_test-inl.h +++ b/containers_cpp/test/queue_test-inl.h @@ -276,16 +276,43 @@ QueueTestSingleProducerSingleConsumer_ThreadMethod() { template void QueueTest:: QueueTestSingleThreadEnqueueDequeue_ThreadMethod() { + // Enqueue the expected amount of elements for (int i = 0; i != n_queue_size; ++i) { bool success = queue->TryEnqueue(element_t(0, i * 133)); PT_ASSERT(success == true); } + + // Some queues may allow enqueueing more elements than their capacity + // permits, so try to enqueue additional elements until the queue is full + int oversized_count = n_queue_size; + while ( queue->TryEnqueue(element_t(0, oversized_count * 133)) ) { + ++oversized_count; + } + // Oversized amount should not be larger than the original capacity + PT_ASSERT_LT(oversized_count, 2 * n_queue_size); + + // Dequeue the expected amount of elements for (int i = 0; i != n_queue_size; ++i) { element_t dequ(0, -1); bool success = queue->TryDequeue(dequ); PT_ASSERT(success == true); PT_ASSERT(dequ.second == i * 133); } + + // Dequeue any elements enqueued above the original capacity + for (int i = n_queue_size; i != oversized_count; ++i) { + element_t dequ(0, -1); + bool success = queue->TryDequeue(dequ); + PT_ASSERT(success == true); + PT_ASSERT(dequ.second == i * 133); + } + + // Ensure the queue is now empty + { + element_t dequ; + bool success = queue->TryDequeue(dequ); + PT_ASSERT(success == false); + } } template