diff --git a/include/etl/queue_spsc_isr.h b/include/etl/queue_spsc_isr.h index e4918ad7..c9f7c5fb 100644 --- a/include/etl/queue_spsc_isr.h +++ b/include/etl/queue_spsc_isr.h @@ -74,7 +74,7 @@ namespace etl /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. ///\param value The value to use to construct the item to push to the queue. //************************************************************************* -#if ETL_CPP11_SUPPORTED && !defined(ETL_STLPORT) && !ETL_QUEUE_ISR_FORCE_CPP03 +#if ETL_CPP11_SUPPORTED && !defined(ETL_STLPORT) && !defined(ETL_QUEUE_ISR_FORCE_CPP03) template bool emplace_from_isr(Args&&... args) { @@ -192,7 +192,7 @@ namespace etl return false; } -#if ETL_CPP11_SUPPORTED && !defined(ETL_STLPORT) && !ETL_QUEUE_ISR_FORCE_CPP03 +#if ETL_CPP11_SUPPORTED && !defined(ETL_STLPORT) && !defined(ETL_QUEUE_ISR_FORCE_CPP03) //************************************************************************* /// Constructs a value in the queue 'in place'. /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. @@ -430,7 +430,7 @@ namespace etl /// Constructs a value in the queue 'in place'. /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. //************************************************************************* -#if ETL_CPP11_SUPPORTED && !defined(ETL_STLPORT) && !ETL_QUEUE_ISR_FORCE_CPP03 +#if ETL_CPP11_SUPPORTED && !defined(ETL_STLPORT) && !defined(ETL_QUEUE_ISR_FORCE_CPP03) template bool emplace(Args&&... args) { diff --git a/include/etl/queue_spsc_locked.h b/include/etl/queue_spsc_locked.h new file mode 100644 index 00000000..d2c3a2c2 --- /dev/null +++ b/include/etl/queue_spsc_locked.h @@ -0,0 +1,681 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2019 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_SPSC_QUEUE_LOCKED_INCLUDED +#define ETL_SPSC_QUEUE_LOCKED_INCLUDED + +#include +#include + +#include "platform.h" +#include "alignment.h" +#include "parameter_type.h" +#include "memory_model.h" +#include "integral_limits.h" +#include "function.h" + +#undef ETL_FILE +#define ETL_FILE "46" + +namespace etl +{ + template + class queue_spsc_isr_base + { + protected: + + typedef typename etl::parameter_type::type parameter_t; + + public: + + /// The type used for determining the size of queue. + typedef typename etl::size_type_lookup::type size_type; + + typedef T value_type; ///< The type stored in the queue. + typedef T& reference; ///< A reference to the type used in the queue. + typedef const T& const_reference; ///< A const reference to the type used in the queue. + + //************************************************************************* + /// Push a value to the queue from an ISR. + //************************************************************************* + bool push_from_unlocked(parameter_t value) + { + return push_implementation(value); + } + + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + ///\param value The value to use to construct the item to push to the queue. + //************************************************************************* +#if ETL_CPP11_SUPPORTED && !defined(ETL_STLPORT) && !defined(ETL_QUEUE_LOCKED_FORCE_CPP03) + template + bool emplace_from_unlocked(Args&&... args) + { + return emplace_implementation(std::forward(args)...); + } +#endif + + //************************************************************************* + /// Pop a value from the queue from an ISR + //************************************************************************* + bool pop_from_unlocked(reference value) + { + return pop_implementation(value); + } + + //************************************************************************* + /// Pop a value from the queue from an ISR, and discard. + //************************************************************************* + bool pop_from_unlocked() + { + return pop_implementation(); + } + + //************************************************************************* + /// How much free space available in the queue. + /// Called from ISR. + //************************************************************************* + size_type available_from_unlocked() const + { + return MAX_SIZE - current_size; + } + + //************************************************************************* + /// Clear the queue from the ISR. + //************************************************************************* + void clear_from_unlocked() + { + while (pop_implementation()) + { + // Do nothing. + } + } + + //************************************************************************* + /// Is the queue empty? + /// Called from ISR. + //************************************************************************* + bool empty_from_unlocked() const + { + return (current_size == 0); + } + + //************************************************************************* + /// Is the queue full? + /// Called from ISR. + //************************************************************************* + bool full_from_unlocked() const + { + return (current_size == MAX_SIZE); + } + + //************************************************************************* + /// How many items in the queue? + /// Called from ISR. + //************************************************************************* + size_type size_from_unlocked() const + { + return current_size; + } + + //************************************************************************* + /// How many items can the queue hold. + //************************************************************************* + size_type capacity() const + { + return MAX_SIZE; + } + + //************************************************************************* + /// How many items can the queue hold. + //************************************************************************* + size_type max_size() const + { + return MAX_SIZE; + } + + protected: + + queue_spsc_isr_base(T* p_buffer_, size_type max_size_) + : p_buffer(p_buffer_), + write_index(0), + read_index(0), + current_size(0), + MAX_SIZE(max_size_) + { + } + + //************************************************************************* + /// Push a value to the queue. + //************************************************************************* + bool push_implementation(parameter_t value) + { + if (current_size != MAX_SIZE) + { + ::new (&p_buffer[write_index]) T(value); + + write_index = get_next_index(write_index, MAX_SIZE); + + ++current_size; + + return true; + } + + // Queue is full. + return false; + } + +#if ETL_CPP11_SUPPORTED && !defined(ETL_STLPORT) && !defined(ETL_QUEUE_LOCKED_FORCE_CPP03) + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + ///\param value The value to use to construct the item to push to the queue. + //************************************************************************* + template + bool emplace_implementation(Args&&... args) + { + if (current_size != MAX_SIZE) + { + ::new (&p_buffer[write_index]) T(std::forward(args)...); + + write_index = get_next_index(write_index, MAX_SIZE); + + ++current_size; + + return true; + } + + // Queue is full. + return false; + } +#else + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + //************************************************************************* + template + bool emplace_implementation(const T1& value1) + { + if (current_size != MAX_SIZE) + { + ::new (&p_buffer[write_index]) T(value1); + + write_index = get_next_index(write_index, MAX_SIZE); + + ++current_size; + + return true; + } + + // Queue is full. + return false; + } + + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + //************************************************************************* + template + bool emplace_implementation(const T1& value1, const T2& value2) + { + if (current_size != MAX_SIZE) + { + ::new (&p_buffer[write_index]) T(value1, value2); + + write_index = get_next_index(write_index, MAX_SIZE); + + ++current_size; + + return true; + } + + // Queue is full. + return false; + } + + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + //************************************************************************* + template + bool emplace_implementation(const T1& value1, const T2& value2, const T3& value3) + { + if (current_size != MAX_SIZE) + { + ::new (&p_buffer[write_index]) T(value1, value2, value3); + + write_index = get_next_index(write_index, MAX_SIZE); + + ++current_size; + + return true; + } + + // Queue is full. + return false; + } + + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + //************************************************************************* + template + bool emplace_implementation(const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + if (current_size != MAX_SIZE) + { + ::new (&p_buffer[write_index]) T(value1, value2, value3, value4); + + write_index = get_next_index(write_index, MAX_SIZE); + + ++current_size; + + return true; + } + + // Queue is full. + return false; + } + +#endif + + //************************************************************************* + /// Pop a value from the queue. + //************************************************************************* + bool pop_implementation(reference value) + { + if (current_size == 0) + { + // Queue is empty + return false; + } + + value = p_buffer[read_index]; + p_buffer[read_index].~T(); + + read_index = get_next_index(read_index, MAX_SIZE); + + --current_size; + + return true; + } + + //************************************************************************* + /// Pop a value from the queue and discard. + //************************************************************************* + bool pop_implementation() + { + if (current_size == 0) + { + // Queue is empty + return false; + } + + p_buffer[read_index].~T(); + + read_index = get_next_index(read_index, MAX_SIZE); + + --current_size; + + return true; + } + + //************************************************************************* + /// Calculate the next index. + //************************************************************************* + static size_type get_next_index(size_type index, size_type maximum) + { + ++index; + + if (index == maximum) + { + index = 0; + } + + return index; + } + + T* p_buffer; ///< The internal buffer. + size_type write_index; ///< Where to input new data. + size_type read_index; ///< Where to get the oldest data. + size_type current_size; ///< The current size of the queue. + const size_type MAX_SIZE; ///< The maximum number of items in the queue. + + private: + + //************************************************************************* + /// Destructor. + //************************************************************************* +#if defined(ETL_POLYMORPHIC_SPSC_QUEUE_ISR) || defined(ETL_POLYMORPHIC_CONTAINERS) + public: + virtual ~queue_spsc_isr_base() + { + } +#else + protected: + ~queue_spsc_isr_base() + { + } +#endif + }; + + //*************************************************************************** + ///\ingroup queue_spsc + ///\brief This is the base for all queue_spsc_isrs that contain a particular type. + ///\details Normally a reference to this type will be taken from a derived queue_spsc_locked. + /// This queue supports concurrent access by one producer and one consumer. + /// \tparam T The type of value that the queue_spsc_locked holds. + //*************************************************************************** + template + class iqueue_spsc_locked : public queue_spsc_isr_base + { + private: + + typedef queue_spsc_isr_base base_t; + typedef typename base_t::parameter_t parameter_t; + + public: + + typedef typename base_t::value_type value_type; ///< The type stored in the queue. + typedef typename base_t::reference reference; ///< A reference to the type used in the queue. + typedef typename base_t::const_reference const_reference; ///< A const reference to the type used in the queue. + typedef typename base_t::size_type size_type; ///< The type used for determining the size of the queue. + + //************************************************************************* + /// Push a value to the queue. + //************************************************************************* + bool push(parameter_t value) + { + lock(); + + bool result = this->push_implementation(value); + + unlock(); + + return result; + } + + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + //************************************************************************* +#if ETL_CPP11_SUPPORTED && !defined(ETL_STLPORT) && !defined(ETL_QUEUE_LOCKED_FORCE_CPP03) + template + bool emplace(Args&&... args) + { + lock(); + + bool result = this->emplace_implementation(std::forward(args)...); + + unlock(); + + return result; + } +#else + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + //************************************************************************* + template + bool emplace(const T1& value1) + { + lock(); + + bool result = this->emplace_implementation(value1); + + unlock(); + + return result; + } + + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + //************************************************************************* + template + bool emplace(const T1& value1, const T2& value2) + { + lock(); + + bool result = this->emplace_implementation(value1, value2); + + unlock(); + + return result; + } + + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + //************************************************************************* + template + bool emplace(const T1& value1, const T2& value2, const T3& value3) + { + lock(); + + bool result = this->emplace_implementation(value1, value2, value3); + + unlock(); + + return result; + } + + //************************************************************************* + /// Constructs a value in the queue 'in place'. + /// If asserts or exceptions are enabled, throws an etl::queue_full if the queue if already full. + //************************************************************************* + template + bool emplace(const T1& value1, const T2& value2, const T3& value3, const T4& value4) + { + lock(); + + bool result = this->emplace_implementation(value1, value2, value3, value4); + + unlock(); + + return result; + } +#endif + + //************************************************************************* + /// Pop a value from the queue. + //************************************************************************* + bool pop(reference value) + { + lock(); + + bool result = this->pop_implementation(value); + + unlock(); + + return result; + } + + //************************************************************************* + /// Pop a value from the queue and discard. + //************************************************************************* + bool pop() + { + lock(); + + bool result = this->pop_implementation(); + + unlock(); + + return result; + } + + //************************************************************************* + /// Clear the queue. + //************************************************************************* + void clear() + { + lock(); + + while (this->pop_implementation()) + { + // Do nothing. + } + + unlock(); + } + + //************************************************************************* + /// Is the queue empty? + //************************************************************************* + bool empty() const + { + lock(); + + size_type result = (this->current_size == 0); + + unlock(); + + return result; + } + + //************************************************************************* + /// Is the queue full? + //************************************************************************* + bool full() const + { + lock(); + + size_type result = (this->current_size == this->MAX_SIZE); + + unlock(); + + return result; + } + + //************************************************************************* + /// How many items in the queue? + //************************************************************************* + size_type size() const + { + lock(); + + size_type result = this->current_size; + + unlock(); + + return result; + } + + //************************************************************************* + /// How much free space available in the queue. + //************************************************************************* + size_type available() const + { + lock(); + + size_type result = this->MAX_SIZE - this->current_size; + + unlock(); + + return result; + } + + protected: + + //************************************************************************* + /// The constructor that is called from derived classes. + //************************************************************************* + iqueue_spsc_locked(T* p_buffer_, size_type max_size_, etl::ifunction& lock_, etl::ifunction& unlock_) + : base_t(p_buffer_, max_size_) + , lock(lock_) + , unlock(unlock_) + { + } + + private: + + // Disable copy construction and assignment. + iqueue_spsc_locked(const iqueue_spsc_locked&); + iqueue_spsc_locked& operator =(const iqueue_spsc_locked&); + + etl::ifunction& lock; ///< The callback that locks interrupts. + etl::ifunction& unlock; ///< The callback that unlocks interrupts. + }; + + //*************************************************************************** + ///\ingroup queue_spsc + /// A fixed capacity spsc queue. + /// This queue supports concurrent access by one producer and one consumer. + /// \tparam T The type this queue should support. + /// \tparam SIZE The maximum capacity of the queue. + /// \tparam MEMORY_MODEL The memory model for the queue. Determines the type of the internal counter variables. + //*************************************************************************** + template + class queue_spsc_locked : public etl::iqueue_spsc_locked + { + private: + + typedef etl::iqueue_spsc_locked base_t; + + public: + + typedef typename base_t::size_type size_type; + + ETL_STATIC_ASSERT((SIZE <= etl::integral_limits::max), "Size too large for memory model"); + + static const size_type MAX_SIZE = size_type(SIZE); + + //************************************************************************* + /// Default constructor. + //************************************************************************* + queue_spsc_locked(etl::ifunction& lock, + etl::ifunction& unlock) + : base_t(reinterpret_cast(&buffer[0]), MAX_SIZE, lock, unlock) + { + } + + //************************************************************************* + /// Destructor. + //************************************************************************* + ~queue_spsc_locked() + { + base_t::clear(); + } + + private: + + queue_spsc_locked(const queue_spsc_locked&); + queue_spsc_locked& operator = (const queue_spsc_locked&); + + /// The uninitialised buffer of T used in the queue_spsc_locked. + typename etl::aligned_storage::value>::type buffer[MAX_SIZE]; + }; +} + +#undef ETL_FILE + +#endif diff --git a/include/etl/version.h b/include/etl/version.h index d717855e..5e46f2bc 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -38,7 +38,7 @@ SOFTWARE. ///\ingroup utilities #define ETL_VERSION_MAJOR 14 -#define ETL_VERSION_MINOR 12 +#define ETL_VERSION_MINOR 13 #define ETL_VERSION_PATCH 0 #define ETL_VERSION ETL_STRINGIFY(ETL_VERSION_MAJOR) ETL_STRINGIFY(ETL_VERSION_MINOR) ETL_STRINGIFY(ETL_VERSION_PATCH) diff --git a/support/Release notes.txt b/support/Release notes.txt index e21a933b..a678831c 100644 --- a/support/Release notes.txt +++ b/support/Release notes.txt @@ -1,3 +1,7 @@ +=============================================================================== +14.13.0 +Changed API for etl::queue_spsc_isr + =============================================================================== 14.12.0 Enabled emplace functions for C++03. diff --git a/test/etl_profile.h b/test/etl_profile.h index 02e0861a..95880884 100644 --- a/test/etl_profile.h +++ b/test/etl_profile.h @@ -84,6 +84,7 @@ SOFTWARE. //#define ETL_QUEUE_FORCE_CPP03 //#define ETL_QUEUE_MPMC_MUTEX_FORCE_CPP03 //#define ETL_QUEUE_ISR_FORCE_CPP03 +//#define ETL_QUEUE_LOCKED_FORCE_CPP03 #ifdef _MSC_VER #include "etl/profiles/msvc_x86.h" diff --git a/test/test_queue_spsc_locked.cpp b/test/test_queue_spsc_locked.cpp new file mode 100644 index 00000000..60f4a31e --- /dev/null +++ b/test/test_queue_spsc_locked.cpp @@ -0,0 +1,628 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://www.etlcpp.com + +Copyright(c) 2019 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++.h" + +#include "etl/queue_spsc_locked.h" +#include "etl/function.h" + +#include +#include +#include + +#if defined(ETL_COMPILER_MICROSOFT) +#include +#endif + +#define REALTIME_TEST 0 + +namespace +{ + class Access + { + public: + + void clear() + { + called_lock = false; + called_unlock = false; + } + + void lock() + { + called_lock = true; + } + + void unlock() + { + called_unlock = true; + } + + bool called_lock; + bool called_unlock; + }; + + Access access; + + etl::function_imv lock; + etl::function_imv unlock; + + struct Data + { + Data(int a_, int b_ = 2, int c_ = 3, int d_ = 4) + : a(a_), + b(b_), + c(c_), + d(d_) + { + } + + Data() + : a(0), + b(0), + c(0), + d(0) + { + } + + int a; + int b; + int c; + int d; + }; + + bool operator ==(const Data& lhs, const Data& rhs) + { + return (lhs.a == rhs.a) && (lhs.b == rhs.b) && (lhs.c == rhs.c) && (lhs.d == rhs.d); + } + + SUITE(test_queue_locked) + { + //************************************************************************* + TEST(test_constructor) + { + access.clear(); + + etl::queue_spsc_locked queue(lock, unlock); + + CHECK_EQUAL(4U, queue.max_size()); + CHECK_EQUAL(4U, queue.capacity()); + + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + } + + //************************************************************************* + TEST(test_size_push_pop) + { + access.clear(); + + etl::queue_spsc_locked queue(lock, unlock); + + CHECK_EQUAL(0U, queue.size_from_unlocked()); + + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + access.clear(); + + CHECK_EQUAL(4U, queue.available_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + access.clear(); + + CHECK_EQUAL(0U, queue.size()); + + CHECK(access.called_lock); + CHECK(access.called_unlock); + + access.clear(); + + CHECK_EQUAL(4U, queue.available()); + + CHECK(access.called_lock); + CHECK(access.called_unlock); + + access.clear(); + + queue.push_from_unlocked(1); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, queue.size_from_unlocked()); + CHECK_EQUAL(3U, queue.available_from_unlocked()); + + access.clear(); + + queue.push(2); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + CHECK_EQUAL(2U, queue.available_from_unlocked()); + + access.clear(); + + queue.push(3); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, queue.size_from_unlocked()); + CHECK_EQUAL(1U, queue.available_from_unlocked()); + + access.clear(); + + queue.push(4); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(4U, queue.size_from_unlocked()); + CHECK_EQUAL(0U, queue.available_from_unlocked()); + + access.clear(); + + CHECK(!queue.push(5)); + CHECK(!queue.push_from_unlocked(5)); + + access.clear(); + + int i; + + CHECK(queue.pop(i)); + CHECK_EQUAL(1, i); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked(i)); + CHECK_EQUAL(2, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked(i)); + CHECK_EQUAL(3, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked(i)); + CHECK_EQUAL(4, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(0U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(!queue.pop(i)); + CHECK(!queue.pop_from_unlocked(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_iqueue) + { + access.clear(); + + etl::queue_spsc_locked queue(lock, unlock); + + etl::iqueue_spsc_locked& iqueue = queue; + + CHECK_EQUAL(0U, iqueue.size_from_unlocked()); + + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + access.clear(); + + CHECK_EQUAL(0U, iqueue.size()); + + CHECK(access.called_lock); + CHECK(access.called_unlock); + + access.clear(); + + iqueue.push_from_unlocked(1); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, iqueue.size_from_unlocked()); + + access.clear(); + + iqueue.push(2); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(2U, iqueue.size_from_unlocked()); + + access.clear(); + + iqueue.push(3); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, iqueue.size_from_unlocked()); + + access.clear(); + + iqueue.push(4); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(4U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(!iqueue.push(5)); + CHECK(!iqueue.push_from_unlocked(5)); + + access.clear(); + + int i; + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(1, i); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(iqueue.pop_from_unlocked(i)); + CHECK_EQUAL(2, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(2U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(iqueue.pop_from_unlocked(i)); + CHECK_EQUAL(3, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(iqueue.pop_from_unlocked(i)); + CHECK_EQUAL(4, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(0U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(!iqueue.pop(i)); + CHECK(!iqueue.pop_from_unlocked(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_void) + { + access.clear(); + + etl::queue_spsc_locked queue(lock, unlock); + + CHECK_EQUAL(0U, queue.size_from_unlocked()); + + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + access.clear(); + + CHECK_EQUAL(0U, queue.size()); + + CHECK(access.called_lock); + CHECK(access.called_unlock); + + access.clear(); + + queue.push_from_unlocked(1); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, queue.size_from_unlocked()); + + access.clear(); + + queue.push(2); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + + access.clear(); + + queue.push(3); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, queue.size_from_unlocked()); + + access.clear(); + + queue.push(4); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(4U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(!queue.push(5)); + CHECK(!queue.push_from_unlocked(5)); + + access.clear(); + + CHECK(queue.pop()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(0U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(!queue.pop()); + CHECK(!queue.pop_from_unlocked()); + } + + //************************************************************************* + TEST(test_multiple_emplace) + { + etl::queue_spsc_locked queue(lock, unlock); + + queue.emplace(1); + queue.emplace(1, 2); + queue.emplace(1, 2, 3); + queue.emplace(1, 2, 3, 4); + + CHECK_EQUAL(4U, queue.size()); + + Data popped; + + queue.pop(popped); + CHECK(popped == Data(1, 2, 3, 4)); + queue.pop(popped); + CHECK(popped == Data(1, 2, 3, 4)); + queue.pop(popped); + CHECK(popped == Data(1, 2, 3, 4)); + queue.pop(popped); + CHECK(popped == Data(1, 2, 3, 4)); + } + + //************************************************************************* + TEST(test_clear) + { + access.clear(); + + etl::queue_spsc_locked queue(lock, unlock); + + CHECK_EQUAL(0U, queue.size()); + + queue.push(1); + queue.push(2); + queue.clear(); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(0U, queue.size()); + + access.clear(); + + // Do it again to check that clear() didn't screw up the internals. + queue.push_from_unlocked(1); + queue.push_from_unlocked(2); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + queue.clear_from_unlocked(); + CHECK_EQUAL(0U, queue.size_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + } + + //************************************************************************* + TEST(test_empty) + { + access.clear(); + + etl::queue_spsc_locked queue(lock, unlock); + + CHECK(queue.empty()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + + queue.push(1); + + access.clear(); + + CHECK(!queue.empty()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + + queue.clear(); + access.clear(); + + CHECK(queue.empty_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + queue.push(1); + + access.clear(); + + CHECK(!queue.empty_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + } + + //************************************************************************* + TEST(test_full) + { + access.clear(); + + etl::queue_spsc_locked queue(lock, unlock); + + CHECK(!queue.full()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + + access.clear(); + + CHECK(queue.full()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + + queue.clear(); + access.clear(); + + CHECK(!queue.full_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + + access.clear(); + + CHECK(queue.full_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + } + + //========================================================================= +#if REALTIME_TEST && defined(ETL_COMPILER_MICROSOFT) + #if defined(ETL_TARGET_OS_WINDOWS) // Only Windows priority is currently supported + #define RAISE_THREAD_PRIORITY SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) + #define FIX_PROCESSOR_AFFINITY SetThreadAffinityMask(GetCurrentThread(), 1); + #else + #error No thread priority modifier defined + #endif + + size_t ticks = 0; + + struct ThreadLock + { + void lock() + { + mutex.lock(); + } + + void unlock() + { + mutex.unlock(); + } + + std::mutex mutex; + }; + + ThreadLock threadLock; + + etl::function_imv lock; + etl::function_imv unlock; + + etl::queue_spsc_locked queue(lock, unlock); + + const size_t LENGTH = 1000; + + void timer_thread() + { + RAISE_THREAD_PRIORITY; + FIX_PROCESSOR_AFFINITY; + + const size_t TICK = 1; + size_t tick = TICK; + ticks = 1; + + while (ticks <= LENGTH) + { + if (threadLock.mutex.try_lock()) + { + if (queue.push_from_unlocked(ticks)) + { + ++ticks; + } + + threadLock.mutex.unlock(); + } + + Sleep(0); + } + } + + TEST(queue_threads) + { + FIX_PROCESSOR_AFFINITY; + + std::vector tick_list; + tick_list.reserve(LENGTH); + + std::thread t1(timer_thread); + + while (tick_list.size() < LENGTH) + { + int i; + + if (queue.pop(i)) + { + tick_list.push_back(i); + } + } + + // Join the thread with the main thread + t1.join(); + + CHECK_EQUAL(LENGTH, tick_list.size()); + + for (size_t i = 0; i < LENGTH; ++i) + { + CHECK_EQUAL(i + 1, tick_list[i]); + } + } +#endif + }; +} diff --git a/test/test_queue_spsc_locked_small.cpp b/test/test_queue_spsc_locked_small.cpp new file mode 100644 index 00000000..30bedefb --- /dev/null +++ b/test/test_queue_spsc_locked_small.cpp @@ -0,0 +1,645 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2019 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++.h" + +#include "etl/queue_spsc_locked.h" + +#include +#include +#include + +#if defined(ETL_COMPILER_MICROSOFT) +#include +#endif + +#define REALTIME_TEST 0 + +namespace +{ + class Access + { + public: + + void clear() + { + called_lock = false; + called_unlock = false; + } + + void lock() + { + called_lock = true; + } + + void unlock() + { + called_unlock = true; + } + + bool called_lock; + bool called_unlock; + }; + + Access access; + + etl::function_imv lock; + etl::function_imv unlock; + + struct Data + { + Data(int a_, int b_ = 2, int c_ = 3, int d_ = 4) + : a(a_), + b(b_), + c(c_), + d(d_) + { + } + + Data() + : a(0), + b(0), + c(0), + d(0) + { + } + + int a; + int b; + int c; + int d; + }; + + bool operator ==(const Data& lhs, const Data& rhs) + { + return (lhs.a == rhs.a) && (lhs.b == rhs.b) && (lhs.c == rhs.c) && (lhs.d == rhs.d); + } + + typedef etl::queue_spsc_locked QueueInt; + typedef etl::iqueue_spsc_locked IQueueInt; + + typedef etl::queue_spsc_locked QueueInt255; + + SUITE(test_queue_locked) + { + //************************************************************************* + TEST(test_constructor) + { + access.clear(); + + QueueInt queue(lock, unlock); + + CHECK_EQUAL(4U, queue.max_size()); + CHECK_EQUAL(4U, queue.capacity()); + + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + } + + //************************************************************************* + TEST(test_size_push_pop) + { + access.clear(); + + QueueInt queue(lock, unlock); + + CHECK_EQUAL(0U, queue.size_from_unlocked()); + + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + access.clear(); + + CHECK_EQUAL(4U, queue.available_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + access.clear(); + + CHECK_EQUAL(0U, queue.size()); + + CHECK(access.called_lock); + CHECK(access.called_unlock); + + access.clear(); + + CHECK_EQUAL(4U, queue.available()); + + CHECK(access.called_lock); + CHECK(access.called_unlock); + + access.clear(); + + queue.push_from_unlocked(1); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, queue.size_from_unlocked()); + CHECK_EQUAL(3U, queue.available_from_unlocked()); + + access.clear(); + + queue.push(2); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + CHECK_EQUAL(2U, queue.available_from_unlocked()); + + access.clear(); + + queue.push(3); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, queue.size_from_unlocked()); + CHECK_EQUAL(1U, queue.available_from_unlocked()); + + access.clear(); + + queue.push(4); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(4U, queue.size_from_unlocked()); + CHECK_EQUAL(0U, queue.available_from_unlocked()); + + access.clear(); + + CHECK(!queue.push(5)); + CHECK(!queue.push_from_unlocked(5)); + + access.clear(); + + int i; + + CHECK(queue.pop(i)); + CHECK_EQUAL(1, i); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked(i)); + CHECK_EQUAL(2, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked(i)); + CHECK_EQUAL(3, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked(i)); + CHECK_EQUAL(4, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(0U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(!queue.pop(i)); + CHECK(!queue.pop_from_unlocked(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_iqueue) + { + access.clear(); + + QueueInt queue(lock, unlock); + + IQueueInt& iqueue = queue; + + CHECK_EQUAL(0U, iqueue.size_from_unlocked()); + + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + access.clear(); + + CHECK_EQUAL(0U, iqueue.size()); + + CHECK(access.called_lock); + CHECK(access.called_unlock); + + access.clear(); + + iqueue.push_from_unlocked(1); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, iqueue.size_from_unlocked()); + + access.clear(); + + iqueue.push(2); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(2U, iqueue.size_from_unlocked()); + + access.clear(); + + iqueue.push(3); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, iqueue.size_from_unlocked()); + + access.clear(); + + iqueue.push(4); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(4U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(!iqueue.push(5)); + CHECK(!iqueue.push_from_unlocked(5)); + + access.clear(); + + int i; + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(1, i); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(iqueue.pop_from_unlocked(i)); + CHECK_EQUAL(2, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(2U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(iqueue.pop_from_unlocked(i)); + CHECK_EQUAL(3, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(iqueue.pop_from_unlocked(i)); + CHECK_EQUAL(4, i); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(0U, iqueue.size_from_unlocked()); + + access.clear(); + + CHECK(!iqueue.pop(i)); + CHECK(!iqueue.pop_from_unlocked(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_void) + { + access.clear(); + + QueueInt queue(lock, unlock); + + CHECK_EQUAL(0U, queue.size_from_unlocked()); + + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + access.clear(); + + CHECK_EQUAL(0U, queue.size()); + + CHECK(access.called_lock); + CHECK(access.called_unlock); + + access.clear(); + + queue.push_from_unlocked(1); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, queue.size_from_unlocked()); + + access.clear(); + + queue.push(2); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + + access.clear(); + + queue.push(3); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, queue.size_from_unlocked()); + + access.clear(); + + queue.push(4); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(4U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(!queue.push(5)); + CHECK(!queue.push_from_unlocked(5)); + + access.clear(); + + CHECK(queue.pop()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(3U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(1U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(queue.pop_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + CHECK_EQUAL(0U, queue.size_from_unlocked()); + + access.clear(); + + CHECK(!queue.pop()); + CHECK(!queue.pop_from_unlocked()); + } + + //************************************************************************* + TEST(test_push_255) + { + QueueInt255 queue(lock, unlock); + + for (int i = 0; i < 255; ++i) + { + queue.push(i); + } + + CHECK_EQUAL(255U, queue.size()); + } + + //************************************************************************* + TEST(test_multiple_emplace) + { + etl::queue_spsc_locked queue(lock, unlock); + + queue.emplace(1); + queue.emplace(1, 2); + queue.emplace(1, 2, 3); + queue.emplace(1, 2, 3, 4); + + CHECK_EQUAL(4U, queue.size()); + + Data popped; + + queue.pop(popped); + CHECK(popped == Data(1, 2, 3, 4)); + queue.pop(popped); + CHECK(popped == Data(1, 2, 3, 4)); + queue.pop(popped); + CHECK(popped == Data(1, 2, 3, 4)); + queue.pop(popped); + CHECK(popped == Data(1, 2, 3, 4)); + } + + //************************************************************************* + TEST(test_clear) + { + access.clear(); + + QueueInt queue(lock, unlock); + + CHECK_EQUAL(0U, queue.size()); + + queue.push(1); + queue.push(2); + queue.clear(); + CHECK(access.called_lock); + CHECK(access.called_unlock); + CHECK_EQUAL(0U, queue.size()); + + access.clear(); + + // Do it again to check that clear() didn't screw up the internals. + queue.push_from_unlocked(1); + queue.push_from_unlocked(2); + CHECK_EQUAL(2U, queue.size_from_unlocked()); + queue.clear_from_unlocked(); + CHECK_EQUAL(0U, queue.size_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + } + + //************************************************************************* + TEST(test_empty) + { + access.clear(); + + QueueInt queue(lock, unlock); + + CHECK(queue.empty()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + + queue.push(1); + + access.clear(); + + CHECK(!queue.empty()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + + queue.clear(); + access.clear(); + + CHECK(queue.empty_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + queue.push(1); + + access.clear(); + + CHECK(!queue.empty_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + } + + //************************************************************************* + TEST(test_full) + { + access.clear(); + + QueueInt queue(lock, unlock); + + CHECK(!queue.full()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + + access.clear(); + + CHECK(queue.full()); + CHECK(access.called_lock); + CHECK(access.called_unlock); + + queue.clear(); + access.clear(); + + CHECK(!queue.full_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + + access.clear(); + + CHECK(queue.full_from_unlocked()); + CHECK(!access.called_lock); + CHECK(!access.called_unlock); + } + + //========================================================================= +#if REALTIME_TEST && defined(ETL_COMPILER_MICROSOFT) + #if defined(ETL_TARGET_OS_WINDOWS) // Only Windows priority is currently supported + #define RAISE_THREAD_PRIORITY SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) + #define FIX_PROCESSOR_AFFINITY SetThreadAffinityMask(GetCurrentThread(), 1); + #else + #error No thread priority modifier defined + #endif + + size_t ticks = 0; + + struct ThreadLock + { + void lock() + { + mutex.lock(); + } + + void unlock() + { + mutex.unlock(); + } + + std::mutex mutex; + }; + + ThreadLock threadLock; + + etl::function_imv lock; + etl::function_imv unlock; + + etl::queue_spsc_locked queue(lock, unlock); + + const size_t LENGTH = 1000; + + void timer_thread() + { + RAISE_THREAD_PRIORITY; + FIX_PROCESSOR_AFFINITY; + + const size_t TICK = 1; + size_t tick = TICK; + ticks = 1; + + while (ticks <= LENGTH) + { + if (threadLock.mutex.try_lock()) + { + if (queue.push_from_unlocked(ticks)) + { + ++ticks; + } + + threadLock.mutex.unlock(); + } + + Sleep(0); + } + } + + TEST(queue_threads) + { + FIX_PROCESSOR_AFFINITY; + + std::vector tick_list; + tick_list.reserve(LENGTH); + + std::thread t1(timer_thread); + + while (tick_list.size() < LENGTH) + { + int i; + + if (queue.pop(i)) + { + tick_list.push_back(i); + } + } + + // Join the thread with the main thread + t1.join(); + + CHECK_EQUAL(LENGTH, tick_list.size()); + + for (size_t i = 0; i < LENGTH; ++i) + { + CHECK_EQUAL(i + 1, tick_list[i]); + } + } +#endif + }; +} diff --git a/test/vs2017/etl.vcxproj b/test/vs2017/etl.vcxproj index 113befae..d01f21d8 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -377,6 +377,7 @@ + @@ -720,6 +721,8 @@ + + true true diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index 170e9085..bb822b22 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -729,6 +729,9 @@ ETL\Maths + + ETL\Containers + @@ -1154,6 +1157,12 @@ Source Files + + Source Files + + + Source Files +