diff --git a/include/etl/memory_model.h b/include/etl/memory_model.h index 07bfc73f..5b474597 100644 --- a/include/etl/memory_model.h +++ b/include/etl/memory_model.h @@ -32,14 +32,44 @@ SOFTWARE. #define ETL_MEMORY_MODEL_INCLUDED #include "user_type.h" +#include +#include "type_lookup.h" namespace etl { ETL_DECLARE_USER_TYPE(memory_model, int) - ETL_USER_TYPE(MM_SMALL, 0) - ETL_USER_TYPE(MM_MEDIUM, 1) - ETL_USER_TYPE(MM_LARGE, 2) + ETL_USER_TYPE(MEMORY_MODEL_SMALL, 0) + ETL_USER_TYPE(MEMORY_MODEL_MEDIUM, 1) + ETL_USER_TYPE(MEMORY_MODEL_LARGE, 2) + ETL_USER_TYPE(MEMORY_MODEL_HUGE, 3) ETL_END_USER_TYPE(memory_model) + + template + struct size_type_lookup; + + template <> + struct size_type_lookup + { + typedef uint_least8_t type; + }; + + template <> + struct size_type_lookup + { + typedef uint_least16_t type; + }; + + template <> + struct size_type_lookup + { + typedef uint_least32_t type; + }; + + template <> + struct size_type_lookup + { + typedef uint_least64_t type; + }; } #endif diff --git a/include/etl/priority_queue.h b/include/etl/priority_queue.h index 34bf15a3..02eb6922 100644 --- a/include/etl/priority_queue.h +++ b/include/etl/priority_queue.h @@ -319,7 +319,7 @@ namespace etl /// Returns the remaining capacity. ///\return The remaining capacity. //************************************************************************* - size_t available() const + size_type available() const { return container.max_size() - container.size(); } @@ -372,7 +372,9 @@ namespace etl { public: - static const size_t MAX_SIZE = SIZE; + typedef typename TContainer::size_type size_type; + + static const size_type MAX_SIZE = size_type(SIZE); //************************************************************************* /// Default constructor. diff --git a/include/etl/queue.h b/include/etl/queue.h index 4a533d8c..fdf4da19 100644 --- a/include/etl/queue.h +++ b/include/etl/queue.h @@ -43,6 +43,8 @@ SOFTWARE. #include "debug_count.h" #include "type_traits.h" #include "parameter_type.h" +#include "memory_model.h" +#include "integral_limits.h" #undef ETL_FILE #define ETL_FILE "13" @@ -102,11 +104,13 @@ namespace etl /// The base class for all queues. ///\ingroup queue //*************************************************************************** + template class queue_base { public: - typedef size_t size_type; ///< The type used for determining the size of queue. + /// The type used for determining the size of queue. + typedef typename etl::size_type_lookup::type size_type; //************************************************************************* /// Returns the current number of items in the queue. @@ -154,7 +158,7 @@ namespace etl /// Returns the remaining capacity. ///\return The remaining capacity. //************************************************************************* - size_t available() const + size_type available() const { return max_size() - size(); } @@ -217,8 +221,8 @@ namespace etl ETL_RESET_DEBUG_COUNT; } - size_type in; ///< Where to input new data. - size_type out; ///< Where to get the oldest data. + size_type in; ///< Where to input new data. + size_type out; ///< Where to get the oldest data. size_type current_size; ///< The number of items in the queue. const size_type CAPACITY; ///< The maximum number of items in the queue. ETL_DECLARE_DEBUG_COUNT; ///< For internal debugging purposes. @@ -236,25 +240,32 @@ namespace etl /// \warning This queue cannot be used for concurrent access from multiple threads. /// \tparam T The type of value that the queue holds. //*************************************************************************** - template - class iqueue : public etl::queue_base + template + class iqueue : public etl::queue_base { - public: - - 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. - typedef T* pointer; ///< A pointer to the type used in the queue. - typedef const T* const_pointer; ///< A const pointer to the type used in the queue. - typedef queue_base::size_type size_type; ///< The type used for determining the size of the queue. - private: - typedef typename etl::parameter_type::type parameter_t; - typedef typename etl::queue_base base_t; + typedef typename etl::parameter_type::type parameter_t; + typedef typename etl::queue_base base_t; public: + 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. + typedef T* pointer; ///< A pointer to the type used in the queue. + typedef const T* const_pointer; ///< A const pointer 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. + + using base_t::in; + using base_t::out; + using base_t::CAPACITY; + using base_t::current_size; + using base_t::full; + using base_t::empty; + using base_t::add_in; + using base_t::del_out; + //************************************************************************* /// Gets a reference to the value at the front of the queue.
/// \return A reference to the value at the front of the queue. @@ -302,7 +313,7 @@ namespace etl ETL_ASSERT(!full(), ETL_ERROR(queue_full)); #endif ::new (&p_buffer[in]) T(value); - base_t::add_in(); + add_in(); } //************************************************************************* @@ -319,7 +330,7 @@ namespace etl #if defined(ETL_CHECK_PUSH_POP) ETL_ASSERT(!full(), ETL_ERROR(queue_full)); #endif - base_t::add_in(); + add_in(); return p_buffer[next]; } @@ -336,7 +347,7 @@ namespace etl ETL_ASSERT(!full(), ETL_ERROR(queue_full)); #endif ::new (&p_buffer[in]) T(value1); - base_t::add_in(); + add_in(); } //************************************************************************* @@ -351,7 +362,7 @@ namespace etl ETL_ASSERT(!full(), ETL_ERROR(queue_full)); #endif ::new (&p_buffer[in]) T(value1, value2); - base_t::add_in(); + add_in(); } //************************************************************************* @@ -366,7 +377,7 @@ namespace etl ETL_ASSERT(!full(), ETL_ERROR(queue_full)); #endif ::new (&p_buffer[in]) T(value1, value2, value3); - base_t::add_in(); + add_in(); } //************************************************************************* @@ -381,7 +392,7 @@ namespace etl ETL_ASSERT(!full(), ETL_ERROR(queue_full)); #endif ::new (&p_buffer[in]) T(value1, value2, value3, value4); - base_t::add_in(); + add_in(); } //************************************************************************* @@ -398,7 +409,7 @@ namespace etl while (current_size > 0) { p_buffer[out].~T(); - base_t::del_out(); + del_out(); } in = 0; @@ -417,7 +428,7 @@ namespace etl ETL_ASSERT(!empty(), ETL_ERROR(queue_empty)); #endif p_buffer[out].~T(); - base_t::del_out(); + del_out(); } //************************************************************************* @@ -466,9 +477,9 @@ namespace etl { clear(); - size_t index = other.out; + size_type index = other.out; - for (size_t i = 0; i < other.size(); ++i) + for (size_type i = 0; i < other.size(); ++i) { push(other.p_buffer[index]); index = (index == (CAPACITY - 1)) ? 0 : index + 1; @@ -479,7 +490,7 @@ namespace etl /// The constructor that is called from derived classes. //************************************************************************* iqueue(T* p_buffer_, size_type max_size_) - : queue_base(max_size_), + : base_t(max_size_), p_buffer(p_buffer_) { } @@ -511,21 +522,30 @@ namespace etl ///\ingroup queue /// A fixed capacity queue. /// This queue does not support concurrent access by different threads. - /// \tparam T The type this queue should support. - /// \tparam SIZE The maximum capacity of the queue. + /// \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 : public etl::iqueue + template + class queue : public etl::iqueue { + private: + + typedef etl::iqueue base_t; + public: - static const size_t MAX_SIZE = SIZE; + 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() - : etl::iqueue(reinterpret_cast(&buffer[0]), SIZE) + : base_t(reinterpret_cast(&buffer[0]), SIZE) { } @@ -533,9 +553,9 @@ namespace etl /// Copy constructor //************************************************************************* queue(const queue& rhs) - : etl::iqueue(reinterpret_cast(&buffer[0]), SIZE) + : base_t(reinterpret_cast(&buffer[0]), SIZE) { - etl::iqueue::clone(rhs); + base_t::clone(rhs); } //************************************************************************* @@ -543,7 +563,7 @@ namespace etl //************************************************************************* ~queue() { - etl::iqueue::clear(); + base_t::clear(); } //************************************************************************* @@ -553,7 +573,7 @@ namespace etl { if (&rhs != this) { - etl::iqueue::clone(rhs); + base_t::clone(rhs); } return *this; diff --git a/include/etl/queue_mpmc_mutex.h b/include/etl/queue_mpmc_mutex.h index f6951406..16ec5e53 100644 --- a/include/etl/queue_mpmc_mutex.h +++ b/include/etl/queue_mpmc_mutex.h @@ -38,20 +38,26 @@ SOFTWARE. #include "alignment.h" #include "parameter_type.h" #include "mutex.h" +#include "memory_model.h" +#include "integral_limits.h" #undef ETL_FILE #define ETL_FILE "48" namespace etl { + template class queue_mpmc_mutex_base { public: + /// The type used for determining the size of queue. + typedef typename etl::size_type_lookup::type size_type; + //************************************************************************* /// How many items can the queue hold. //************************************************************************* - size_t capacity() const + size_type capacity() const { return MAX_SIZE; } @@ -59,14 +65,14 @@ namespace etl //************************************************************************* /// How many items can the queue hold. //************************************************************************* - size_t max_size() const + size_type max_size() const { return MAX_SIZE; } protected: - queue_mpmc_mutex_base(size_t max_size_) + queue_mpmc_mutex_base(size_type max_size_) : write_index(0), read_index(0), current_size(0), @@ -77,7 +83,7 @@ namespace etl //************************************************************************* /// Calculate the next index. //************************************************************************* - static size_t get_next_index(size_t index, size_t maximum) + static size_type get_next_index(size_type index, size_type maximum) { ++index; @@ -89,10 +95,10 @@ namespace etl return index; } - size_t write_index; ///< Where to input new data. - size_t read_index; ///< Where to get the oldest data. - size_t current_size; ///< The current size of the queue. - const size_t MAX_SIZE; ///< The maximum number of items in the queue. + 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. //************************************************************************* /// Destructor. @@ -121,19 +127,26 @@ namespace etl /// This queue supports concurrent access by one producer and one consumer. /// \tparam T The type of value that the queue_mpmc_mutex holds. //*************************************************************************** - template - class iqueue_mpmc_mutex : public queue_mpmc_mutex_base + template + class iqueue_mpmc_mutex : public queue_mpmc_mutex_base { - protected: + private: - typedef typename etl::parameter_type::type parameter_t; + typedef typename etl::parameter_type::type parameter_t; + typedef etl::queue_mpmc_mutex_base base_t; public: - 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. - typedef size_t size_type; ///< The type used for determining the size of the queue. + 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. + typedef typename base_t::size_type size_type; ///< The type used for determining the size of the queue. + + using base_t::write_index; + using base_t::read_index; + using base_t::current_size; + using base_t::MAX_SIZE; + using base_t::get_next_index; //************************************************************************* /// Push a value to the queue. @@ -199,7 +212,7 @@ namespace etl { access.lock(); - size_t result = (current_size == 0); + size_type result = (current_size == 0); access.unlock(); @@ -213,7 +226,7 @@ namespace etl { access.lock(); - size_t result = (current_size == MAX_SIZE); + size_type result = (current_size == MAX_SIZE); access.unlock(); @@ -223,11 +236,11 @@ namespace etl //************************************************************************* /// How many items in the queue? //************************************************************************* - size_t size() const + size_type size() const { access.lock(); - size_t result = current_size; + size_type result = current_size; access.unlock(); @@ -237,11 +250,11 @@ namespace etl //************************************************************************* /// How much free space available in the queue. //************************************************************************* - size_t available() const + size_type available() const { access.lock(); - size_t result = MAX_SIZE - current_size; + size_type result = MAX_SIZE - current_size; access.unlock(); @@ -254,7 +267,7 @@ namespace etl /// The constructor that is called from derived classes. //************************************************************************* iqueue_mpmc_mutex(T* p_buffer_, size_type max_size_) - : queue_mpmc_mutex_base(max_size_), + : base_t(max_size_), p_buffer(p_buffer_) { } @@ -335,17 +348,24 @@ namespace etl ///\ingroup queue_mpmc /// A fixed capacity mpmc 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 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_mpmc_mutex : public etl::iqueue_mpmc_mutex + template + class queue_mpmc_mutex : public etl::iqueue_mpmc_mutex { - typedef etl::iqueue_mpmc_mutex base_t; + private: + + typedef etl::iqueue_mpmc_mutex base_t; public: - static const size_t MAX_SIZE = SIZE; + 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. diff --git a/include/etl/queue_spsc_atomic.h b/include/etl/queue_spsc_atomic.h index dfe32fc0..44eeb918 100644 --- a/include/etl/queue_spsc_atomic.h +++ b/include/etl/queue_spsc_atomic.h @@ -38,16 +38,22 @@ SOFTWARE. #include "alignment.h" #include "parameter_type.h" #include "atomic.h" +#include "memory_model.h" +#include "integral_limits.h" #undef ETL_FILE #define ETL_FILE "47" namespace etl { + template class queue_spsc_atomic_base { public: + /// The type used for determining the size of queue. + typedef typename etl::size_type_lookup::type size_type; + //************************************************************************* /// Is the queue empty? /// Accurate from the 'pop' thread. @@ -65,7 +71,7 @@ namespace etl //************************************************************************* bool full() const { - size_t next_index = get_next_index(write.load(etl::memory_order_acquire), RESERVED); + size_type next_index = get_next_index(write.load(etl::memory_order_acquire), RESERVED); return (next_index == read.load(etl::memory_order_acquire)); } @@ -74,12 +80,12 @@ namespace etl /// How many items in the queue? /// Due to concurrency, this is a guess. //************************************************************************* - size_t size() const + size_type size() const { - size_t write_index = write.load(etl::memory_order_acquire); - size_t read_index = read.load(etl::memory_order_acquire); + size_type write_index = write.load(etl::memory_order_acquire); + size_type read_index = read.load(etl::memory_order_acquire); - size_t n; + size_type n; if (write_index >= read_index) { @@ -97,7 +103,7 @@ namespace etl /// How much free space available in the queue. /// Due to concurrency, this is a guess. //************************************************************************* - size_t available() const + size_type available() const { return RESERVED - size() - 1; } @@ -105,7 +111,7 @@ namespace etl //************************************************************************* /// How many items can the queue hold. //************************************************************************* - size_t capacity() const + size_type capacity() const { return RESERVED - 1; } @@ -113,14 +119,14 @@ namespace etl //************************************************************************* /// How many items can the queue hold. //************************************************************************* - size_t max_size() const + size_type max_size() const { return RESERVED - 1; } protected: - queue_spsc_atomic_base(size_t reserved_) + queue_spsc_atomic_base(size_type reserved_) : write(0), read(0), RESERVED(reserved_) @@ -130,7 +136,7 @@ namespace etl //************************************************************************* /// Calculate the next index. //************************************************************************* - static size_t get_next_index(size_t index, size_t maximum) + static size_type get_next_index(size_type index, size_type maximum) { ++index; @@ -142,9 +148,9 @@ namespace etl return index; } - etl::atomic_size_t write; ///< Where to input new data. - etl::atomic_size_t read; ///< Where to get the oldest data. - const size_t RESERVED; ///< The maximum number of items in the queue. + etl::atomic write; ///< Where to input new data. + etl::atomic read; ///< Where to get the oldest data. + const size_type RESERVED; ///< The maximum number of items in the queue. private: @@ -163,7 +169,7 @@ namespace etl } #endif }; - + //*************************************************************************** ///\ingroup queue_spsc_atomic ///\brief This is the base for all queue_spscs that contain a particular type. @@ -175,27 +181,33 @@ namespace etl /// This queue supports concurrent access by one producer and one consumer. /// \tparam T The type of value that the queue_spsc_atomic holds. //*************************************************************************** - template - class iqueue_spsc_atomic : public queue_spsc_atomic_base + template + class iqueue_spsc_atomic : public queue_spsc_atomic_base { private: - typedef typename etl::parameter_type::type parameter_t; + typedef typename etl::parameter_type::type parameter_t; + typedef typename etl::queue_spsc_atomic_base base_t; public: - 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. - typedef size_t size_type; ///< The type used for determining the size of the queue. + 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. + typedef typename base_t::size_type size_type; ///< The type used for determining the size of the queue. + + using base_t::write; + using base_t::read; + using base_t::RESERVED; + using base_t::get_next_index; //************************************************************************* /// Push a value to the queue. //************************************************************************* bool push(parameter_t value) { - size_t write_index = write.load(etl::memory_order_relaxed); - size_t next_index = get_next_index(write_index, RESERVED); + size_type write_index = write.load(etl::memory_order_relaxed); + size_type next_index = get_next_index(write_index, RESERVED); if (next_index != read.load(etl::memory_order_acquire)) { @@ -205,7 +217,7 @@ namespace etl return true; } - + // Queue is full. return false; } @@ -214,16 +226,16 @@ namespace etl /// Pop a value from the queue. //************************************************************************* bool pop(reference value) - { - size_t read_index = read.load(etl::memory_order_relaxed); - - if (read_index == write.load(etl::memory_order_acquire)) + { + size_type read_index = read.load(etl::memory_order_relaxed); + + if (read_index == write.load(etl::memory_order_acquire)) { // Queue is empty return false; } - size_t next_index = get_next_index(read_index, RESERVED); + size_type next_index = get_next_index(read_index, RESERVED); value = p_buffer[read_index]; p_buffer[read_index].~T(); @@ -238,7 +250,7 @@ namespace etl //************************************************************************* bool pop() { - size_t read_index = read.load(etl::memory_order_relaxed); + size_type read_index = read.load(etl::memory_order_relaxed); if (read_index == write.load(etl::memory_order_acquire)) { @@ -246,7 +258,7 @@ namespace etl return false; } - size_t next_index = get_next_index(read_index, RESERVED); + size_type next_index = get_next_index(read_index, RESERVED); p_buffer[read_index].~T(); @@ -274,7 +286,7 @@ namespace etl /// The constructor that is called from derived classes. //************************************************************************* iqueue_spsc_atomic(T* p_buffer_, size_type reserved_) - : queue_spsc_atomic_base(reserved_), + : base_t(reserved_), p_buffer(p_buffer_) { } @@ -292,19 +304,30 @@ namespace etl ///\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 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_atomic : public iqueue_spsc_atomic + template + class queue_spsc_atomic : public iqueue_spsc_atomic { - typedef etl::iqueue_spsc_atomic base_t; + private: - static const size_t RESERVED_SIZE = SIZE + 1; + typedef typename etl::iqueue_spsc_atomic base_t; public: - static const size_t MAX_SIZE = SIZE; + typedef typename base_t::size_type size_type; + + private: + + static const size_type RESERVED_SIZE = size_type(SIZE + 1); + + public: + + ETL_STATIC_ASSERT((SIZE <= (etl::integral_limits::max - 1)), "Size too large for memory model"); + + static const size_type MAX_SIZE = size_type(SIZE); //************************************************************************* /// Default constructor. @@ -329,4 +352,4 @@ namespace etl }; }; -#endif \ No newline at end of file +#endif diff --git a/include/etl/queue_spsc_isr.h b/include/etl/queue_spsc_isr.h index 2b89603d..dc5922cb 100644 --- a/include/etl/queue_spsc_isr.h +++ b/include/etl/queue_spsc_isr.h @@ -37,13 +37,15 @@ SOFTWARE. #include "platform.h" #include "alignment.h" #include "parameter_type.h" +#include "memory_model.h" +#include "integral_limits.h" #undef ETL_FILE #define ETL_FILE "46" namespace etl { - template + template class queue_spsc_isr_base { protected: @@ -52,10 +54,12 @@ namespace etl 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. - typedef size_t size_type; ///< The type used for determining the size of the queue. //************************************************************************* /// Push a value to the queue from an ISR. @@ -85,7 +89,7 @@ namespace etl /// How much free space available in the queue. /// Called from ISR. //************************************************************************* - size_t available_from_isr() const + size_type available_from_isr() const { return MAX_SIZE - current_size; } @@ -123,7 +127,7 @@ namespace etl /// How many items in the queue? /// Called from ISR. //************************************************************************* - size_t size_from_isr() const + size_type size_from_isr() const { return current_size; } @@ -131,7 +135,7 @@ namespace etl //************************************************************************* /// How many items can the queue hold. //************************************************************************* - size_t capacity() const + size_type capacity() const { return MAX_SIZE; } @@ -139,7 +143,7 @@ namespace etl //************************************************************************* /// How many items can the queue hold. //************************************************************************* - size_t max_size() const + size_type max_size() const { return MAX_SIZE; } @@ -219,7 +223,7 @@ namespace etl //************************************************************************* /// Calculate the next index. //************************************************************************* - static size_t get_next_index(size_t index, size_t maximum) + static size_type get_next_index(size_type index, size_type maximum) { ++index; @@ -266,19 +270,20 @@ namespace etl /// This queue supports concurrent access by one producer and one consumer. /// \tparam T The type of value that the queue_spsc_isr holds. //*************************************************************************** - template - class iqueue_spsc_isr : public queue_spsc_isr_base + template + class iqueue_spsc_isr : public queue_spsc_isr_base { private: - typedef typename queue_spsc_isr_base::parameter_t parameter_t; + typedef queue_spsc_isr_base base_t; + typedef typename base_t::parameter_t parameter_t; public: - typedef typename queue_spsc_isr_base::value_type value_type; ///< The type stored in the queue. - typedef typename queue_spsc_isr_base::reference reference; ///< A reference to the type used in the queue. - typedef typename queue_spsc_isr_base::const_reference const_reference; ///< A const reference to the type used in the queue. - typedef typename queue_spsc_isr_base::size_type size_type; ///< The type used for determining the size of the queue. + 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. @@ -344,7 +349,7 @@ namespace etl { TAccess::lock(); - size_t result = (this->current_size == 0); + size_type result = (this->current_size == 0); TAccess::unlock(); @@ -358,7 +363,7 @@ namespace etl { TAccess::lock(); - size_t result = (this->current_size == this->MAX_SIZE); + size_type result = (this->current_size == this->MAX_SIZE); TAccess::unlock(); @@ -368,11 +373,11 @@ namespace etl //************************************************************************* /// How many items in the queue? //************************************************************************* - size_t size() const + size_type size() const { TAccess::lock(); - size_t result = this->current_size; + size_type result = this->current_size; TAccess::unlock(); @@ -382,11 +387,11 @@ namespace etl //************************************************************************* /// How much free space available in the queue. //************************************************************************* - size_t available() const + size_type available() const { TAccess::lock(); - size_t result = this->MAX_SIZE - this->current_size; + size_type result = this->MAX_SIZE - this->current_size; TAccess::unlock(); @@ -399,7 +404,7 @@ namespace etl /// The constructor that is called from derived classes. //************************************************************************* iqueue_spsc_isr(T* p_buffer_, size_type max_size_) - : queue_spsc_isr_base(p_buffer_, max_size_) + : base_t(p_buffer_, max_size_) { } @@ -416,18 +421,25 @@ namespace etl ///\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 TAccess The type that will lock and unlock interrupts. + /// \tparam T The type this queue should support. + /// \tparam SIZE The maximum capacity of the queue. + /// \tparam TAccess The type that will lock and unlock interrupts. + /// \tparam MEMORY_MODEL The memory model for the queue. Determines the type of the internal counter variables. //*************************************************************************** - template - class queue_spsc_isr : public etl::iqueue_spsc_isr + template + class queue_spsc_isr : public etl::iqueue_spsc_isr { - typedef etl::iqueue_spsc_isr base_t; + private: + + typedef etl::iqueue_spsc_isr base_t; public: - static const size_t MAX_SIZE = SIZE; + 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. diff --git a/include/etl/version.h b/include/etl/version.h index 1b6c4836..531de754 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -37,13 +37,13 @@ SOFTWARE. /// Definitions of the ETL version ///\ingroup utilities -#define ETL_VERSION "11.14.3" -#define ETL_VERSION_W L"11.14.3" -#define ETL_VERSION_U16 u"11.14.3" -#define ETL_VERSION_U32 U"11.14.3" +#define ETL_VERSION "11.15.0" +#define ETL_VERSION_W L"11.15.0" +#define ETL_VERSION_U16 u"11.15.0" +#define ETL_VERSION_U32 U"11.15.0" #define ETL_VERSION_MAJOR 11 -#define ETL_VERSION_MINOR 14 -#define ETL_VERSION_PATCH 3 +#define ETL_VERSION_MINOR 15 +#define ETL_VERSION_PATCH 0 #define ETL_VERSION_VALUE ((ETL_VERSION_MAJOR * 10000) + (ETL_VERSION_MINOR * 100) + ETL_VERSION_PATCH) #endif diff --git a/support/Release notes.txt b/support/Release notes.txt index 07decddd..fde6c598 100644 --- a/support/Release notes.txt +++ b/support/Release notes.txt @@ -1,3 +1,15 @@ +=============================================================================== +11.15.0 +Added 'memory model' selection for queues to allow more efficient implementations. + +Maximum queue sizes: +MEMORY_MODEL_SMALL 255 (254 for queue_spsc_atomic) +MEMORY_MODEL_MEDIUM 65535 +MEMORY_MODEL_LARGE 2147483647 +MEMORY_MODEL_HUGE 9223372036854775807 + +Fixed syntax errors highlighted by GCC v8 + =============================================================================== 11.14.2 Removed reference_flat_set & reference_flat_map reliance on equality. diff --git a/test/codeblocks/ETL.cbp b/test/codeblocks/ETL.cbp index a3c7d0cb..cbbcc3ec 100644 --- a/test/codeblocks/ETL.cbp +++ b/test/codeblocks/ETL.cbp @@ -406,9 +406,13 @@ + + + + diff --git a/test/test_queue_memory_model_small.cpp b/test/test_queue_memory_model_small.cpp new file mode 100644 index 00000000..e60e9181 --- /dev/null +++ b/test/test_queue_memory_model_small.cpp @@ -0,0 +1,589 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 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 + +#include "etl/queue.h" + +namespace +{ + struct Item + { + Item(char c_, int i_, double d_) + : c(c_), + i(i_), + d(d_) + { + } + + char c; + int i; + double d; + }; + + bool operator == (const Item& lhs, const Item& rhs) + { + return (lhs.c == rhs.c) && (lhs.i == rhs.i) && (lhs.d == rhs.d); + } + + struct ItemNTD + { + ItemNTD() + { + p = new char; + } + + ItemNTD(const ItemNTD&) + : p(new char) + { + } + + ~ItemNTD() + { + delete p; + } + + char* p; + }; + + typedef etl::queue QueueInt; + typedef etl::iqueue IQueueInt; + + typedef etl::queue QueueItemNTD; + typedef etl::iqueue IQueueItemNTD; + + typedef etl::queue QueueInt255; + + SUITE(test_queue) + { + //************************************************************************* + TEST(test_copy_constructor) + { + QueueInt queue; + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + + QueueInt queue2(queue); + + CHECK(queue.size() == queue2.size()); + + while (!queue.empty()) + { + CHECK_EQUAL(queue.front(), queue2.front()); + queue.pop(); + queue2.pop(); + } + } + + //************************************************************************* + TEST(test_delete_via_iqueue) + { + QueueInt* pqueue = new QueueInt; + + IQueueInt* piqueue = pqueue; + + piqueue->push(1); + piqueue->push(2); + piqueue->push(3); + piqueue->push(4); + + delete piqueue; + } + + //************************************************************************* + TEST(test_size) + { + QueueInt queue; + + queue.push(1); + queue.push(2); + queue.push(3); + + CHECK_EQUAL(3U, queue.size()); + } + + //************************************************************************* + TEST(test_clear) + { + QueueInt queue; + + queue.push(1); + queue.push(2); + queue.clear(); + CHECK_EQUAL(0U, queue.size()); + + // Do it again to check that clear() didn't screw up the internals. + queue.push(1); + queue.push(2); + CHECK_EQUAL(2U, queue.size()); + queue.clear(); + CHECK_EQUAL(0U, queue.size()); + } + + //************************************************************************* + TEST(test_clear_non_pod) + { + QueueItemNTD queue; + + queue.push(ItemNTD()); + queue.push(ItemNTD()); + queue.clear(); + CHECK_EQUAL(0U, queue.size()); + + // Do it again to check that clear() didn't screw up the internals. + queue.push(ItemNTD()); + queue.push(ItemNTD()); + CHECK_EQUAL(2U, queue.size()); + queue.clear(); + CHECK_EQUAL(0U, queue.size()); + } + + //************************************************************************* + TEST(test_empty) + { + QueueInt queue; + + CHECK(queue.empty()); + + queue.push(1); + + CHECK(!queue.empty()); + } + + //************************************************************************* + TEST(test_full) + { + QueueInt queue; + + CHECK(!queue.full()); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + + CHECK(queue.full()); + } + + //************************************************************************* + TEST(test_front) + { + QueueInt queue; + + queue.push(1); + queue.push(2); + queue.push(3); + + CHECK_EQUAL(1, queue.front()); + + queue.pop(); + CHECK_EQUAL(2, queue.front()); + + queue.pop(); + CHECK_EQUAL(3, queue.front()); + } + + //************************************************************************* + TEST(test_front_const) + { + QueueInt queue; + const QueueInt& constQueue = queue; + + queue.push(1); + queue.push(2); + queue.push(3); + + CHECK_EQUAL(1, constQueue.front()); + + queue.pop(); + CHECK_EQUAL(2, constQueue.front()); + + queue.pop(); + CHECK_EQUAL(3, constQueue.front()); + } + + //************************************************************************* + TEST(test_back) + { + QueueInt queue; + + queue.push(1); + CHECK_EQUAL(1, queue.back()); + + queue.push(2); + CHECK_EQUAL(2, queue.back()); + + queue.push(3); + CHECK_EQUAL(3, queue.back()); + } + + //************************************************************************* + TEST(test_back_const) + { + QueueInt queue; + const QueueInt& constQueue = queue; + + queue.push(1); + CHECK_EQUAL(1, constQueue.back()); + + queue.push(2); + CHECK_EQUAL(2, constQueue.back()); + + queue.push(3); + CHECK_EQUAL(3, constQueue.back()); + } + + //************************************************************************* + TEST(test_push) + { + QueueInt queue; + + queue.push(1); + CHECK_EQUAL(1U, queue.size()); + + queue.push(2); + CHECK_EQUAL(2U, queue.size()); + + CHECK_EQUAL(1, queue.front()); + + queue.pop(); + CHECK_EQUAL(2, queue.front()); + } + + //************************************************************************* + TEST(test_push_255) + { + QueueInt255 queue; + + for (int i = 0; i < 255; ++i) + { + queue.push(i); + } + + CHECK_EQUAL(255U, queue.size()); + } + + //************************************************************************* + TEST(test_push_void) + { + QueueInt queue; + + queue.push() = 1; + CHECK_EQUAL(1U, queue.size()); + + queue.push() = 2; + CHECK_EQUAL(2U, queue.size()); + + CHECK_EQUAL(1, queue.front()); + + queue.pop(); + CHECK_EQUAL(2, queue.front()); + } + + //************************************************************************* + TEST(test_push_excess) + { + QueueInt queue; + + for (size_t i = 0; i < queue.max_size(); ++i) + { + queue.push(1); + } + + CHECK_THROW(queue.push(1), etl::queue_full); + } + + //************************************************************************* + TEST(test_multiple_push) + { + QueueInt queue; + + queue.push(1); + queue.push(2); + queue.push(3); + + bool pass = true; + + if (queue.front() != 1) + { + pass = false; + } + + queue.pop(); + + if (queue.front() != 2) + { + pass = false; + } + + queue.pop(); + + if (queue.front() != 3) + { + pass = false; + } + + CHECK(pass); + } + + //************************************************************************* + TEST(test_multiple_emplace) + { + etl::queue queue; + + queue.emplace('a', 1, 1.2); + queue.emplace('b', 2, 3.4); + queue.emplace('c', 3, 5.6); + queue.emplace('d', 4, 7.8); + + CHECK(queue.front() == Item('a', 1, 1.2)); + queue.pop(); + CHECK(queue.front() == Item('b', 2, 3.4)); + queue.pop(); + CHECK(queue.front() == Item('c', 3, 5.6)); + queue.pop(); + CHECK(queue.front() == Item('d', 4, 7.8)); + queue.pop(); + } + + //************************************************************************* + TEST(test_multiple_push_void) + { + QueueInt queue; + + queue.push() = 1; + queue.push() = 2; + queue.push() = 3; + + bool pass = true; + + if (queue.front() != 1) + { + pass = false; + } + + queue.pop(); + + if (queue.front() != 2) + { + pass = false; + } + + queue.pop(); + + if (queue.front() != 3) + { + pass = false; + } + + CHECK(pass); + } + + //************************************************************************* + TEST(test_pop) + { + QueueInt queue; + + queue.push(1); + queue.push(2); + queue.pop(); + CHECK_EQUAL(1U, queue.size()); + } + + //************************************************************************* + TEST(test_pop_into) + { + QueueInt queue; + + int i; + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + + queue.pop_into(i); + CHECK_EQUAL(1, i); + CHECK_EQUAL(3U, queue.size()); + + queue.pop_into(i); + CHECK_EQUAL(2, i); + CHECK_EQUAL(2U, queue.size()); + + queue.pop_into(i); + CHECK_EQUAL(3, i); + CHECK_EQUAL(1U, queue.size()); + + queue.pop_into(i); + CHECK_EQUAL(4, i); + CHECK_EQUAL(0U, queue.size()); + } + + //************************************************************************* + TEST(test_pop_into_queue) + { + QueueInt queue1; + QueueInt queue2; + + queue1.push(1); + queue1.push(2); + queue1.push(3); + queue1.push(4); + + queue1.pop_into(queue2); + CHECK_EQUAL(1U, queue2.size()); + CHECK_EQUAL(1, queue2.front()); + CHECK_EQUAL(1, queue2.back()); + + queue1.pop_into(queue2); + CHECK_EQUAL(2U, queue2.size()); + CHECK_EQUAL(1, queue2.front()); + CHECK_EQUAL(2, queue2.back()); + + queue1.pop_into(queue2); + CHECK_EQUAL(3U, queue2.size()); + CHECK_EQUAL(1, queue2.front()); + CHECK_EQUAL(3, queue2.back()); + + queue1.pop_into(queue2); + CHECK_EQUAL(4U, queue2.size()); + CHECK_EQUAL(1, queue2.front()); + CHECK_EQUAL(4, queue2.back()); + + int i; + + queue2.pop_into(i); + CHECK_EQUAL(1, i); + + queue2.pop_into(i); + CHECK_EQUAL(2, i); + + queue2.pop_into(i); + CHECK_EQUAL(3, i); + + queue2.pop_into(i); + CHECK_EQUAL(4, i); + } + + //************************************************************************* + TEST(test_pop_exception) + { + QueueInt queue; + + queue.push(1); + queue.push(2); + queue.pop(); + queue.pop(); + + CHECK_THROW(queue.pop(), etl::queue_empty); + } + + //************************************************************************* + TEST(test_assignment) + { + QueueInt queue; + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + + QueueInt queue2; + + queue2 = queue; + + CHECK(queue.size() == queue2.size()); + + while (!queue.empty()) + { + CHECK_EQUAL(queue.front(), queue2.front()); + queue.pop(); + queue2.pop(); + } + } + + //************************************************************************* + TEST(test_assignment_interface) + { + QueueInt queue1; + + queue1.push(1); + queue1.push(2); + queue1.push(3); + queue1.push(4); + + QueueInt queue2; + + IQueueInt& iqueue1 = queue1; + IQueueInt& iqueue2 = queue2; + + iqueue2 = iqueue1; + + CHECK(queue1.size() == queue2.size()); + + while (!queue1.empty()) + { + CHECK_EQUAL(queue1.front(), queue2.front()); + queue1.pop(); + queue2.pop(); + } + } + + //************************************************************************* + TEST(test_self_assignment) + { + QueueInt queue; + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + + queue = queue; + + CHECK(queue.max_size() == queue.size()); + + CHECK_EQUAL(1, queue.front()); + queue.pop(); + + CHECK_EQUAL(2, queue.front()); + queue.pop(); + + CHECK_EQUAL(3, queue.front()); + queue.pop(); + + CHECK_EQUAL(4, queue.front()); + queue.pop(); + } + }; +} diff --git a/test/test_queue_mpmc_mutex.cpp b/test/test_queue_mpmc_mutex.cpp index ef15fee4..75c1be5c 100644 --- a/test/test_queue_mpmc_mutex.cpp +++ b/test/test_queue_mpmc_mutex.cpp @@ -69,17 +69,17 @@ namespace 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); - } +// 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); +// } - std::ostream& operator <<(std::ostream& os, const Data& data) - { - os << data.a << " " << data.b << " " << data.c << " " << data.d; - - return os; - } +// std::ostream& operator <<(std::ostream& os, const Data& data) +// { +// os << data.a << " " << data.b << " " << data.c << " " << data.d; +// +// return os; +// } SUITE(test_queue_mpmc_mutex) { diff --git a/test/test_queue_mpmc_mutex_small.cpp b/test/test_queue_mpmc_mutex_small.cpp new file mode 100644 index 00000000..0c57c332 --- /dev/null +++ b/test/test_queue_mpmc_mutex_small.cpp @@ -0,0 +1,460 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 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 +#include +#include +#include +#include +#include + +#include "etl/queue_mpmc_mutex.h" + +#if defined(ETL_COMPILER_MICROSOFT) + #include +#endif + +#define REALTIME_TEST 1 + +namespace +{ + 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; + }; + + typedef etl::queue_mpmc_mutex QueueInt; + typedef etl::iqueue_mpmc_mutex IQueueInt; + + typedef etl::queue_mpmc_mutex QueueInt255; + +// 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); +// } + +// std::ostream& operator <<(std::ostream& os, const Data& data) +// { +// os << data.a << " " << data.b << " " << data.c << " " << data.d; +// +// return os; +// } + + SUITE(test_queue_mpmc_mutex) + { + //************************************************************************* + TEST(test_constructor) + { + QueueInt queue; + + CHECK_EQUAL(4U, queue.max_size()); + CHECK_EQUAL(4U, queue.capacity()); + } + + //************************************************************************* + TEST(test_size_push_pop) + { + QueueInt queue; + + CHECK_EQUAL(0U, queue.size()); + + CHECK_EQUAL(4U, queue.available()); + CHECK_EQUAL(0U, queue.size()); + + queue.push(1); + CHECK_EQUAL(1U, queue.size()); + CHECK_EQUAL(3U, queue.available()); + + queue.push(2); + CHECK_EQUAL(2U, queue.size()); + CHECK_EQUAL(2U, queue.available()); + + queue.push(3); + CHECK_EQUAL(3U, queue.size()); + CHECK_EQUAL(1U, queue.available()); + + queue.push(4); + CHECK_EQUAL(4U, queue.size()); + CHECK_EQUAL(0U, queue.available()); + + CHECK(!queue.push(5)); + CHECK(!queue.push(5)); + + int i; + + CHECK(queue.pop(i)); + CHECK_EQUAL(1, i); + CHECK_EQUAL(3U, queue.size()); + + CHECK(queue.pop(i)); + CHECK_EQUAL(2, i); + CHECK_EQUAL(2U, queue.size()); + + CHECK(queue.pop(i)); + CHECK_EQUAL(3, i); + CHECK_EQUAL(1U, queue.size()); + + CHECK(queue.pop(i)); + CHECK_EQUAL(4, i); + CHECK_EQUAL(0U, queue.size()); + + CHECK(!queue.pop(i)); + CHECK(!queue.pop(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_iqueue) + { + QueueInt queue; + + IQueueInt& iqueue = queue; + + CHECK_EQUAL(0U, iqueue.size()); + + iqueue.push(1); + CHECK_EQUAL(1U, iqueue.size()); + + iqueue.push(2); + CHECK_EQUAL(2U, iqueue.size()); + + iqueue.push(3); + CHECK_EQUAL(3U, iqueue.size()); + + iqueue.push(4); + CHECK_EQUAL(4U, iqueue.size()); + + CHECK(!iqueue.push(5)); + CHECK(!iqueue.push(5)); + + int i; + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(1, i); + CHECK_EQUAL(3U, iqueue.size()); + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(2, i); + CHECK_EQUAL(2U, iqueue.size()); + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(3, i); + CHECK_EQUAL(1U, iqueue.size()); + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(4, i); + CHECK_EQUAL(0U, iqueue.size()); + + CHECK(!iqueue.pop(i)); + CHECK(!iqueue.pop(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_void) + { + QueueInt queue; + + CHECK_EQUAL(0U, queue.size()); + + queue.push(1); + CHECK_EQUAL(1U, queue.size()); + + queue.push(2); + CHECK_EQUAL(2U, queue.size()); + + queue.push(3); + CHECK_EQUAL(3U, queue.size()); + + queue.push(4); + CHECK_EQUAL(4U, queue.size()); + + CHECK(!queue.push(5)); + CHECK(!queue.push(5)); + + CHECK(queue.pop()); + CHECK_EQUAL(3U, queue.size()); + + CHECK(queue.pop()); + CHECK_EQUAL(2U, queue.size()); + + CHECK(queue.pop()); + CHECK_EQUAL(1U, queue.size()); + + CHECK(queue.pop()); + CHECK_EQUAL(0U, queue.size()); + + CHECK(!queue.pop()); + CHECK(!queue.pop()); + } + + //************************************************************************* + TEST(test_push_255) + { + QueueInt255 queue; + + for (int i = 0; i < 255; ++i) + { + queue.push(i); + } + + CHECK_EQUAL(255U, queue.size()); + } + + //************************************************************************* + TEST(test_clear) + { + QueueInt queue; + + CHECK_EQUAL(0U, queue.size()); + + queue.push(1); + queue.push(2); + queue.clear(); + CHECK_EQUAL(0U, queue.size()); + + // Do it again to check that clear() didn't screw up the internals. + queue.push(1); + queue.push(2); + CHECK_EQUAL(2U, queue.size()); + queue.clear(); + CHECK_EQUAL(0U, queue.size()); + } + + //************************************************************************* + TEST(test_empty) + { + QueueInt queue; + CHECK(queue.empty()); + + queue.push(1); + CHECK(!queue.empty()); + + queue.clear(); + CHECK(queue.empty()); + + queue.push(1); + CHECK(!queue.empty()); + } + + //************************************************************************* + TEST(test_full) + { + QueueInt queue; + CHECK(!queue.full()); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + CHECK(queue.full()); + + queue.clear(); + CHECK(!queue.full()); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + CHECK(queue.full()); + } + + //========================================================================= +#if REALTIME_TEST && defined(ETL_COMPILER_MICROSOFT) + #if defined(ETL_TARGET_OS_WINDOWS) // Only Windows priority is currently supported + #define SET_THREAD_PRIORITY SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL) + #define FIX_PROCESSOR_AFFINITY1 SetThreadAffinityMask(GetCurrentThread(), 1); + #define FIX_PROCESSOR_AFFINITY2 SetThreadAffinityMask(GetCurrentThread(), 2); + #define FIX_PROCESSOR_AFFINITY3 SetThreadAffinityMask(GetCurrentThread(), 4); + #define FIX_PROCESSOR_AFFINITY4 SetThreadAffinityMask(GetCurrentThread(), 8); + #else + #error No thread priority modifier defined + #endif + + etl::queue_mpmc_mutex queue; + + const size_t LENGTH = 100000; + + std::vector push1; + std::vector push2; + + std::vector pop1; + std::vector pop2; + + volatile std::atomic_bool start; + + void push_thread1() + { + FIX_PROCESSOR_AFFINITY1; + SET_THREAD_PRIORITY; + + size_t count = 0; + int value = 0; + + while (!start.load()); + + while (count < (LENGTH / 2)) + { + if (queue.push(value)) + { + push1.push_back(value); + ++count; + ++value; + } + } + } + + void push_thread2() + { + FIX_PROCESSOR_AFFINITY2; + SET_THREAD_PRIORITY; + + size_t count = 0; + int value = LENGTH / 2; + + while (!start.load()); + + while (count < (LENGTH / 2)) + { + if (queue.push(value)) + { + push2.push_back(value); + ++count; + ++value; + } + } + } + + void pop_thread1() + { + FIX_PROCESSOR_AFFINITY3; + SET_THREAD_PRIORITY; + + size_t count = 0; + + while (!start.load()); + + while (count < (LENGTH / 2)) + { + int i; + + if (queue.pop(i)) + { + pop1.push_back(i); + ++count; + } + } + } + + void pop_thread2() + { + FIX_PROCESSOR_AFFINITY4; + SET_THREAD_PRIORITY; + + size_t count = 0; + + while (!start.load()); + + while (count < (LENGTH / 2)) + { + int i; + + if (queue.pop(i)) + { + pop2.push_back(i); + ++count; + } + } + } + + TEST(queue_threads) + { + push1.reserve(LENGTH / 2); + push2.reserve(LENGTH / 2);; + + pop1.reserve(LENGTH / 2);; + pop2.reserve(LENGTH / 2);; + + start = false; + + std::thread t1(push_thread1); + std::thread t2(push_thread2); + std::thread t3(pop_thread1); + std::thread t4(pop_thread2); + + start.store(true); + + // Join the threads with the main thread + t1.join(); + t2.join(); + t3.join(); + t4.join(); + + // Combine input vectors. + std::vector push; + push.insert(push.end(), push1.begin(), push1.end()); + push.insert(push.end(), push2.begin(), push2.end()); + std::sort(push.begin(), push.end()); + + // Combine output vectors. + std::vector pop; + pop.insert(pop.end(), pop1.begin(), pop1.end()); + pop.insert(pop.end(), pop2.begin(), pop2.end()); + std::sort(pop.begin(), pop.end()); + + CHECK_EQUAL(LENGTH, push.size()); + CHECK_EQUAL(LENGTH, pop.size()); + + for (size_t i = 0; i < LENGTH; ++i) + { + CHECK_EQUAL(push[i], pop[i]); + CHECK_EQUAL(i, pop[i]); + } + } +#endif + }; +} diff --git a/test/test_queue_spsc_atomic_small.cpp b/test/test_queue_spsc_atomic_small.cpp new file mode 100644 index 00000000..fd8a5274 --- /dev/null +++ b/test/test_queue_spsc_atomic_small.cpp @@ -0,0 +1,330 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 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 +#include +#include + +#include "etl/queue_spsc_atomic.h" + +#if defined(ETL_COMPILER_MICROSOFT) + #include +#endif + +#define REALTIME_TEST 0 + +namespace +{ + typedef etl::queue_spsc_atomic QueueInt; + typedef etl::iqueue_spsc_atomic IQueueInt; + + typedef etl::queue_spsc_atomic QueueInt254; + + SUITE(test_queue_atomic) + { + //************************************************************************* + TEST(test_constructor) + { + QueueInt queue; + + CHECK_EQUAL(4U, queue.max_size()); + CHECK_EQUAL(4U, queue.capacity()); + } + + //************************************************************************* + TEST(test_size_push_pop) + { + QueueInt queue; + + CHECK_EQUAL(0U, queue.size()); + + CHECK_EQUAL(4U, queue.available()); + CHECK_EQUAL(0U, queue.size()); + + queue.push(1); + CHECK_EQUAL(1U, queue.size()); + CHECK_EQUAL(3U, queue.available()); + + queue.push(2); + CHECK_EQUAL(2U, queue.size()); + CHECK_EQUAL(2U, queue.available()); + + queue.push(3); + CHECK_EQUAL(3U, queue.size()); + CHECK_EQUAL(1U, queue.available()); + + queue.push(4); + CHECK_EQUAL(4U, queue.size()); + CHECK_EQUAL(0U, queue.available()); + + CHECK(!queue.push(5)); + CHECK(!queue.push(5)); + + int i; + + CHECK(queue.pop(i)); + CHECK_EQUAL(1, i); + CHECK_EQUAL(3U, queue.size()); + + CHECK(queue.pop(i)); + CHECK_EQUAL(2, i); + CHECK_EQUAL(2U, queue.size()); + + CHECK(queue.pop(i)); + CHECK_EQUAL(3, i); + CHECK_EQUAL(1U, queue.size()); + + CHECK(queue.pop(i)); + CHECK_EQUAL(4, i); + CHECK_EQUAL(0U, queue.size()); + + CHECK(!queue.pop(i)); + CHECK(!queue.pop(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_iqueue) + { + QueueInt queue; + + IQueueInt& iqueue = queue; + + CHECK_EQUAL(0U, iqueue.size()); + + iqueue.push(1); + CHECK_EQUAL(1U, iqueue.size()); + + iqueue.push(2); + CHECK_EQUAL(2U, iqueue.size()); + + iqueue.push(3); + CHECK_EQUAL(3U, iqueue.size()); + + iqueue.push(4); + CHECK_EQUAL(4U, iqueue.size()); + + CHECK(!iqueue.push(5)); + CHECK(!iqueue.push(5)); + + int i; + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(1, i); + CHECK_EQUAL(3U, iqueue.size()); + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(2, i); + CHECK_EQUAL(2U, iqueue.size()); + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(3, i); + CHECK_EQUAL(1U, iqueue.size()); + + CHECK(iqueue.pop(i)); + CHECK_EQUAL(4, i); + CHECK_EQUAL(0U, iqueue.size()); + + CHECK(!iqueue.pop(i)); + CHECK(!iqueue.pop(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_void) + { + QueueInt queue; + + CHECK_EQUAL(0U, queue.size()); + + queue.push(1); + CHECK_EQUAL(1U, queue.size()); + + queue.push(2); + CHECK_EQUAL(2U, queue.size()); + + queue.push(3); + CHECK_EQUAL(3U, queue.size()); + + queue.push(4); + CHECK_EQUAL(4U, queue.size()); + + CHECK(!queue.push(5)); + CHECK(!queue.push(5)); + + CHECK(queue.pop()); + CHECK_EQUAL(3U, queue.size()); + + CHECK(queue.pop()); + CHECK_EQUAL(2U, queue.size()); + + CHECK(queue.pop()); + CHECK_EQUAL(1U, queue.size()); + + CHECK(queue.pop()); + CHECK_EQUAL(0U, queue.size()); + + CHECK(!queue.pop()); + CHECK(!queue.pop()); + } + + //************************************************************************* + TEST(test_push_254) + { + QueueInt254 queue; + + for (int i = 0; i < 254; ++i) + { + queue.push(i); + } + + CHECK_EQUAL(254U, queue.size()); + } + + //************************************************************************* + TEST(test_clear) + { + QueueInt queue; + + CHECK_EQUAL(0U, queue.size()); + + queue.push(1); + queue.push(2); + queue.clear(); + CHECK_EQUAL(0U, queue.size()); + + // Do it again to check that clear() didn't screw up the internals. + queue.push(1); + queue.push(2); + CHECK_EQUAL(2U, queue.size()); + queue.clear(); + CHECK_EQUAL(0U, queue.size()); + } + + //************************************************************************* + TEST(test_empty) + { + QueueInt queue; + CHECK(queue.empty()); + + queue.push(1); + CHECK(!queue.empty()); + + queue.clear(); + CHECK(queue.empty()); + + queue.push(1); + CHECK(!queue.empty()); + } + + //************************************************************************* + TEST(test_full) + { + QueueInt queue; + CHECK(!queue.full()); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + CHECK(queue.full()); + + queue.clear(); + CHECK(!queue.full()); + + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + CHECK(queue.full()); + } + + //========================================================================= +#if REALTIME_TEST && defined(ETL_COMPILER_MICROSOFT) + #if defined(ETL_TARGET_OS_WINDOWS) // Only Windows priority is currently supported + #define FIX_PROCESSOR_AFFINITY1 SetThreadAffinityMask(GetCurrentThread(), 1); + #define FIX_PROCESSOR_AFFINITY2 SetThreadAffinityMask(GetCurrentThread(), 2); + #else + #error No thread priority modifier defined + #endif + + size_t ticks = 0; + + etl::queue_spsc_atomic queue; + + const size_t LENGTH = 1000000; + + void timer_event() + { + FIX_PROCESSOR_AFFINITY1; + + const size_t TICK = 1; + size_t tick = TICK; + ticks = 1; + + while (ticks <= LENGTH) + { + if (queue.push(ticks)) + { + ++ticks; + } + } + } + + TEST(queue_threads) + { + FIX_PROCESSOR_AFFINITY2; + + std::vector tick_list; + tick_list.reserve(LENGTH); + + std::thread t1(timer_event); + + 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_isr_small.cpp b/test/test_queue_spsc_isr_small.cpp new file mode 100644 index 00000000..3895b3d0 --- /dev/null +++ b/test/test_queue_spsc_isr_small.cpp @@ -0,0 +1,587 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 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_isr.h" + +#include +#include +#include + +#if defined(ETL_COMPILER_MICROSOFT) +#include +#endif + +#define REALTIME_TEST 0 + +namespace +{ + class Access + { + public: + + static void clear() + { + called_lock = false; + called_unlock = false; + } + + static void lock() + { + called_lock = true; + } + + static void unlock() + { + called_unlock = true; + } + + static bool called_lock; + static bool called_unlock; + }; + + bool Access::called_lock; + bool Access::called_unlock; + + typedef etl::queue_spsc_isr QueueInt; + typedef etl::iqueue_spsc_isr IQueueInt; + + typedef etl::queue_spsc_isr QueueInt255; + + SUITE(test_queue_isr) + { + //************************************************************************* + TEST(test_constructor) + { + Access::clear(); + + QueueInt queue; + + 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; + + CHECK_EQUAL(0U, queue.size_from_isr()); + + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + + Access::clear(); + + CHECK_EQUAL(4U, queue.available_from_isr()); + 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_isr(1); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(1U, queue.size_from_isr()); + CHECK_EQUAL(3U, queue.available_from_isr()); + + Access::clear(); + + queue.push(2); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(2U, queue.size_from_isr()); + CHECK_EQUAL(2U, queue.available_from_isr()); + + Access::clear(); + + queue.push(3); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(3U, queue.size_from_isr()); + CHECK_EQUAL(1U, queue.available_from_isr()); + + Access::clear(); + + queue.push(4); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(4U, queue.size_from_isr()); + CHECK_EQUAL(0U, queue.available_from_isr()); + + Access::clear(); + + CHECK(!queue.push(5)); + CHECK(!queue.push_from_isr(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_isr()); + + Access::clear(); + + CHECK(queue.pop_from_isr(i)); + CHECK_EQUAL(2, i); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(2U, queue.size_from_isr()); + + Access::clear(); + + CHECK(queue.pop_from_isr(i)); + CHECK_EQUAL(3, i); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(1U, queue.size_from_isr()); + + Access::clear(); + + CHECK(queue.pop_from_isr(i)); + CHECK_EQUAL(4, i); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(0U, queue.size_from_isr()); + + Access::clear(); + + CHECK(!queue.pop(i)); + CHECK(!queue.pop_from_isr(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_iqueue) + { + Access::clear(); + + QueueInt queue; + + IQueueInt& iqueue = queue; + + CHECK_EQUAL(0U, iqueue.size_from_isr()); + + 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_isr(1); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(1U, iqueue.size_from_isr()); + + Access::clear(); + + iqueue.push(2); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(2U, iqueue.size_from_isr()); + + Access::clear(); + + iqueue.push(3); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(3U, iqueue.size_from_isr()); + + Access::clear(); + + iqueue.push(4); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(4U, iqueue.size_from_isr()); + + Access::clear(); + + CHECK(!iqueue.push(5)); + CHECK(!iqueue.push_from_isr(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_isr()); + + Access::clear(); + + CHECK(iqueue.pop_from_isr(i)); + CHECK_EQUAL(2, i); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(2U, iqueue.size_from_isr()); + + Access::clear(); + + CHECK(iqueue.pop_from_isr(i)); + CHECK_EQUAL(3, i); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(1U, iqueue.size_from_isr()); + + Access::clear(); + + CHECK(iqueue.pop_from_isr(i)); + CHECK_EQUAL(4, i); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(0U, iqueue.size_from_isr()); + + Access::clear(); + + CHECK(!iqueue.pop(i)); + CHECK(!iqueue.pop_from_isr(i)); + } + + //************************************************************************* + TEST(test_size_push_pop_void) + { + Access::clear(); + + QueueInt queue; + + CHECK_EQUAL(0U, queue.size_from_isr()); + + 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_isr(1); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(1U, queue.size_from_isr()); + + Access::clear(); + + queue.push(2); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(2U, queue.size_from_isr()); + + Access::clear(); + + queue.push(3); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(3U, queue.size_from_isr()); + + Access::clear(); + + queue.push(4); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(4U, queue.size_from_isr()); + + Access::clear(); + + CHECK(!queue.push(5)); + CHECK(!queue.push_from_isr(5)); + + Access::clear(); + + CHECK(queue.pop()); + CHECK(Access::called_lock); + CHECK(Access::called_unlock); + CHECK_EQUAL(3U, queue.size_from_isr()); + + Access::clear(); + + CHECK(queue.pop_from_isr()); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(2U, queue.size_from_isr()); + + Access::clear(); + + CHECK(queue.pop_from_isr()); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(1U, queue.size_from_isr()); + + Access::clear(); + + CHECK(queue.pop_from_isr()); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + CHECK_EQUAL(0U, queue.size_from_isr()); + + Access::clear(); + + CHECK(!queue.pop()); + CHECK(!queue.pop_from_isr()); + } + + //************************************************************************* + TEST(test_push_255) + { + QueueInt255 queue; + + for (int i = 0; i < 255; ++i) + { + queue.push(i); + } + + CHECK_EQUAL(255U, queue.size()); + } + + //************************************************************************* + TEST(test_clear) + { + Access::clear(); + + QueueInt queue; + + 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_isr(1); + queue.push_from_isr(2); + CHECK_EQUAL(2U, queue.size_from_isr()); + queue.clear_from_isr(); + CHECK_EQUAL(0U, queue.size_from_isr()); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + } + + //************************************************************************* + TEST(test_empty) + { + Access::clear(); + + QueueInt queue; + + 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_isr()); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + + queue.push(1); + + Access::clear(); + + CHECK(!queue.empty_from_isr()); + CHECK(!Access::called_lock); + CHECK(!Access::called_unlock); + } + + //************************************************************************* + TEST(test_full) + { + Access::clear(); + + QueueInt queue; + + 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_isr()); + 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_isr()); + 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 + { + static void lock() + { + mutex.lock(); + } + + static void unlock() + { + mutex.unlock(); + } + + static std::mutex mutex; + }; + + std::mutex ThreadLock::mutex; + + etl::queue_spsc_isr queue; + + 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_isr(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 e79c511c..89fd056b 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -704,9 +704,13 @@ + + + + true true diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index 52458239..31d549a3 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -1109,6 +1109,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files +