Commit 41613a67 by Christian Kern

Merge remote-tracking branch 'origin/development' into embb445_opencl_plugin_example_fails

parents 78957f33 6276d2c2
...@@ -28,7 +28,7 @@ cmake_minimum_required (VERSION 2.8.9) ...@@ -28,7 +28,7 @@ cmake_minimum_required (VERSION 2.8.9)
# Version number # Version number
set (EMBB_BASE_VERSION_MAJOR 0) set (EMBB_BASE_VERSION_MAJOR 0)
set (EMBB_BASE_VERSION_MINOR 2) set (EMBB_BASE_VERSION_MINOR 2)
set (EMBB_BASE_VERSION_PATCH 3) set (EMBB_BASE_VERSION_PATCH 4)
# Fix compilation for CMake versions >= 3.1 # Fix compilation for CMake versions >= 3.1
# #
......
...@@ -92,13 +92,14 @@ contact us: embb-dev@googlegroups.com. ...@@ -92,13 +92,14 @@ contact us: embb-dev@googlegroups.com.
Directory Structure Directory Structure
------------------- -------------------
EMB² is a technology stack consisting of various building blocks. For some of EMB² consists of various building blocks. For some of them, there exist C and
them, there exist C and C++ versions, others are only implemented in C++. The C++ versions, others are only implemented in C++. The directory names are
directory names are postfixed with either "_cpp" or "_c" for the C++ and C postfixed with either "_cpp" or "_c" for the C++ and C versions, respectively.
versions, respectively. Currently, EMB² contains the following components: Currently, EMB² contains the following components:
- base: base_c, base_cpp - base: base_c, base_cpp
- mtapi: mtapi_c, mtapi_cpp - mtapi: mtapi_c, mtapi_network_c, mtapi_opencl_c, mtapi_cpp
- tasks: tasks_cpp
- algorithms: algorithms_cpp - algorithms: algorithms_cpp
- dataflow: dataflow_cpp - dataflow: dataflow_cpp
- containers: containers_cpp - containers: containers_cpp
...@@ -109,12 +110,14 @@ the header files, source files, and unit tests, respectively. ...@@ -109,12 +110,14 @@ the header files, source files, and unit tests, respectively.
Component base_c contains abstractions for threading, synchronization, atomic Component base_c contains abstractions for threading, synchronization, atomic
operations, and other functionalities. As the name indicates, the code is operations, and other functionalities. As the name indicates, the code is
implemented in C. Component base_cpp is mainly a C++ wrapper around the base_c implemented in C. Component base_cpp is mainly a C++ wrapper around the base_c
functionalities. Component mtapi_c is a task scheduler written in C and functions. Component mtapi_c is a task scheduler written in C and mtapi_cpp a
mtapi_cpp a C++ wrapper for the scheduler. Component algorithms_cpp provides C++ wrapper for the scheduler (mtapi_network_c and mtapi_opencl_c are scheduler
high-level constructs for typical parallelization task in C++, and plugins for distributed and OpenCL-based heterogeneous systems, respectively).
dataflow_cpp generic skeletons for the development of parallel stream-based To simplify programming of homogeneous systems, tasks_cpp contains abstractions
applications. Finally, component containers_cpp provides containers, i.e., to the MTAPI interfaces. Component algorithms_cpp provides high-level constructs
data structures for storing object in an organized and thread-safe way. for typical parallelization tasks in C++, and dataflow_cpp generic skeletons for
the development of parallel stream-based applications. Finally, containers_cpp
provides data structures for storing objects in a thread-safe way.
Build and Installation Build and Installation
......
...@@ -31,109 +31,104 @@ namespace embb { ...@@ -31,109 +31,104 @@ namespace embb {
namespace algorithms { namespace algorithms {
namespace internal { namespace internal {
template<typename ForwardIterator> template<typename RAI>
ChunkDescriptor<ForwardIterator>::ChunkDescriptor(ForwardIterator first, ChunkDescriptor<RAI>::ChunkDescriptor(
ForwardIterator last) : RAI first, RAI last) :
first(first), last(last) { first_(first), last_(last) {
} }
template<typename ForwardIterator> template<typename RAI>
ForwardIterator ChunkDescriptor<ForwardIterator>::GetFirst() const { RAI ChunkDescriptor<RAI>::GetFirst() const {
return first; return first_;
} }
template<typename ForwardIterator> template<typename RAI>
ForwardIterator ChunkDescriptor<ForwardIterator>::GetLast() const { RAI ChunkDescriptor<RAI>::GetLast() const {
return last; return last_;
} }
template<typename ForwardIterator> template<typename RAI>
BlockSizePartitioner<ForwardIterator>::BlockSizePartitioner( BlockSizePartitioner<RAI>::BlockSizePartitioner(
ForwardIterator first, ForwardIterator last, size_t chunkSize) : RAI first, RAI last, size_t chunkSize) :
first(first), last(last), chunkSize(chunkSize) { first_(first), last_(last), chunk_size_(chunkSize) {
elements_count = static_cast<size_t>(std::distance(first, last)); elements_count_ = static_cast<size_t>(std::distance(first_, last_));
chunks = elements_count / chunkSize; chunks_ = elements_count_ / chunk_size_;
if (elements_count % chunkSize != 0) if (elements_count_ % chunk_size_ != 0) {
chunks++; chunks_++;
}
} }
template<typename ForwardIterator> template<typename RAI>
size_t BlockSizePartitioner<ForwardIterator>::Size() { size_t BlockSizePartitioner<RAI>::Size() {
return chunks; return chunks_;
} }
template<typename ForwardIterator> template<typename RAI>
const ChunkDescriptor<ForwardIterator> const ChunkDescriptor<RAI>
BlockSizePartitioner<ForwardIterator>::operator[]( BlockSizePartitioner<RAI>::operator[](
size_t const& index) const { size_t const & index) const {
ForwardIterator first_new = first; typedef typename std::iterator_traits<RAI>::difference_type
std::advance(first_new, index * chunkSize); difference_type;
RAI first_new(first_);
ForwardIterator last_new = first_new; first_new += static_cast<difference_type>(chunk_size_ * index);
RAI last_new(first_new);
if (index >= chunks - 1) { if (index >= chunks_ - 1) {
last_new = last; last_new = last_;
} else { } else {
std::advance(last_new, chunkSize); last_new += static_cast<difference_type>(chunk_size_);
} }
return ChunkDescriptor<RAI>(first_new, last_new);
return ChunkDescriptor<ForwardIterator>(first_new, last_new);
} }
template<typename ForwardIterator> template<typename RAI>
size_t ChunkPartitioner<ForwardIterator>::Size() { size_t ChunkPartitioner<RAI>::Size() {
return size; return size_;
} }
template<typename ForwardIterator> template<typename RAI>
ChunkPartitioner<ForwardIterator>::ChunkPartitioner(ForwardIterator first, ChunkPartitioner<RAI>::ChunkPartitioner(
ForwardIterator last, size_t amountChunks) : RAI first, RAI last, size_t amountChunks) :
first(first), last(last) { first_(first), last_(last) {
if (amountChunks > 0) { if (amountChunks > 0) {
size = amountChunks; size_ = amountChunks;
} else { } else {
// if no concrete chunk size was given, use number of cores... // if no concrete chunk size was given, use number of cores
embb::tasks::Node& node = embb::tasks::Node::GetInstance(); embb::tasks::Node& node = embb::tasks::Node::GetInstance();
size = node.GetWorkerThreadCount(); size_ = node.GetWorkerThreadCount();
} }
elements_count_ = static_cast<size_t>(std::distance(first_, last_));
elements_count = static_cast<size_t>(std::distance(first, last)); if (size_ > elements_count_) {
if (size > elements_count) {
// if we want to make more chunks than we have elements, correct // if we want to make more chunks than we have elements, correct
// the number of chunks // the number of chunks
size = elements_count; size_ = elements_count_;
} }
standard_chunk_size = elements_count / size; standard_chunk_size_ = elements_count_ / size_;
bigger_chunk_count = elements_count % size; bigger_chunk_count_ = elements_count_ % size_;
} }
template<typename ForwardIterator> template<typename RAI>
const ChunkDescriptor<ForwardIterator> const ChunkDescriptor<RAI>
ChunkPartitioner<ForwardIterator>::operator[]( ChunkPartitioner<RAI>::operator[](
size_t const& index) const { size_t const& index) const {
typedef typename std::iterator_traits<ForwardIterator>::difference_type typedef typename std::iterator_traits<RAI>::difference_type
difference_type; difference_type;
// Number of element preceding elements in the given chunk
size_t prec_elements_count = 0; size_t prec_elements_count = 0;
if (index <= bigger_chunk_count_) {
if (index <= bigger_chunk_count) { prec_elements_count = index * (standard_chunk_size_ + 1);
prec_elements_count = index * (standard_chunk_size + 1);
} else { } else {
prec_elements_count = (standard_chunk_size + 1) * bigger_chunk_count prec_elements_count =
+ standard_chunk_size * (index - bigger_chunk_count); (standard_chunk_size_ + 1) * bigger_chunk_count_ +
(standard_chunk_size_ * (index - bigger_chunk_count_));
} }
size_t cur_elements_count = (index < bigger_chunk_count_)
size_t cur_elements_count = ? (standard_chunk_size_ + 1)
(index < bigger_chunk_count) ? : standard_chunk_size_;
(standard_chunk_size + 1) : standard_chunk_size; RAI first_new(first_);
first_new += static_cast<difference_type>(prec_elements_count);
ForwardIterator first_new = first; RAI last_new(first_new);
std::advance(first_new, prec_elements_count); last_new += static_cast<difference_type>(cur_elements_count);
return ChunkDescriptor<RAI>(first_new, last_new);
first_new = first + static_cast<difference_type>(prec_elements_count);
ForwardIterator last_new = first_new;
std::advance(last_new, cur_elements_count);
return ChunkDescriptor<ForwardIterator>(first_new, last_new);
} }
} // namespace internal } // namespace internal
......
...@@ -38,14 +38,14 @@ namespace internal { ...@@ -38,14 +38,14 @@ namespace internal {
* Describes a single partition of a 1-dimensional * Describes a single partition of a 1-dimensional
* partitioning, using first and last iterator. * partitioning, using first and last iterator.
* *
* \tparam ForwardIterator Type of the iterator. * \tparam RAI Type of the iterator.
*/ */
template<typename ForwardIterator> template<typename RAI>
class ChunkDescriptor { class ChunkDescriptor {
private: private:
ForwardIterator first; RAI first_;
ForwardIterator last; RAI last_;
public: public:
/** /**
...@@ -54,7 +54,7 @@ class ChunkDescriptor { ...@@ -54,7 +54,7 @@ class ChunkDescriptor {
* \param first The first iterator. * \param first The first iterator.
* \param last The last iterator * \param last The last iterator
*/ */
ChunkDescriptor(ForwardIterator first, ForwardIterator last); ChunkDescriptor(RAI first, RAI last);
/** /**
* Gets the first iterator. * Gets the first iterator.
...@@ -63,7 +63,7 @@ class ChunkDescriptor { ...@@ -63,7 +63,7 @@ class ChunkDescriptor {
* *
* \waitfree * \waitfree
*/ */
ForwardIterator GetFirst() const; RAI GetFirst() const;
/** /**
* Gets the last iterator. * Gets the last iterator.
...@@ -72,7 +72,7 @@ class ChunkDescriptor { ...@@ -72,7 +72,7 @@ class ChunkDescriptor {
* *
* \waitfree * \waitfree
*/ */
ForwardIterator GetLast() const; RAI GetLast() const;
}; };
/** /**
...@@ -80,9 +80,9 @@ class ChunkDescriptor { ...@@ -80,9 +80,9 @@ class ChunkDescriptor {
* *
* Describes the interface for accessing a 1-dimensional partitioning. * Describes the interface for accessing a 1-dimensional partitioning.
* *
* \tparam ForwardIterator Type of the iterator. * \tparam RAI Type of the iterator.
*/ */
template<typename ForwardIterator> template<typename RAI>
class IPartitioner { class IPartitioner {
public: public:
virtual ~IPartitioner() {} virtual ~IPartitioner() {}
...@@ -106,7 +106,7 @@ class IPartitioner { ...@@ -106,7 +106,7 @@ class IPartitioner {
* *
* \waitfree * \waitfree
*/ */
virtual const ChunkDescriptor<ForwardIterator> operator[]( virtual const ChunkDescriptor<RAI> operator[](
size_t const& index) const = 0; size_t const& index) const = 0;
}; };
...@@ -129,16 +129,16 @@ class IPartitioner { ...@@ -129,16 +129,16 @@ class IPartitioner {
* 2: [6,7,8,9,10] * 2: [6,7,8,9,10]
* 3: [11,12,13] * 3: [11,12,13]
* *
* \tparam ForwardIterator Type of the iterator. * \tparam RAI Type of the iterator.
*/ */
template<typename ForwardIterator> template<typename RAI>
class BlockSizePartitioner : IPartitioner < ForwardIterator > { class BlockSizePartitioner : IPartitioner < RAI > {
private: private:
ForwardIterator first; RAI first_;
ForwardIterator last; RAI last_;
size_t chunkSize; size_t chunk_size_;
size_t elements_count; size_t elements_count_;
size_t chunks; size_t chunks_;
public: public:
/** /**
...@@ -150,7 +150,7 @@ class BlockSizePartitioner : IPartitioner < ForwardIterator > { ...@@ -150,7 +150,7 @@ class BlockSizePartitioner : IPartitioner < ForwardIterator > {
* \param chunkSize (Optional) size of the chunk. * \param chunkSize (Optional) size of the chunk.
*/ */
BlockSizePartitioner( BlockSizePartitioner(
ForwardIterator first, ForwardIterator last, size_t chunkSize = 1); RAI first, RAI last, size_t chunkSize = 1);
/** /**
* See IPartitioner * See IPartitioner
...@@ -164,7 +164,7 @@ class BlockSizePartitioner : IPartitioner < ForwardIterator > { ...@@ -164,7 +164,7 @@ class BlockSizePartitioner : IPartitioner < ForwardIterator > {
* *
* \waitfree * \waitfree
*/ */
virtual const ChunkDescriptor<ForwardIterator> operator[]( virtual const ChunkDescriptor<RAI> operator[](
size_t const& index) const; size_t const& index) const;
}; };
...@@ -196,17 +196,17 @@ class BlockSizePartitioner : IPartitioner < ForwardIterator > { ...@@ -196,17 +196,17 @@ class BlockSizePartitioner : IPartitioner < ForwardIterator > {
* 4: [10,11] * 4: [10,11]
* 5: [12,13] * 5: [12,13]
* *
* \tparam ForwardIterator Type of the iterator. * \tparam RAI Type of the iterator.
*/ */
template<typename ForwardIterator> template<typename RAI>
class ChunkPartitioner : IPartitioner < ForwardIterator > { class ChunkPartitioner : IPartitioner < RAI > {
private: private:
size_t size; size_t size_;
size_t elements_count; size_t elements_count_;
ForwardIterator first; RAI first_;
ForwardIterator last; RAI last_;
size_t standard_chunk_size; size_t standard_chunk_size_;
size_t bigger_chunk_count; size_t bigger_chunk_count_;
public: public:
/** /**
...@@ -227,7 +227,7 @@ class ChunkPartitioner : IPartitioner < ForwardIterator > { ...@@ -227,7 +227,7 @@ class ChunkPartitioner : IPartitioner < ForwardIterator > {
* \param last The last. * \param last The last.
* \param amountChunks (Optional) the amount chunks. * \param amountChunks (Optional) the amount chunks.
*/ */
ChunkPartitioner(ForwardIterator first, ForwardIterator last, ChunkPartitioner(RAI first, RAI last,
size_t amountChunks = 0); size_t amountChunks = 0);
/** /**
...@@ -235,7 +235,7 @@ class ChunkPartitioner : IPartitioner < ForwardIterator > { ...@@ -235,7 +235,7 @@ class ChunkPartitioner : IPartitioner < ForwardIterator > {
* *
* \waitfree * \waitfree
*/ */
virtual const ChunkDescriptor<ForwardIterator> operator[]( virtual const ChunkDescriptor<RAI> operator[](
size_t const& index) const; size_t const& index) const;
}; };
......
...@@ -33,9 +33,15 @@ ...@@ -33,9 +33,15 @@
#include <vector> #include <vector>
#include <list> #include <list>
PartitionerTest::PartitionerTest() { PartitionerTest::PartitionerTest()
CreateUnit("algorithms partitioner test"). : partitioned_array_size_(16384) {
Add(&PartitionerTest::TestBasic, this); // Size of array to be partitioned should be power of 2
CreateUnit("TestBasic")
.Add(&PartitionerTest::TestBasic, this);
CreateUnit("TestLargeRange")
.Pre(&PartitionerTest::TestLargeRangePre, this)
.Add(&PartitionerTest::TestLargeRange, this)
.Post(&PartitionerTest::TestLargeRangePost, this);
} }
void PartitionerTest::TestBasic() { void PartitionerTest::TestBasic() {
...@@ -70,3 +76,62 @@ void PartitionerTest::TestBasic() { ...@@ -70,3 +76,62 @@ void PartitionerTest::TestBasic() {
PT_EXPECT_EQ_MSG(partitioner2.Size(), size_t(3), "Check count of partitions"); PT_EXPECT_EQ_MSG(partitioner2.Size(), size_t(3), "Check count of partitions");
} }
void PartitionerTest::TestLargeRangePre() {
partitioned_array_ = new int[partitioned_array_size_];
for (size_t i = 0; i < partitioned_array_size_; ++i) {
partitioned_array_[i] = static_cast<int>(i);
}
}
void PartitionerTest::TestLargeRangePost() {
delete[] partitioned_array_;
}
void PartitionerTest::TestLargeRange() {
// Test chunk partitioner with increasing number of chunks:
for (size_t num_chunks = 2;
num_chunks < partitioned_array_size_;
num_chunks *= 2) {
embb::algorithms::internal::ChunkPartitioner<int *>
chunk_partitioner(
partitioned_array_,
partitioned_array_ + partitioned_array_size_,
num_chunks);
int last_value_prev = -1;
PT_EXPECT_EQ(num_chunks, chunk_partitioner.Size());
// Iterate over chunks in partition:
for (size_t chunk = 0; chunk < chunk_partitioner.Size(); ++chunk) {
int first_value = *(chunk_partitioner[chunk].GetFirst());
int last_value = *(chunk_partitioner[chunk].GetLast() - 1);
PT_EXPECT_LT(first_value, last_value);
// Test seams between chunks: chunk[i].last + 1 == chunk[i+1].first
PT_EXPECT_EQ((last_value_prev + 1), first_value);
last_value_prev = last_value;
}
}
// Test block size partitioner with increasing chunk size:
for (size_t block_size = 1;
block_size < partitioned_array_size_;
block_size *= 2) {
embb::algorithms::internal::BlockSizePartitioner<int *>
chunk_partitioner(
partitioned_array_,
partitioned_array_ + partitioned_array_size_,
block_size);
int last_value_prev = -1;
// Iterate over chunks in partition:
for (size_t chunk = 0; chunk < chunk_partitioner.Size(); ++chunk) {
int first_value = *(chunk_partitioner[chunk].GetFirst());
int last_value = *(chunk_partitioner[chunk].GetLast() - 1);
if (block_size == 1) {
PT_EXPECT_EQ(first_value, last_value);
} else {
PT_EXPECT_LT(first_value, last_value);
}
// Test seams between chunks: chunk[i].last + 1 == chunk[i+1].first
PT_EXPECT_EQ((last_value_prev + 1), first_value);
last_value_prev = last_value;
}
}
}
...@@ -35,6 +35,13 @@ class PartitionerTest : public partest::TestCase { ...@@ -35,6 +35,13 @@ class PartitionerTest : public partest::TestCase {
private: private:
void TestBasic(); void TestBasic();
void TestLargeRangePre();
void TestLargeRangePost();
void TestLargeRange();
int * partitioned_array_;
size_t partitioned_array_size_;
}; };
#endif // ALGORITHMS_CPP_TEST_PARTITIONER_TEST_H_ #endif // ALGORITHMS_CPP_TEST_PARTITIONER_TEST_H_
...@@ -37,11 +37,19 @@ ...@@ -37,11 +37,19 @@
namespace embb { namespace embb {
namespace containers { namespace containers {
template<typename Type, class Allocator> template<typename Type, class Allocator>
WaitFreeSPSCQueue<Type, Allocator>::WaitFreeSPSCQueue(size_t capacity) : size_t WaitFreeSPSCQueue<Type, Allocator>::
capacity(capacity), AlignCapacityToPowerOfTwo(size_t capacity) {
head_index(0), size_t result = 1;
tail_index(0) { while (result < capacity) result <<= 1;
queue_array = allocator.allocate(capacity); return result;
}
template<typename Type, class Allocator>
WaitFreeSPSCQueue<Type, Allocator>::WaitFreeSPSCQueue(size_t capacity)
: capacity(AlignCapacityToPowerOfTwo(capacity)),
head_index(0),
tail_index(0) {
queue_array = allocator.allocate(this->capacity);
} }
template<typename Type, class Allocator> template<typename Type, class Allocator>
...@@ -51,7 +59,7 @@ size_t WaitFreeSPSCQueue<Type, Allocator>::GetCapacity() { ...@@ -51,7 +59,7 @@ size_t WaitFreeSPSCQueue<Type, Allocator>::GetCapacity() {
template<typename Type, class Allocator> template<typename Type, class Allocator>
bool WaitFreeSPSCQueue<Type, Allocator>::TryEnqueue(Type const & element) { bool WaitFreeSPSCQueue<Type, Allocator>::TryEnqueue(Type const & element) {
if (head_index - tail_index == capacity) if (tail_index - head_index == capacity)
return false; return false;
queue_array[tail_index % capacity] = element; queue_array[tail_index % capacity] = element;
......
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
* <td>\code{.cpp} Queue<Type>(capacity) \endcode</td> * <td>\code{.cpp} Queue<Type>(capacity) \endcode</td>
* <td>Nothing</td> * <td>Nothing</td>
* <td> * <td>
* Constructs a queue with capacity \c capacity that holds elements of * Constructs a queue with minimal capacity \c capacity that holds elements of
* type \c T. * type \c T.
* </td> * </td>
* </tr> * </tr>
...@@ -145,11 +145,17 @@ class WaitFreeSPSCQueue { ...@@ -145,11 +145,17 @@ class WaitFreeSPSCQueue {
*/ */
embb::base::Atomic<size_t> tail_index; embb::base::Atomic<size_t> tail_index;
/**
* Align capacity to the next smallest power of two
*/
static size_t AlignCapacityToPowerOfTwo(size_t capacity);
public: 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 <tt>capacity <= 2^k</tt> holds.
* *
* \notthreadsafe * \notthreadsafe
* *
......
...@@ -276,16 +276,43 @@ QueueTestSingleProducerSingleConsumer_ThreadMethod() { ...@@ -276,16 +276,43 @@ QueueTestSingleProducerSingleConsumer_ThreadMethod() {
template<typename Queue_t, bool MultipleProducers, bool MultipleConsumers> template<typename Queue_t, bool MultipleProducers, bool MultipleConsumers>
void QueueTest<Queue_t, MultipleProducers, MultipleConsumers>:: void QueueTest<Queue_t, MultipleProducers, MultipleConsumers>::
QueueTestSingleThreadEnqueueDequeue_ThreadMethod() { QueueTestSingleThreadEnqueueDequeue_ThreadMethod() {
// Enqueue the expected amount of elements
for (int i = 0; i != n_queue_size; ++i) { for (int i = 0; i != n_queue_size; ++i) {
bool success = queue->TryEnqueue(element_t(0, i * 133)); bool success = queue->TryEnqueue(element_t(0, i * 133));
PT_ASSERT(success == true); 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) { for (int i = 0; i != n_queue_size; ++i) {
element_t dequ(0, -1); element_t dequ(0, -1);
bool success = queue->TryDequeue(dequ); bool success = queue->TryDequeue(dequ);
PT_ASSERT(success == true); PT_ASSERT(success == true);
PT_ASSERT(dequ.second == i * 133); 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<typename Queue_t, bool MultipleProducers, bool MultipleConsumers> template<typename Queue_t, bool MultipleProducers, bool MultipleConsumers>
......
...@@ -41,7 +41,8 @@ extern "C" { ...@@ -41,7 +41,8 @@ extern "C" {
/* /*
* For each extension, follow this template * For each extension, follow this template
* /* cl_VEN_extname extension */ * // cl_VEN_extname extension
*/
/* #define cl_VEN_extname 1 /* #define cl_VEN_extname 1
* ... define new types, if any * ... define new types, if any
* ... define new tokens, if any * ... define new tokens, if any
......
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