diff --git a/include/etl/atomic.h b/include/etl/atomic.h index 652f815d..98a11efd 100644 --- a/include/etl/atomic.h +++ b/include/etl/atomic.h @@ -31,21 +31,17 @@ SOFTWARE. #include "platform.h" -#if ETL_ATOMIC_SUPPORTED == 1 - #include - - namespace etl - { - typedef std::atomic atomic_uint32_t; - } +#if ETL_CPP11_SUPPORTED == 1 + #include "atomic/atomic_std.h" + #define ETL_HAS_ATOMIC 1 #elif defined(ETL_COMPILER_ARM) #include "atomic/atomic_arm.h" + #define ETL_HAS_ATOMIC 1 #elif defined(ETL_COMPILER_GCC) - #include "atomic/atomic_gcc.h" -#elif defined(ETL_COMPILER_MSVC) - #include "atomic/atomic_windows.h" + #include "atomic/atomic_gcc_sync.h" + #define ETL_HAS_ATOMIC 1 #else - #warning NO ATOMIC SUPPORT DEFINED! + #define ETL_HAS_ATOMIC 0 #endif #endif diff --git a/include/etl/atomic/atomic_arm.h b/include/etl/atomic/atomic_arm.h index 84de98bb..9a21ac25 100644 --- a/include/etl/atomic/atomic_arm.h +++ b/include/etl/atomic/atomic_arm.h @@ -29,6 +29,6 @@ SOFTWARE. #ifndef __ETL_ATOMIC_ARM__ #define __ETL_ATOMIC_ARM__ -#include "atomic_gcc.h" +#include "atomic_gcc_sync.h" #endif diff --git a/include/etl/atomic/atomic_gcc.h b/include/etl/atomic/atomic_gcc_sync.h similarity index 60% rename from include/etl/atomic/atomic_gcc.h rename to include/etl/atomic/atomic_gcc_sync.h index 9bc9409c..493b9803 100644 --- a/include/etl/atomic/atomic_gcc.h +++ b/include/etl/atomic/atomic_gcc_sync.h @@ -26,23 +26,37 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ -#ifndef __ETL_ATOMIC_GCC__ -#define __ETL_ATOMIC_GCC__ +#ifndef __ETL_ATOMIC_GCC_SYNC__ +#define __ETL_ATOMIC_GCC_SYNC__ #include "../platform.h" #include "../type_traits.h" -#include "../char_traits.h" #include "../static_assert.h" #include "../nullptr.h" +#include #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" + namespace etl { //*************************************************************************** - // Atomic type for pre C++11 GCC compilers that support the builtin 'fetch' functions. + // Atomic type for pre C++11 GCC compilers that support the builtin '__sync' functions. // Only integral and pointer types are supported. //*************************************************************************** + + typedef enum memory_order + { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst + } memory_order; + template class atomic { @@ -63,14 +77,14 @@ namespace etl // Assignment T operator =(T v) { - __sync_lock_test_and_set(&value, v); + store(v); return v; } T operator =(T v) volatile { - __sync_lock_test_and_set(&value, v); + store(v); return v; } @@ -177,12 +191,12 @@ namespace etl // Conversion operator operator T () const { - return __sync_fetch_and_add(const_cast(&value), 0); + return __sync_fetch_and_add(&value, 0); } operator T() volatile const { - return __sync_fetch_and_add(const_cast(&value), 0); + return __sync_fetch_and_add(&value, 0); } // Is lock free? @@ -197,95 +211,95 @@ namespace etl } // Store - void store(T v) + void store(T v, etl::memory_order order = etl::memory_order_seq_cst) { __sync_lock_test_and_set(&value, v); } - void store(T v) volatile + void store(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile { __sync_lock_test_and_set(&value, v); } // Load - T load() + T load(etl::memory_order order = etl::memory_order_seq_cst) { return __sync_fetch_and_add(&value, 0); } - T load() volatile + T load(etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_fetch_and_add(&value, 0); } // Fetch add - T fetch_add(T v) + T fetch_add(T v, etl::memory_order order = etl::memory_order_seq_cst) { return __sync_fetch_and_add(&value, v); } - T fetch_add(T v) volatile + T fetch_add(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_fetch_and_add(&value, v); } // Fetch subtract - T fetch_sub(T v) + T fetch_sub(T v, etl::memory_order order = etl::memory_order_seq_cst) { return __sync_fetch_and_sub(&value, v); } - T fetch_sub(T v) volatile + T fetch_sub(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_fetch_and_sub(&value, v); } // Fetch or - T fetch_or(T v) + T fetch_or(T v, etl::memory_order order = etl::memory_order_seq_cst) { return __sync_fetch_and_or(&value, v); } - T fetch_or(T v) volatile + T fetch_or(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_fetch_and_or(&value, v); } // Fetch and - T fetch_and(T v) + T fetch_and(T v, etl::memory_order order = etl::memory_order_seq_cst) { return __sync_fetch_and_and(&value, v); } - T fetch_and(T v) volatile + T fetch_and(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_fetch_and_and(&value, v); } // Fetch exclusive or - T fetch_xor(T v) + T fetch_xor(T v, etl::memory_order order = etl::memory_order_seq_cst) { return __sync_fetch_and_xor(&value, v); } - T fetch_xor(T v) volatile + T fetch_xor(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_fetch_and_xor(&value, v); } // Exchange - T exchange(T v) + T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) { return __sync_lock_test_and_set(&value, v); } - T exchange(T v) volatile + T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_lock_test_and_set(&value, v); } // Compare exchange weak - bool compare_exchange_weak(T& expected, T desired) + bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { T old = __sync_val_compare_and_swap(&value, expected, desired); @@ -300,7 +314,37 @@ namespace etl } } - bool compare_exchange_weak(T& expected, T desired) volatile + bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + T old = __sync_val_compare_and_swap(&value, expected, desired); + + if (old == expected) + { + return true; + } + else + { + expected = old; + return false; + } + } + + bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) + { + T old = __sync_val_compare_and_swap(&value, expected, desired); + + if (old == expected) + { + return true; + } + else + { + expected = old; + return false; + } + } + + bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile { T old = __sync_val_compare_and_swap(&value, expected, desired); @@ -316,7 +360,7 @@ namespace etl } // Compare exchange strong - bool compare_exchange_strong(T& expected, T desired) + bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) { T old = expected; @@ -332,7 +376,39 @@ namespace etl return true; } - bool compare_exchange_strong(T& expected, T desired) volatile + bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + T old = expected; + + while (!compare_exchange_weak(old, desired)) + { + if (memcmp(&old, &expected, sizeof(T))) + { + expected = old; + return false; + } + } + + return true; + } + + bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) + { + T old = expected; + + while (!compare_exchange_weak(old, desired)) + { + if (memcmp(&old, &expected, sizeof(T))) + { + expected = old; + return false; + } + } + + return true; + } + + bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile { T old = expected; @@ -353,12 +429,9 @@ namespace etl atomic& operator =(const atomic&); atomic& operator =(const atomic&) volatile; - T value; + volatile T value; }; - //*************************************************************************** - // Specialisation for pointers. - //*************************************************************************** template class atomic { @@ -369,66 +442,66 @@ namespace etl { } - atomic(T v) + atomic(T* v) : value(v) { } // Assignment - T operator =(T* v) + T* operator =(T* v) { - __sync_lock_test_and_set(&value, v); + store(v); return v; } - T operator =(T* v) volatile + T* operator =(T* v) volatile { - __sync_lock_test_and_set(&value, v); + store(v); return v; } // Pre-increment - T operator ++() + T* operator ++() { return fetch_add(1) + 1; } - T operator ++() volatile + T* operator ++() volatile { return fetch_add(1) + 1; } // Post-increment - T operator ++(int) + T* operator ++(int) { return fetch_add(1); } - T operator ++(int) volatile + T* operator ++(int) volatile { return fetch_add(1); } // Pre-decrement - T operator --() + T* operator --() { return fetch_sub(1) + 1; } - T operator --() volatile + T* operator --() volatile { return fetch_sub(1) + 1; } // Post-decrement - T operator --(int) + T* operator --(int) { return fetch_sub(1); } - T operator --(int) volatile + T* operator --(int) volatile { return fetch_sub(1); } @@ -456,14 +529,14 @@ namespace etl } // Conversion operator - operator T () const + operator T* () const { - return __sync_fetch_and_add(const_cast(&value), 0); + return __sync_fetch_and_add(&value, 0); } - operator T() volatile const + operator T*() volatile const { - return __sync_fetch_and_add(const_cast(&value), 0); + return __sync_fetch_and_add(&value, 0); } // Is lock free? @@ -478,79 +551,129 @@ namespace etl } // Store - void store(T v) + void store(T* v, etl::memory_order order = etl::memory_order_seq_cst) { __sync_lock_test_and_set(&value, v); } - void store(T v) volatile + void store(T* v, etl::memory_order order = etl::memory_order_seq_cst) volatile { __sync_lock_test_and_set(&value, v); } // Load - T load() + T* load(etl::memory_order order = etl::memory_order_seq_cst) { return __sync_fetch_and_add(&value, 0); } - T load() volatile + T* load(etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_fetch_and_add(&value, 0); } // Fetch add - T* fetch_add(std::ptrdiff_t v) + T* fetch_add(std::ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) { return __sync_fetch_and_add(&value, v); } - T* fetch_add(std::ptrdiff_t v) volatile + T* fetch_add(std::ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_fetch_and_add(&value, v); } // Fetch subtract - T* fetch_sub(std::ptrdiff_t v) + T* fetch_sub(std::ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) { return __sync_fetch_and_sub(&value, v); } - T* fetch_sub(std::ptrdiff_t v) volatile + T* fetch_sub(std::ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_fetch_and_sub(&value, v); } // Exchange - T exchange(T v) + T* exchange(T* v, etl::memory_order order = etl::memory_order_seq_cst) { return __sync_lock_test_and_set(&value, v); } - T exchange(T v) volatile + T* exchange(T* v, etl::memory_order order = etl::memory_order_seq_cst) volatile { return __sync_lock_test_and_set(&value, v); } // Compare exchange weak - bool compare_exchange_weak(T& expected, T desired) + bool compare_exchange_weak(T*& expected, T* desired, etl::memory_order order = etl::memory_order_seq_cst) { - return __sync_bool_compare_and_swap(&value, expected, desired); + T* old = __sync_val_compare_and_swap(&value, expected, desired); + + if (old == expected) + { + return true; + } + else + { + expected = old; + return false; + } } - bool compare_exchange_weak(T& expected, T desired) volatile + bool compare_exchange_weak(T*& expected, T* desired, etl::memory_order order = etl::memory_order_seq_cst) volatile { - return __sync_bool_compare_and_swap(&value, expected, desired); + T* old = __sync_val_compare_and_swap(&value, expected, desired); + + if (old == expected) + { + return true; + } + else + { + expected = old; + return false; + } + } + + bool compare_exchange_weak(T*& expected, T* desired, etl::memory_order success, etl::memory_order failure) + { + T* old = __sync_val_compare_and_swap(&value, expected, desired); + + if (old == expected) + { + return true; + } + else + { + expected = old; + return false; + } + } + + bool compare_exchange_weak(T*& expected, T* desired, etl::memory_order success, etl::memory_order failure) volatile + { + T* old = __sync_val_compare_and_swap(&value, expected, desired); + + if (old == expected) + { + return true; + } + else + { + expected = old; + return false; + } } // Compare exchange strong - bool compare_exchange_strong(T& expected, T desired) + bool compare_exchange_strong(T*& expected, T* desired, etl::memory_order order = etl::memory_order_seq_cst) { - T old = expected; + T* old = expected; while (!compare_exchange_weak(old, desired)) { - if (memcmp(&old, &expected, sizeof(T))) + if (memcmp(&old, &expected, sizeof(T*))) { expected = old; return false; @@ -560,13 +683,45 @@ namespace etl return true; } - bool compare_exchange_strong(T& expected, T desired) volatile + bool compare_exchange_strong(T*& expected, T* desired, etl::memory_order order = etl::memory_order_seq_cst) volatile { - T old = expected; + T* old = expected; while (!compare_exchange_weak(old, desired)) { - if (memcmp(&old, &expected, sizeof(T))) + if (memcmp(&old, &expected, sizeof(T*))) + { + expected = old; + return false; + } + } + + return true; + } + + bool compare_exchange_strong(T*& expected, T* desired, etl::memory_order success, etl::memory_order failure) + { + T* old = expected; + + while (!compare_exchange_weak(old, desired)) + { + if (memcmp(&old, &expected, sizeof(T*))) + { + expected = old; + return false; + } + } + + return true; + } + + bool compare_exchange_strong(T*& expected, T* desired, etl::memory_order success, etl::memory_order failure) volatile + { + T* old = expected; + + while (!compare_exchange_weak(old, desired)) + { + if (memcmp(&old, &expected, sizeof(T*))) { expected = old; return false; @@ -581,7 +736,7 @@ namespace etl atomic& operator =(const atomic&); atomic& operator =(const atomic&) volatile; - T* value; + volatile T* value; }; typedef etl::atomic atomic_char; @@ -630,4 +785,6 @@ namespace etl typedef etl::atomic atomic_uintmax_t; } +#pragma GCC diagnostic pop + #endif diff --git a/include/etl/atomic/atomic_std.h b/include/etl/atomic/atomic_std.h new file mode 100644 index 00000000..3970a290 --- /dev/null +++ b/include/etl/atomic/atomic_std.h @@ -0,0 +1,604 @@ +/****************************************************************************** +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. +******************************************************************************/ + +#ifndef __ETL_ATOMIC_STD__ +#define __ETL_ATOMIC_STD__ + +#include "../platform.h" +#include "../nullptr.h" + +#include +#include + +namespace etl +{ + //*************************************************************************** + // ETL Atomic type for compilers that support std::atomic. + // etl::atomic is a simple wrapper around std::atomic. + //*************************************************************************** + + typedef std::memory_order memory_order; + + static const etl::memory_order memory_order_relaxed = std::memory_order_relaxed; + static const etl::memory_order memory_order_consume = std::memory_order_consume; + static const etl::memory_order memory_order_acquire = std::memory_order_acquire; + static const etl::memory_order memory_order_release = std::memory_order_release; + static const etl::memory_order memory_order_acq_rel = std::memory_order_acq_rel; + static const etl::memory_order memory_order_seq_cst = std::memory_order_seq_cst; + + template + class atomic + { + public: + + atomic() + : value(0) + { + } + + atomic(T v) + : value(v) + { + } + + // Assignment + T operator =(T v) + { + return value = v; + } + + T operator =(T v) volatile + { + return value = v; + } + + // Pre-increment + T operator ++() + { + return ++value; + } + + T operator ++() volatile + { + return ++value; + } + + // Post-increment + T operator ++(int) + { + return value++; + } + + T operator ++(int) volatile + { + return value++; + } + + // Pre-decrement + T operator --() + { + return --value; + } + + T operator --() volatile + { + return --value; + } + + // Post-decrement + T operator --(int) + { + return value--; + } + + T operator --(int) volatile + { + return value--; + } + + // Add + T operator +=(T v) + { + return value += v; + } + + T operator +=(T v) volatile + { + return value += v; + } + + // Subtract + T operator -=(T v) + { + return value -= v; + } + + T operator -=(T v) volatile + { + return value -= v; + } + + // And + T operator &=(T v) + { + return value &= v; + } + + T operator &=(T v) volatile + { + return value &= v; + } + + // Or + T operator |=(T v) + { + return value |= v; + } + + T operator |=(T v) volatile + { + return value |= v; + } + + // Exclusive or + T operator ^=(T v) + { + return value ^= v; + } + + T operator ^=(T v) volatile + { + return value ^= v; + } + + // Conversion operator + operator T () const + { + return T(value); + } + + operator T() volatile const + { + return T(value); + } + + // Is lock free? + bool is_lock_free() const + { + return value.is_lock_free(); + } + + bool is_lock_free() const volatile + { + return value.is_lock_free(); + } + + // Store + void store(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + value.store(v, order); + } + + void store(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + value.store(v, order); + } + + // Load + T load(etl::memory_order order = etl::memory_order_seq_cst) + { + return value.load(order); + } + + T load(etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.load(order); + } + + // Fetch add + T fetch_add(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.fetch_add(v, order); + } + + T fetch_add(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.fetch_add(v, order); + } + + // Fetch subtract + T fetch_sub(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.fetch_sub(v, order); + } + + T fetch_sub(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.fetch_sub(v, order); + } + + // Fetch or + T fetch_or(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.fetch_or(v, order); + } + + T fetch_or(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.fetch_or(v, order); + } + + // Fetch and + T fetch_and(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.fetch_and(v, order); + } + + T fetch_and(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.fetch_and(v, order); + } + + // Fetch exclusive or + T fetch_xor(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.fetch_xor(v, order); + } + + T fetch_xor(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.fetch_xor(v, order); + } + + // Exchange + T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.exchange(v, order); + } + + T exchange(T v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.exchange(v, order); + } + + // Compare exchange weak + bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.compare_exchange_weak(expected, desired, order); + } + + bool compare_exchange_weak(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.compare_exchange_weak(expected, desired, order); + } + + bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) + { + return value.compare_exchange_weak(expected, desired, success, failure); + } + + bool compare_exchange_weak(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile + { + return value.compare_exchange_weak(expected, desired, success, failure); + } + + // Compare exchange strong + bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.compare_exchange_strong(expected, desired, order); + } + + bool compare_exchange_strong(T& expected, T desired, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.compare_exchange_strong(expected, desired, order); + } + + bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) + { + return value.compare_exchange_strong(expected, desired, success, failure); + } + + bool compare_exchange_strong(T& expected, T desired, etl::memory_order success, etl::memory_order failure) volatile + { + return value.compare_exchange_strong(expected, desired, success, failure); + } + + private: + + atomic& operator =(const atomic&); + //atomic& operator =(const atomic&) volatile; + + std::atomic value; + }; + + template + class atomic + { + public: + + atomic() + : value(nullptr) + { + } + + atomic(T* v) + : value(v) + { + } + + // Assignment + T* operator =(T* v) + { + return value = v; + } + + T* operator =(T* v) volatile + { + return value = v; + } + + // Pre-increment + T* operator ++() + { + return ++value; + } + + T* operator ++() volatile + { + return ++value; + } + + // Post-increment + T* operator ++(int) + { + return value++; + } + + T* operator ++(int) volatile + { + return value++; + } + + // Pre-decrement + T* operator --() + { + return --value; + } + + T* operator --() volatile + { + return --value; + } + + // Post-decrement + T* operator --(int) + { + return value--; + } + + T* operator --(int) volatile + { + return value--; + } + + // Add + T* operator +=(std::ptrdiff_t v) + { + return value += v; + } + + T* operator +=(std::ptrdiff_t v) volatile + { + return value += v; + } + + // Subtract + T* operator -=(std::ptrdiff_t v) + { + return value -= v; + } + + T* operator -=(std::ptrdiff_t v) volatile + { + return value -= v; + } + + // Conversion operator + operator T* () const + { + return (T*)value; + } + + operator T*() volatile const + { + return (T*)value; + } + + // Is lock free? + bool is_lock_free() const + { + return value.is_lock_free(); + } + + bool is_lock_free() const volatile + { + return value.is_lock_free(); + } + + // Store + void store(T* v, etl::memory_order order = etl::memory_order_seq_cst) + { + value.store(v, order); + } + + void store(T* v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + value.store(v, order); + } + + // Load + T* load(etl::memory_order order = etl::memory_order_seq_cst) + { + return value.load(order); + } + + T* load(etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.load(order); + } + + // Fetch add + T* fetch_add(std::ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.fetch_add(v, order); + } + + T* fetch_add(std::ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.fetch_add(v, order); + } + + // Fetch subtract + T* fetch_sub(std::ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.fetch_sub(v, order); + } + + T* fetch_sub(std::ptrdiff_t v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.fetch_sub(v, order); + } + + // Exchange + T* exchange(T* v, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.exchange(v, order); + } + + T* exchange(T* v, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.exchange(v, order); + } + + // Compare exchange weak + bool compare_exchange_weak(T*& expected, T* desired, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.compare_exchange_weak(expected, desired, order); + } + + bool compare_exchange_weak(T*& expected, T* desired, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.compare_exchange_weak(expected, desired, order); + } + + bool compare_exchange_weak(T*& expected, T* desired, etl::memory_order success, etl::memory_order failure) + { + return value.compare_exchange_weak(expected, desired, success, failure); + } + + bool compare_exchange_weak(T*& expected, T* desired, etl::memory_order success, etl::memory_order failure) volatile + { + return value.compare_exchange_weak(expected, desired, success, failure); + } + + // Compare exchange strong + bool compare_exchange_strong(T*& expected, T* desired, etl::memory_order order = etl::memory_order_seq_cst) + { + return value.compare_exchange_strong(expected, desired, order); + } + + bool compare_exchange_strong(T*& expected, T* desired, etl::memory_order order = etl::memory_order_seq_cst) volatile + { + return value.compare_exchange_strong(expected, desired, order); + } + + bool compare_exchange_strong(T*& expected, T* desired, etl::memory_order success, etl::memory_order failure) + { + return value.compare_exchange_strong(expected, desired, success, failure); + } + + bool compare_exchange_strong(T*& expected, T* desired, etl::memory_order success, etl::memory_order failure) volatile + { + return value.compare_exchange_strong(expected, desired, success, failure); + } + + private: + + atomic & operator =(const atomic&); + //atomic& operator =(const atomic&) volatile; + + std::atomic value; + }; + + typedef std::atomic atomic_char; + typedef std::atomic atomic_schar; + typedef std::atomic atomic_uchar; + typedef std::atomic atomic_short; + typedef std::atomic atomic_ushort; + typedef std::atomic atomic_int; + typedef std::atomic atomic_uint; + typedef std::atomic atomic_long; + typedef std::atomic atomic_ulong; + typedef std::atomic atomic_llong; + typedef std::atomic atomic_ullong; + typedef std::atomic atomic_wchar_t; + typedef std::atomic atomic_char16_t; + typedef std::atomic atomic_char32_t; + typedef std::atomic atomic_uint8_t; + typedef std::atomic atomic_int8_t; + typedef std::atomic atomic_uint16_t; + typedef std::atomic atomic_int16_t; + typedef std::atomic atomic_uint32_t; + typedef std::atomic atomic_int32_t; + typedef std::atomic atomic_uint64_t; + typedef std::atomic atomic_int64_t; + typedef std::atomic atomic_int_least8_t; + typedef std::atomic atomic_uint_least8_t; + typedef std::atomic atomic_int_least16_t; + typedef std::atomic atomic_uint_least16_t; + typedef std::atomic atomic_int_least32_t; + typedef std::atomic atomic_uint_least32_t; + typedef std::atomic atomic_int_least64_t; + typedef std::atomic atomic_uint_least64_t; + typedef std::atomic atomic_int_fast8_t; + typedef std::atomic atomic_uint_fast8_t; + typedef std::atomic atomic_int_fast16_t; + typedef std::atomic atomic_uint_fast16_t; + typedef std::atomic atomic_int_fast32_t; + typedef std::atomic atomic_uint_fast32_t; + typedef std::atomic atomic_int_fast64_t; + typedef std::atomic atomic_uint_fast64_t; + typedef std::atomic atomic_intptr_t; + typedef std::atomic atomic_uintptr_t; + typedef std::atomic atomic_size_t; + typedef std::atomic atomic_ptrdiff_t; + typedef std::atomic atomic_intmax_t; + typedef std::atomic atomic_uintmax_t; +}; + +#endif diff --git a/include/etl/combinations.h b/include/etl/combinations.h new file mode 100644 index 00000000..3ecb8a5e --- /dev/null +++ b/include/etl/combinations.h @@ -0,0 +1,55 @@ +///\file + +/****************************************************************************** +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. +******************************************************************************/ + +#ifndef __ETL_COMBINATIONS__ +#define __ETL_COMBINATIONS__ + +#include "platform.h" +#include "permutations.h" +#include "factorial.h" + +///\defgroup combinations combinations +/// combinations : Calculates K combinations from N. +///\ingroup maths + +namespace etl +{ + //*************************************************************************** + ///\ingroup combinations + /// Calculates combinations. + //*************************************************************************** + template + struct combinations + { + static const size_t value = etl::permutations::value / etl::factorial::value; + }; +} + +#endif diff --git a/include/etl/experimental/spsc_queue.h b/include/etl/experimental/spsc_queue.h deleted file mode 100644 index 253d5db1..00000000 --- a/include/etl/experimental/spsc_queue.h +++ /dev/null @@ -1,57 +0,0 @@ - -#include "platform.h" - -#include "atomic.h" - -struct spsc_queue_implementation -{ - enum - { - UNKNOWN, - STD_ATOMIC, - ETL_ATOMIC, - CMSIS_RTOS2, - FREERTOS, - GCC, - WIN32 - }; -}; - -template -class ispsc_queue; - -template -class spsc_queue : public ispsc_queue; - -#if ETL_ATOMIC_AVAILABLE -template -class ispsc_queue -{ - -}; - -template -class spsc_queue - : public ispsc_queue -{ - -}; -#endif - -#if ETL_OPERATING_SYSTEM == ETL_CMSIS_RTOS2 -template -class spsc_queue - : public ispsc_queue -{ - -}; -#endif - -#if ETL_OPERATING_SYSTEM == ETL_FREERTOS -template -class spsc_queue - : public ispsc_queue -{ - -}; -#endif diff --git a/include/etl/file_error_numbers.txt b/include/etl/file_error_numbers.txt index 7516f471..7680234f 100644 --- a/include/etl/file_error_numbers.txt +++ b/include/etl/file_error_numbers.txt @@ -42,4 +42,7 @@ 42 string_view 43 callback_timer 44 message_timer -45 type_lookup \ No newline at end of file +45 type_lookup +46 queue_spsc_isr +47 queue_spsc_atomic +48 queue_mpmc_mutex diff --git a/include/etl/mutex.h b/include/etl/mutex.h new file mode 100644 index 00000000..87ea68a8 --- /dev/null +++ b/include/etl/mutex.h @@ -0,0 +1,47 @@ +/****************************************************************************** +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. +******************************************************************************/ + +#ifndef __ETL_MUTEX__ +#define __ETL_MUTEX__ + +#include "platform.h" + +#if ETL_CPP11_SUPPORTED == 1 + #include "mutex/mutex_std.h" + #define ETL_HAS_MUTEX 1 +#elif defined(ETL_COMPILER_ARM) + #include "mutex/mutex_arm.h" + #define ETL_HAS_MUTEX 1 +#elif defined(ETL_COMPILER_GCC) + #include "mutex/mutex_gcc_sync.h" + #define ETL_HAS_MUTEX 1 +#else + #define ETL_HAS_MUTEX 0 +#endif + +#endif diff --git a/include/etl/mutex/mutex_arm.h b/include/etl/mutex/mutex_arm.h new file mode 100644 index 00000000..4b33bb77 --- /dev/null +++ b/include/etl/mutex/mutex_arm.h @@ -0,0 +1,34 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 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_MUTEX_ARM__ +#define __ETL_MUTEX_ARM__ + +#include "mutex_gcc_sync.h" + +#endif diff --git a/include/etl/atomic/atomic_windows.h b/include/etl/mutex/mutex_gcc_sync.h similarity index 59% rename from include/etl/atomic/atomic_windows.h rename to include/etl/mutex/mutex_gcc_sync.h index 9b7a2ed3..e61e08b8 100644 --- a/include/etl/atomic/atomic_windows.h +++ b/include/etl/mutex/mutex_gcc_sync.h @@ -26,78 +26,51 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************************/ -#ifndef __ETL_ATOMIC_WINDOWS__ -#define __ETL_ATOMIC_WINDOWS__ +#ifndef __ETL_MUTEX_GCC_SYNC__ +#define __ETL_MUTEX_GCC_SYNC__ #include "../platform.h" #include -#include namespace etl { - class atomic_uint32_t + //*************************************************************************** + ///\ingroup mutex + ///\brief This mutex class is implemented using GCC's __sync functions. + //*************************************************************************** + class mutex { public: - atomic_uint32_t() + mutex() + : flag(0) { - InterlockedExchange(&value, 0); + __sync_lock_release(flag); } - atomic_uint32_t(uint32_t v) + void lock() { - InterlockedExchange(&value, v); + while (__sync_lock_test_and_set(&flag, 1U)) + { + while (flag) + { + } } - atomic_uint32_t& operator =(uint32_t v) + bool try_lock() { - InterlockedExchange(&value, v); - - return *this; + return (__sync_lock_test_and_set(&flag, 1U) == 1U); } - atomic_uint32_t& operator ++() + void unlock() { - InterlockedIncrement(&value); - - return *this; - } - - volatile atomic_uint32_t& operator ++() volatile - { - InterlockedIncrement(&value); - - return *this; - } - - atomic_uint32_t& operator --() - { - InterlockedDecrement(&value); - - return *this; - } - - volatile atomic_uint32_t& operator --() volatile - { - InterlockedDecrement(&value); - - return *this; - } - - operator uint32_t () const - { - return InterlockedAdd((volatile long*)&value, 0); - } - - operator uint32_t() volatile const - { - return InterlockedAdd((volatile long*)&value, 0); + __sync_lock_release(flag); } private: - uint32_t value; + volatile uint32_t flag; }; } diff --git a/include/etl/mutex/mutex_std.h b/include/etl/mutex/mutex_std.h new file mode 100644 index 00000000..35df0ed9 --- /dev/null +++ b/include/etl/mutex/mutex_std.h @@ -0,0 +1,72 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 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_MUTEX_STD__ +#define __ETL_MUTEX_STD__ + +#include "../platform.h" + +#include + +namespace etl +{ + //*************************************************************************** + ///\ingroup mutex + ///\brief This mutex class is implemented using std::mutex. + //*************************************************************************** + class mutex + { + public: + + mutex() + : access() + { + } + + void lock() + { + access.lock(); + } + + bool try_lock() + { + return access.try_lock(); + } + + void unlock() + { + access.unlock(); + } + + private: + + std::mutex access; + }; +} + +#endif diff --git a/include/etl/permutations.h b/include/etl/permutations.h new file mode 100644 index 00000000..9a1feba4 --- /dev/null +++ b/include/etl/permutations.h @@ -0,0 +1,63 @@ +///\file + +/****************************************************************************** +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. +******************************************************************************/ + +#ifndef __ETL_PERMUTATIONS__ +#define __ETL_PERMUTATIONS__ + +#include "platform.h" + +///\defgroup permutations permutations +/// permutations : Calculates K permutations from N. +///\ingroup maths + +namespace etl +{ + //*************************************************************************** + ///\ingroup permutations + /// Calculates permutaions. + //*************************************************************************** + template + struct permutations + { + static const size_t value = NV * permutations::value; + }; + + //*************************************************************************** + /// Calculates permutations. + /// Specialisation for KV == 0. + //*************************************************************************** + template + struct permutations + { + static const size_t value = 1; + }; +} + +#endif diff --git a/include/etl/platform.h b/include/etl/platform.h index 8eaba63e..dd787607 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -51,7 +51,7 @@ SOFTWARE. #undef ETL_NO_NULLPTR_SUPPORT #undef ETL_NO_LARGE_CHAR_SUPPORT #undef ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED -#undef ETL_ATOMIC_SUPPORTED +#undef ETL_STD_ATOMIC_SUPPORTED // Determine the bit width of the platform. #define ETL_PLATFORM_16BIT (UINT16_MAX == UINTPTR_MAX) @@ -67,6 +67,10 @@ SOFTWARE. #pragma warning(disable : 4996) #endif +#if defined(ETL_COMPILER_GCC) + #define GCC_VERSION ((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) +#endif + #if ETL_CPP11_SUPPORTED #define ETL_CONSTEXPR constexpr #else diff --git a/include/etl/profiles/arduino_arm.h b/include/etl/profiles/arduino_arm.h index 2ffe5977..6390676b 100644 --- a/include/etl/profiles/arduino_arm.h +++ b/include/etl/profiles/arduino_arm.h @@ -46,6 +46,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT 1 #define ETL_NO_LARGE_CHAR_SUPPORT 1 #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED 0 -#define ETL_ATOMIC_SUPPORTED 0 #endif diff --git a/include/etl/profiles/armv5.h b/include/etl/profiles/armv5.h index 5d108fb6..cf9db405 100644 --- a/include/etl/profiles/armv5.h +++ b/include/etl/profiles/armv5.h @@ -46,6 +46,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT 1 #define ETL_NO_LARGE_CHAR_SUPPORT 1 #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED 0 -#define ETL_ATOMIC_SUPPORTED 0 #endif diff --git a/include/etl/profiles/armv6.h b/include/etl/profiles/armv6.h index 08b99db2..d3a1a0d3 100644 --- a/include/etl/profiles/armv6.h +++ b/include/etl/profiles/armv6.h @@ -46,6 +46,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT 0 #define ETL_NO_LARGE_CHAR_SUPPORT 0 #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED 1 -#define ETL_ATOMIC_SUPPORTED 1 #endif diff --git a/include/etl/profiles/cpp03.h b/include/etl/profiles/cpp03.h index 4b50addb..8f6f3d90 100644 --- a/include/etl/profiles/cpp03.h +++ b/include/etl/profiles/cpp03.h @@ -46,6 +46,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT 1 #define ETL_NO_LARGE_CHAR_SUPPORT 1 #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED 0 -#define ETL_ATOMIC_SUPPORTED 0 #endif diff --git a/include/etl/profiles/cpp11.h b/include/etl/profiles/cpp11.h index 6af61ee5..f392e56e 100644 --- a/include/etl/profiles/cpp11.h +++ b/include/etl/profiles/cpp11.h @@ -46,6 +46,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT 0 #define ETL_NO_LARGE_CHAR_SUPPORT 0 #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED 1 -#define ETL_ATOMIC_SUPPORTED 1 #endif diff --git a/include/etl/profiles/cpp14.h b/include/etl/profiles/cpp14.h index 4a26d7cb..f3083565 100644 --- a/include/etl/profiles/cpp14.h +++ b/include/etl/profiles/cpp14.h @@ -46,6 +46,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT 0 #define ETL_NO_LARGE_CHAR_SUPPORT 0 #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED 1 -#define ETL_ATOMIC_SUPPORTED 1 #endif diff --git a/include/etl/profiles/gcc_generic.h b/include/etl/profiles/gcc_generic.h index 3e4c4d0a..9bb3cb9f 100644 --- a/include/etl/profiles/gcc_generic.h +++ b/include/etl/profiles/gcc_generic.h @@ -52,6 +52,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT !ETL_CPP11_SUPPORTED #define ETL_NO_LARGE_CHAR_SUPPORT !ETL_CPP11_SUPPORTED #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED ETL_CPP14_SUPPORTED -#define ETL_ATOMIC_SUPPORTED ETL_CPP11_SUPPORTED #endif diff --git a/include/etl/profiles/gcc_linux_x86.h b/include/etl/profiles/gcc_linux_x86.h index 8280d59d..ea98ea8c 100644 --- a/include/etl/profiles/gcc_linux_x86.h +++ b/include/etl/profiles/gcc_linux_x86.h @@ -52,6 +52,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT !ETL_CPP11_SUPPORTED #define ETL_NO_LARGE_CHAR_SUPPORT !ETL_CPP11_SUPPORTED #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED ETL_CPP14_SUPPORTED -#define ETL_ATOMIC_SUPPORTED ETL_CPP11_SUPPORTED #endif diff --git a/include/etl/profiles/gcc_windows_x86.h b/include/etl/profiles/gcc_windows_x86.h index 610cc60f..455e96be 100644 --- a/include/etl/profiles/gcc_windows_x86.h +++ b/include/etl/profiles/gcc_windows_x86.h @@ -52,6 +52,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT !ETL_CPP11_SUPPORTED #define ETL_NO_LARGE_CHAR_SUPPORT !ETL_CPP11_SUPPORTED #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED ETL_CPP14_SUPPORTED -#define ETL_ATOMIC_SUPPORTED ETL_CPP11_SUPPORTED #endif diff --git a/include/etl/profiles/msvc_x86.h b/include/etl/profiles/msvc_x86.h index 5382b62d..9bc6bdd9 100644 --- a/include/etl/profiles/msvc_x86.h +++ b/include/etl/profiles/msvc_x86.h @@ -46,6 +46,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT !ETL_CPP11_SUPPORTED #define ETL_NO_LARGE_CHAR_SUPPORT !ETL_CPP11_SUPPORTED #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED ETL_CPP14_SUPPORTED -#define ETL_ATOMIC_SUPPORTED ETL_CPP11_SUPPORTED #endif diff --git a/include/etl/profiles/ticc.h b/include/etl/profiles/ticc.h index b1478c64..132071ec 100644 --- a/include/etl/profiles/ticc.h +++ b/include/etl/profiles/ticc.h @@ -46,6 +46,5 @@ SOFTWARE. #define ETL_NO_NULLPTR_SUPPORT 1 #define ETL_NO_LARGE_CHAR_SUPPORT 1 #define ETL_CPP11_TYPE_TRAITS_IS_TRIVIAL_SUPPORTED 0 -#define ETL_ATOMIC_SUPPORTED 0 #endif diff --git a/include/etl/queue_mpmc_mutex.h b/include/etl/queue_mpmc_mutex.h new file mode 100644 index 00000000..d6078b9f --- /dev/null +++ b/include/etl/queue_mpmc_mutex.h @@ -0,0 +1,376 @@ +///\file + +/****************************************************************************** +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. +******************************************************************************/ + +#ifndef __ETL_MPMC_QUEUE_MUTEX__ +#define __ETL_MPMC_QUEUE_MUTEX__ + +#include +#include + +#include "platform.h" +#include "alignment.h" +#include "parameter_type.h" +#include "mutex.h" + +#undef ETL_FILE +#define ETL_FILE "48" + +namespace etl +{ + class queue_mpmc_mutex_base + { + public: + + //************************************************************************* + /// How many items can the queue hold. + //************************************************************************* + size_t capacity() const + { + return MAX_SIZE; + } + + //************************************************************************* + /// How many items can the queue hold. + //************************************************************************* + size_t max_size() const + { + return MAX_SIZE; + } + + protected: + + queue_mpmc_mutex_base(size_t max_size_) + : write_index(0), + read_index(0), + current_size(0), + MAX_SIZE(max_size_) + { + } + + //************************************************************************* + /// Calculate the next index. + //************************************************************************* + static size_t get_next_index(size_t index, size_t maximum) + { + ++index; + + if (index == maximum) + { + index = 0; + } + + 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. + + //************************************************************************* + /// Destructor. + //************************************************************************* +#if defined(ETL_POLYMORPHIC_MPMC_QUEUE_MUTEX) || defined(ETL_POLYMORPHIC_CONTAINERS) + public: + virtual ~queue_mpmc_mutex_base() + { + } +#else + protected: + ~queue_mpmc_mutex_base() + { + } +#endif + }; + + //*************************************************************************** + ///\ingroup queue_mpmc + ///\brief This is the base for all queue_mpmc_mutexs that contain a particular type. + ///\details Normally a reference to this type will be taken from a derived queue_mpmc_mutex. + ///\code + /// etl::queue_mpmc_mutex myQueue; + /// etl::iqueue_mpmc_mutex& iQueue = myQueue; + ///\endcode + /// 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 + { + protected: + + typedef typename etl::parameter_type::type parameter_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. + + //************************************************************************* + /// Push a value to the queue. + //************************************************************************* + bool push(parameter_t value) + { + access.lock(); + + bool result = push_implementation(value); + + access.unlock(); + + return result; + } + + //************************************************************************* + /// Pop a value from the queue. + //************************************************************************* + bool pop(reference value) + { + access.lock(); + + bool result = pop_implementation(value); + + access.unlock(); + + return result; + } + + //************************************************************************* + /// Pop a value from the queue and discard. + //************************************************************************* + bool pop() + { + access.lock(); + + bool result = pop_implementation(); + + access.unlock(); + + return result; + } + + //************************************************************************* + /// Clear the queue. + //************************************************************************* + void clear() + { + access.lock(); + + while (pop_implementation()) + { + // Do nothing. + } + + access.unlock(); + } + + //************************************************************************* + /// Is the queue empty? + //************************************************************************* + bool empty() const + { + access.lock(); + + size_t result = (current_size == 0); + + access.unlock(); + + return result; + } + + //************************************************************************* + /// Is the queue full? + //************************************************************************* + bool full() const + { + access.lock(); + + size_t result = (current_size == MAX_SIZE); + + access.unlock(); + + return result; + } + + //************************************************************************* + /// How many items in the queue? + //************************************************************************* + size_t size() const + { + access.lock(); + + size_t result = current_size; + + access.unlock(); + + return result; + } + + //************************************************************************* + /// How much free space available in the queue. + //************************************************************************* + size_t available() const + { + access.lock(); + + size_t result = MAX_SIZE - current_size; + + access.unlock(); + + return result; + } + + protected: + + //************************************************************************* + /// The constructor that is called from derived classes. + //************************************************************************* + iqueue_mpmc_mutex(T* p_buffer_, size_type max_size_) + : queue_mpmc_mutex_base(max_size_), + p_buffer(p_buffer_) + { + } + + private: + + //************************************************************************* + /// 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; + } + + //************************************************************************* + /// 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; + } + + // Disable copy construction and assignment. + iqueue_mpmc_mutex(const iqueue_mpmc_mutex&); + iqueue_mpmc_mutex& operator =(const iqueue_mpmc_mutex&); + + T* p_buffer; ///< The internal buffer. + + mutable etl::mutex access; ///< The object that locks/unlocks access. + }; + + //*************************************************************************** + ///\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. + //*************************************************************************** + template + class queue_mpmc_mutex : public etl::iqueue_mpmc_mutex + { + typedef etl::iqueue_mpmc_mutex base_t; + + public: + + static const size_t MAX_SIZE = SIZE; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + queue_mpmc_mutex() + : base_t(reinterpret_cast(&buffer[0]), MAX_SIZE) + { + } + + //************************************************************************* + /// Destructor. + //************************************************************************* + ~queue_mpmc_mutex() + { + base_t::clear(); + } + + private: + + queue_mpmc_mutex(const queue_mpmc_mutex&); + queue_mpmc_mutex& operator = (const queue_mpmc_mutex&); + + /// The uninitialised buffer of T used in the queue_mpmc_mutex. + typename etl::aligned_storage::value>::type buffer[MAX_SIZE]; + }; +}; + +#endif diff --git a/include/etl/queue_spsc_atomic.h b/include/etl/queue_spsc_atomic.h new file mode 100644 index 00000000..b6be6452 --- /dev/null +++ b/include/etl/queue_spsc_atomic.h @@ -0,0 +1,332 @@ +///\file + +/****************************************************************************** +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. +******************************************************************************/ + +#ifndef __ETL_SPSC_QUEUE_ATOMIC__ +#define __ETL_SPSC_QUEUE_ATOMIC__ + +#include +#include + +#include "platform.h" +#include "alignment.h" +#include "parameter_type.h" +#include "atomic.h" + +#undef ETL_FILE +#define ETL_FILE "47" + +namespace etl +{ + class queue_spsc_atomic_base + { + public: + + //************************************************************************* + /// Is the queue empty? + /// Accurate from the 'pop' thread. + /// 'Not empty' is a guess from the 'push' thread. + //************************************************************************* + bool empty() const + { + return read.load(etl::memory_order_acquire) == write.load(etl::memory_order_acquire); + } + + //************************************************************************* + /// Is the queue full? + /// Accurate from the 'push' thread. + /// 'Not full' is a guess from the 'pop' thread. + //************************************************************************* + bool full() const + { + size_t next_index = get_next_index(write.load(etl::memory_order_acquire), RESERVED); + + return (next_index == read.load(etl::memory_order_acquire)); + } + + //************************************************************************* + /// How many items in the queue? + /// Due to concurrency, this is a guess. + //************************************************************************* + size_t size() const + { + size_t write_index = write.load(etl::memory_order_acquire); + size_t read_index = read.load(etl::memory_order_acquire); + + size_t n; + + if (write_index >= read_index) + { + n = write_index - read_index; + } + else + { + n = RESERVED - read_index + write_index - 1; + } + + return n; + } + + //************************************************************************* + /// How much free space available in the queue. + /// Due to concurrency, this is a guess. + //************************************************************************* + size_t available() const + { + return RESERVED - size() - 1; + } + + //************************************************************************* + /// How many items can the queue hold. + //************************************************************************* + size_t capacity() const + { + return RESERVED - 1; + } + + //************************************************************************* + /// How many items can the queue hold. + //************************************************************************* + size_t max_size() const + { + return RESERVED - 1; + } + + protected: + + queue_spsc_atomic_base(size_t reserved_) + : write(0), + read(0), + RESERVED(reserved_) + { + } + + //************************************************************************* + /// Calculate the next index. + //************************************************************************* + static size_t get_next_index(size_t index, size_t maximum) + { + ++index; + + if (index == maximum) + { + index = 0; + } + + 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. + + private: + + //************************************************************************* + /// Destructor. + //************************************************************************* +#if defined(ETL_POLYMORPHIC_SPSC_QUEUE_ATOMIC) || defined(ETL_POLYMORPHIC_CONTAINERS) + public: + virtual ~queue_spsc_atomic_base() + { + } +#else + protected: + ~queue_spsc_atomic_base() + { + } +#endif + }; + + //*************************************************************************** + ///\ingroup queue_spsc_atomic + ///\brief This is the base for all queue_spscs that contain a particular type. + ///\details Normally a reference to this type will be taken from a derived queue_spsc. + ///\code + /// etl::queue_spsc_atomic myQueue; + /// etl::iqueue_spsc_atomic& iQueue = myQueue; + ///\endcode + /// 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 + { + private: + + typedef typename etl::parameter_type::type parameter_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. + + //************************************************************************* + /// 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); + + if (next_index != read.load(etl::memory_order_acquire)) + { + ::new (&p_buffer[write_index]) T(value); + + write.store(next_index, etl::memory_order_release); + + return true; + } + + // Queue is full. + return false; + } + + //************************************************************************* + /// 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)) + { + // Queue is empty + return false; + } + + size_t next_index = get_next_index(read_index, RESERVED); + + value = p_buffer[read_index]; + p_buffer[read_index].~T(); + + read.store(next_index, etl::memory_order_release); + + return true; + } + + //************************************************************************* + /// Pop a value from the queue and discard. + //************************************************************************* + bool pop() + { + size_t 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); + + p_buffer[read_index].~T(); + + read.store(next_index, etl::memory_order_release); + + return true; + } + + //************************************************************************* + /// Clear the queue. + /// Must be called from thread that pops the queue or when there is no + /// possibility of concurrent access. + //************************************************************************* + void clear() + { + while (pop()) + { + // Do nothing. + } + } + + protected: + + //************************************************************************* + /// The constructor that is called from derived classes. + //************************************************************************* + iqueue_spsc_atomic(T* p_buffer_, size_type reserved_) + : queue_spsc_atomic_base(reserved_), + p_buffer(p_buffer_) + { + } + + private: + + // Disable copy construction and assignment. + iqueue_spsc_atomic(const iqueue_spsc_atomic&); + iqueue_spsc_atomic& operator =(const iqueue_spsc_atomic&); + + T* p_buffer; ///< The internal buffer. + }; + + //*************************************************************************** + ///\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. + //*************************************************************************** + template + class queue_spsc_atomic : public iqueue_spsc_atomic + { + typedef etl::iqueue_spsc_atomic base_t; + + static const size_t RESERVED_SIZE = SIZE + 1; + + public: + + static const size_t MAX_SIZE = SIZE; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + queue_spsc_atomic() + : base_t(reinterpret_cast(&buffer[0]), RESERVED_SIZE) + { + } + + //************************************************************************* + /// Destructor. + //************************************************************************* + ~queue_spsc_atomic() + { + base_t::clear(); + } + + private: + + /// The uninitialised buffer of T used in the queue_spsc. + typename etl::aligned_storage::value>::type buffer[RESERVED_SIZE]; + }; +}; + +#endif \ No newline at end of file diff --git a/include/etl/queue_spsc_isr.h b/include/etl/queue_spsc_isr.h new file mode 100644 index 00000000..b630461c --- /dev/null +++ b/include/etl/queue_spsc_isr.h @@ -0,0 +1,458 @@ +///\file + +/****************************************************************************** +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. +******************************************************************************/ + +#ifndef __ETL_SPSC_QUEUE_ISR__ +#define __ETL_SPSC_QUEUE_ISR__ + +#include +#include + +#include "platform.h" +#include "alignment.h" +#include "parameter_type.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: + + 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. + //************************************************************************* + bool push_from_isr(parameter_t value) + { + return push_implementation(value); + } + + //************************************************************************* + /// Pop a value from the queue from an ISR + //************************************************************************* + bool pop_from_isr(reference value) + { + return pop_implementation(value); + } + + //************************************************************************* + /// Pop a value from the queue from an ISR, and discard. + //************************************************************************* + bool pop_from_isr() + { + return pop_implementation(); + } + + //************************************************************************* + /// How much free space available in the queue. + /// Called from ISR. + //************************************************************************* + size_t available_from_isr() const + { + return MAX_SIZE - current_size; + } + + //************************************************************************* + /// Clear the queue from the ISR. + //************************************************************************* + void clear_from_isr() + { + while (pop_implementation()) + { + // Do nothing. + } + } + + //************************************************************************* + /// Is the queue empty? + /// Called from ISR. + //************************************************************************* + bool empty_from_isr() const + { + return (current_size == 0); + } + + //************************************************************************* + /// Is the queue full? + /// Called from ISR. + //************************************************************************* + bool full_from_isr() const + { + return (current_size == MAX_SIZE); + } + + //************************************************************************* + /// How many items in the queue? + /// Called from ISR. + //************************************************************************* + size_t size_from_isr() const + { + return current_size; + } + + //************************************************************************* + /// How many items can the queue hold. + //************************************************************************* + size_t capacity() const + { + return MAX_SIZE; + } + + //************************************************************************* + /// How many items can the queue hold. + //************************************************************************* + size_t 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; + } + + //************************************************************************* + /// 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_t get_next_index(size_t index, size_t 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_isr. + ///\code + /// etl::queue_spsc_isr_isr myQueue; + /// etl::iqueue_isr& iQueue = myQueue; + ///\endcode + /// 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 + { + private: + + typedef typename queue_spsc_isr_base::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. + + //************************************************************************* + /// Push a value to the queue. + //************************************************************************* + bool push(parameter_t value) + { + TAccess::lock(); + + bool result = this->push_implementation(value); + + TAccess::unlock(); + + return result; + } + + //************************************************************************* + /// Pop a value from the queue. + //************************************************************************* + bool pop(reference value) + { + TAccess::lock(); + + bool result = this->pop_implementation(value); + + TAccess::unlock(); + + return result; + } + + //************************************************************************* + /// Pop a value from the queue and discard. + //************************************************************************* + bool pop() + { + TAccess::lock(); + + bool result = this->pop_implementation(); + + TAccess::unlock(); + + return result; + } + + //************************************************************************* + /// Clear the queue. + //************************************************************************* + void clear() + { + TAccess::lock(); + + while (this->pop_implementation()) + { + // Do nothing. + } + + TAccess::unlock(); + } + + //************************************************************************* + /// Is the queue empty? + //************************************************************************* + bool empty() const + { + TAccess::lock(); + + size_t result = (this->current_size == 0); + + TAccess::unlock(); + + return result; + } + + //************************************************************************* + /// Is the queue full? + //************************************************************************* + bool full() const + { + TAccess::lock(); + + size_t result = (this->current_size == this->MAX_SIZE); + + TAccess::unlock(); + + return result; + } + + //************************************************************************* + /// How many items in the queue? + //************************************************************************* + size_t size() const + { + TAccess::lock(); + + size_t result = this->current_size; + + TAccess::unlock(); + + return result; + } + + //************************************************************************* + /// How much free space available in the queue. + //************************************************************************* + size_t available() const + { + TAccess::lock(); + + size_t result = this->MAX_SIZE - this->current_size; + + TAccess::unlock(); + + return result; + } + + protected: + + //************************************************************************* + /// 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_) + { + } + + private: + + // Disable copy construction and assignment. + iqueue_spsc_isr(const iqueue_spsc_isr&); + iqueue_spsc_isr& operator =(const iqueue_spsc_isr&); + + TAccess access; ///< The object that locks/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 TAccess The type that will lock and unlock interrupts. + //*************************************************************************** + template + class queue_spsc_isr : public etl::iqueue_spsc_isr + { + typedef etl::iqueue_spsc_isr base_t; + + public: + + static const size_t MAX_SIZE = SIZE; + + //************************************************************************* + /// Default constructor. + //************************************************************************* + queue_spsc_isr() + : base_t(reinterpret_cast(&buffer[0]), MAX_SIZE) + { + } + + //************************************************************************* + /// Destructor. + //************************************************************************* + ~queue_spsc_isr() + { + base_t::clear(); + } + + private: + + queue_spsc_isr(const queue_spsc_isr&); + queue_spsc_isr& operator = (const queue_spsc_isr&); + + /// The uninitialised buffer of T used in the queue_spsc_isr. + typename etl::aligned_storage::value>::type buffer[MAX_SIZE]; + }; +}; + +#endif diff --git a/include/etl/spsc_queue.h b/include/etl/spsc_queue.h new file mode 100644 index 00000000..dcd5fe0e --- /dev/null +++ b/include/etl/spsc_queue.h @@ -0,0 +1,47 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2018 jwellbelove, Mark Kitson + +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__ +#define __ETL_SPSC_QUEUE__ + +#include "platform.h" +#include "atomic.h" + +#if ETL_HAS_ATOMIC + #include "spsc_queue_atomic.h" +#endif + +#if defined(ETL_OS_FREERTOS) + #include "spsc_queue_freertos.h" +#endif + +#include "spsc_queue_isr.h" + +#endif \ No newline at end of file diff --git a/include/etl/version.h b/include/etl/version.h index aa0009f1..e1d798f3 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -37,9 +37,9 @@ SOFTWARE. /// Definitions of the ETL version ///\ingroup utilities -#define ETL_VERSION "11.4.0" +#define ETL_VERSION "11.5.0" #define ETL_VERSION_MAJOR 11 -#define ETL_VERSION_MINOR 4 +#define ETL_VERSION_MINOR 5 #define ETL_VERSION_PATCH 0 #endif diff --git a/support/Release notes.txt b/support/Release notes.txt index d418f610..9dd37736 100644 --- a/support/Release notes.txt +++ b/support/Release notes.txt @@ -1,3 +1,7 @@ +=============================================================================== +11.5.0 +Added etl::permutations and etl::combinations constant templates. + =============================================================================== 11.4.0 Added improved atomics. diff --git a/test/codeblocks/ETL.cbp b/test/codeblocks/ETL.cbp index 994dbbe8..69583f71 100644 --- a/test/codeblocks/ETL.cbp +++ b/test/codeblocks/ETL.cbp @@ -81,8 +81,8 @@ - - + + @@ -92,6 +92,7 @@ + @@ -163,6 +164,7 @@ + @@ -183,6 +185,9 @@ + + + @@ -194,6 +199,10 @@ + + + + @@ -317,6 +326,8 @@ + + @@ -376,6 +387,9 @@ + + + diff --git a/test/test_atomic_gcc.cpp b/test/test_atomic_gcc.cpp deleted file mode 100644 index bd6c881c..00000000 --- a/test/test_atomic_gcc.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/****************************************************************************** -The MIT License(MIT) - -Embedded Template Library. -https://github.com/ETLCPP/etl -https://www.etlcpp.com - -Copyright(c) 2017 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 "platform.h" - -#include "atomic/atomic_gcc.h" - -#include - -namespace -{ - SUITE(test_atomic_gcc) - { - //========================================================================= - TEST(test_atomic_compare_exchange_weak_fail) - { - std::atomic compare; - etl::atomic test; - - int actual = 1U; - - compare = actual; - test = actual; - - int compare_expected = 2U; - int test_expected = 2U; - int desired = 3U; - - bool compare_result = compare.compare_exchange_weak(compare_expected, desired); - bool test_result = test.compare_exchange_weak(test_expected, desired); - - CHECK_EQUAL(compare_result, test_result); - CHECK_EQUAL(compare_expected, test_expected); - CHECK_EQUAL(compare.load(), test.load()); - } - - //========================================================================= - TEST(test_atomic_compare_exchange_weak_pass) - { - std::atomic compare; - etl::atomic test; - - int actual = 1U; - - compare = actual; - test = actual; - - int compare_expected = actual; - int test_expected = actual; - int desired = 3U; - - bool compare_result = compare.compare_exchange_weak(compare_expected, desired); - bool test_result = test.compare_exchange_weak(test_expected, desired); - - CHECK_EQUAL(compare_result, test_result); - CHECK_EQUAL(compare_expected, test_expected); - CHECK_EQUAL(compare.load(), test.load()); - } - }; -} diff --git a/test/test_atomic_gcc_sync.cpp b/test/test_atomic_gcc_sync.cpp new file mode 100644 index 00000000..9af9c77e --- /dev/null +++ b/test/test_atomic_gcc_sync.cpp @@ -0,0 +1,484 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 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 "platform.h" +#include "atomic/atomic_std.h" + +#include + +namespace +{ + SUITE(test_atomic_std) + { + //========================================================================= + TEST(test_atomic_integer_is_lock_free) + { + std::atomic compare; + etl::atomic test; + + CHECK_EQUAL(compare.is_lock_free(), test.is_lock_free()); + } + + //========================================================================= + TEST(test_atomic_pointer_is_lock_free) + { + std::atomic compare; + etl::atomic test; + + CHECK_EQUAL(compare.is_lock_free(), test.is_lock_free()); + } + + //========================================================================= + TEST(test_atomic_integer_load) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare.load(), (int)test.load()); + } + + //========================================================================= + TEST(test_atomic_pointer_load) + { + int i; + + std::atomic compare(&i); + etl::atomic test(&i); + + CHECK_EQUAL((int*)compare.load(), (int*)test.load()); + } + + //========================================================================= + TEST(test_atomic_integer_store) + { + std::atomic compare(1); + etl::atomic test(1); + + compare.store(2); + test.store(2); + CHECK_EQUAL((int)compare.load(), (int)test.load()); + } + + //========================================================================= + TEST(test_atomic_pointer_store) + { + int i; + int j; + + std::atomic compare(&i); + etl::atomic test(&i); + + compare.store(&j); + test.store(&j); + CHECK_EQUAL((int*)compare.load(), (int*)test.load()); + } + + //========================================================================= + TEST(test_atomic_integer_assignment) + { + std::atomic compare(1); + etl::atomic test(1); + + compare = 2; + test = 2; + CHECK_EQUAL((int)compare.load(), (int)test.load()); + } + + //========================================================================= + TEST(test_atomic_pointer_assignment) + { + int i; + int j; + + std::atomic compare(&i); + etl::atomic test(&i); + + compare = &j; + test = &j; + CHECK_EQUAL((int*)compare.load(), (int*)test.load()); + } + + //========================================================================= + TEST(test_atomic_operator_integer_pre_increment) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)++compare, (int)++test); + CHECK_EQUAL((int)++compare, (int)++test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_post_increment) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare++, (int)test++); + CHECK_EQUAL((int)compare++, (int)test++); + } + + //========================================================================= + TEST(test_atomic_operator_integer_pre_decrement) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)--compare, (int)--test); + CHECK_EQUAL((int)--compare, (int)--test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_post_decrement) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare--, (int)test--); + CHECK_EQUAL((int)compare--, (int)test--); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_pre_increment) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + CHECK_EQUAL((int*)++compare, (int*)++test); + CHECK_EQUAL((int*)++compare, (int*)++test); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_post_increment) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + CHECK_EQUAL((int*)compare++, (int*)test++); + CHECK_EQUAL((int*)compare++, (int*)test++); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_pre_decrement) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[3]); + etl::atomic test(&data[3]); + + CHECK_EQUAL((int*)--compare, (int*)--test); + CHECK_EQUAL((int*)--compare, (int*)--test); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_post_decrement) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[3]); + etl::atomic test(&data[3]); + + CHECK_EQUAL((int*)compare--, (int*)test--); + CHECK_EQUAL((int*)compare--, (int*)test--); + } + + //========================================================================= + TEST(test_atomic_operator_integer_fetch_add) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare.fetch_add(2), (int)test.fetch_add(2)); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_fetch_add) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + CHECK_EQUAL((int*)compare.fetch_add(std::ptrdiff_t(10)), (int*)test.fetch_add(std::ptrdiff_t(10))); + } + + //========================================================================= + TEST(test_atomic_operator_integer_plus_equals) + { + std::atomic compare(1); + etl::atomic test(1); + + compare += 2; + test += 2; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_plus_equals) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + compare += 2; + test += 2; + + CHECK_EQUAL((int*)compare, (int*)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_minus_equals) + { + std::atomic compare(1); + etl::atomic test(1); + + compare += 2; + test += 2; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_minus_equals) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[3]); + etl::atomic test(&data[3]); + + compare += 2; + test += 2; + + CHECK_EQUAL((int*)compare, (int*)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_and_equals) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + compare &= 0x55AA55AA; + test &= 0x55AA55AA; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_or_equals) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + compare |= 0x55AA55AA; + test |= 0x55AA55AA; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_xor_equals) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + compare ^= 0x55AA55AA; + test ^= 0x55AA55AA; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_fetch_sub) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare.fetch_sub(2), (int)test.fetch_sub(2)); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_fetch_sub) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + CHECK_EQUAL((int*)compare.fetch_add(std::ptrdiff_t(10)), (int*)test.fetch_add(std::ptrdiff_t(10))); + } + + //========================================================================= + TEST(test_atomic_operator_fetch_and) + { + std::atomic compare(0xFFFFFFFF); + etl::atomic test(0xFFFFFFFF); + + CHECK_EQUAL((int)compare.fetch_and(0x55AA55AA), (int)test.fetch_and(0x55AA55AA)); + } + + //========================================================================= + TEST(test_atomic_operator_fetch_or) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + CHECK_EQUAL((int)compare.fetch_or(0x55AA55AA), (int)test.fetch_or(0x55AA55AA)); + } + + //========================================================================= + TEST(test_atomic_operator_fetch_xor) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + CHECK_EQUAL((int)compare.fetch_xor(0x55AA55AA), (int)test.fetch_xor(0x55AA55AA)); + } + + //========================================================================= + TEST(test_atomic_integer_exchange) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare.exchange(2), (int)test.exchange(2)); + } + + //========================================================================= + TEST(test_atomic_pointer_exchange) + { + int i; + int j; + + std::atomic compare(&i); + etl::atomic test(&i); + + CHECK_EQUAL((int*)compare.exchange(&j), (int*)test.exchange(&j)); + } + + //========================================================================= + TEST(test_atomic_compare_exchange_weak_fail) + { + std::atomic compare; + etl::atomic test; + + int actual = 1U; + + compare = actual; + test = actual; + + int compare_expected = 2U; + int test_expected = 2U; + int desired = 3U; + + bool compare_result = compare.compare_exchange_weak(compare_expected, desired); + bool test_result = test.compare_exchange_weak(test_expected, desired); + + CHECK_EQUAL(compare_result, test_result); + CHECK_EQUAL(compare_expected, test_expected); + CHECK_EQUAL(compare.load(), test.load()); + } + + //========================================================================= + TEST(test_atomic_compare_exchange_weak_pass) + { + std::atomic compare; + etl::atomic test; + + int actual = 1U; + + compare = actual; + test = actual; + + int compare_expected = actual; + int test_expected = actual; + int desired = 3U; + + bool compare_result = compare.compare_exchange_weak(compare_expected, desired); + bool test_result = test.compare_exchange_weak(test_expected, desired); + + CHECK_EQUAL(compare_result, test_result); + CHECK_EQUAL(compare_expected, test_expected); + CHECK_EQUAL(compare.load(), test.load()); + } + + //========================================================================= + TEST(test_atomic_compare_exchange_strong_fail) + { + std::atomic compare; + etl::atomic test; + + int actual = 1U; + + compare = actual; + test = actual; + + int compare_expected = 2U; + int test_expected = 2U; + int desired = 3U; + + bool compare_result = compare.compare_exchange_strong(compare_expected, desired); + bool test_result = test.compare_exchange_strong(test_expected, desired); + + CHECK_EQUAL(compare_result, test_result); + CHECK_EQUAL(compare_expected, test_expected); + CHECK_EQUAL(compare.load(), test.load()); + } + + //========================================================================= + TEST(test_atomic_compare_exchange_strong_pass) + { + std::atomic compare; + etl::atomic test; + + int actual = 1U; + + compare = actual; + test = actual; + + int compare_expected = actual; + int test_expected = actual; + int desired = 3U; + + bool compare_result = compare.compare_exchange_strong(compare_expected, desired); + bool test_result = test.compare_exchange_strong(test_expected, desired); + + CHECK_EQUAL(compare_result, test_result); + CHECK_EQUAL(compare_expected, test_expected); + CHECK_EQUAL(compare.load(), test.load()); + } + }; +} diff --git a/test/test_atomic_std.cpp b/test/test_atomic_std.cpp new file mode 100644 index 00000000..9af9c77e --- /dev/null +++ b/test/test_atomic_std.cpp @@ -0,0 +1,484 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 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 "platform.h" +#include "atomic/atomic_std.h" + +#include + +namespace +{ + SUITE(test_atomic_std) + { + //========================================================================= + TEST(test_atomic_integer_is_lock_free) + { + std::atomic compare; + etl::atomic test; + + CHECK_EQUAL(compare.is_lock_free(), test.is_lock_free()); + } + + //========================================================================= + TEST(test_atomic_pointer_is_lock_free) + { + std::atomic compare; + etl::atomic test; + + CHECK_EQUAL(compare.is_lock_free(), test.is_lock_free()); + } + + //========================================================================= + TEST(test_atomic_integer_load) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare.load(), (int)test.load()); + } + + //========================================================================= + TEST(test_atomic_pointer_load) + { + int i; + + std::atomic compare(&i); + etl::atomic test(&i); + + CHECK_EQUAL((int*)compare.load(), (int*)test.load()); + } + + //========================================================================= + TEST(test_atomic_integer_store) + { + std::atomic compare(1); + etl::atomic test(1); + + compare.store(2); + test.store(2); + CHECK_EQUAL((int)compare.load(), (int)test.load()); + } + + //========================================================================= + TEST(test_atomic_pointer_store) + { + int i; + int j; + + std::atomic compare(&i); + etl::atomic test(&i); + + compare.store(&j); + test.store(&j); + CHECK_EQUAL((int*)compare.load(), (int*)test.load()); + } + + //========================================================================= + TEST(test_atomic_integer_assignment) + { + std::atomic compare(1); + etl::atomic test(1); + + compare = 2; + test = 2; + CHECK_EQUAL((int)compare.load(), (int)test.load()); + } + + //========================================================================= + TEST(test_atomic_pointer_assignment) + { + int i; + int j; + + std::atomic compare(&i); + etl::atomic test(&i); + + compare = &j; + test = &j; + CHECK_EQUAL((int*)compare.load(), (int*)test.load()); + } + + //========================================================================= + TEST(test_atomic_operator_integer_pre_increment) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)++compare, (int)++test); + CHECK_EQUAL((int)++compare, (int)++test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_post_increment) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare++, (int)test++); + CHECK_EQUAL((int)compare++, (int)test++); + } + + //========================================================================= + TEST(test_atomic_operator_integer_pre_decrement) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)--compare, (int)--test); + CHECK_EQUAL((int)--compare, (int)--test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_post_decrement) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare--, (int)test--); + CHECK_EQUAL((int)compare--, (int)test--); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_pre_increment) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + CHECK_EQUAL((int*)++compare, (int*)++test); + CHECK_EQUAL((int*)++compare, (int*)++test); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_post_increment) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + CHECK_EQUAL((int*)compare++, (int*)test++); + CHECK_EQUAL((int*)compare++, (int*)test++); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_pre_decrement) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[3]); + etl::atomic test(&data[3]); + + CHECK_EQUAL((int*)--compare, (int*)--test); + CHECK_EQUAL((int*)--compare, (int*)--test); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_post_decrement) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[3]); + etl::atomic test(&data[3]); + + CHECK_EQUAL((int*)compare--, (int*)test--); + CHECK_EQUAL((int*)compare--, (int*)test--); + } + + //========================================================================= + TEST(test_atomic_operator_integer_fetch_add) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare.fetch_add(2), (int)test.fetch_add(2)); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_fetch_add) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + CHECK_EQUAL((int*)compare.fetch_add(std::ptrdiff_t(10)), (int*)test.fetch_add(std::ptrdiff_t(10))); + } + + //========================================================================= + TEST(test_atomic_operator_integer_plus_equals) + { + std::atomic compare(1); + etl::atomic test(1); + + compare += 2; + test += 2; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_plus_equals) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + compare += 2; + test += 2; + + CHECK_EQUAL((int*)compare, (int*)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_minus_equals) + { + std::atomic compare(1); + etl::atomic test(1); + + compare += 2; + test += 2; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_minus_equals) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[3]); + etl::atomic test(&data[3]); + + compare += 2; + test += 2; + + CHECK_EQUAL((int*)compare, (int*)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_and_equals) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + compare &= 0x55AA55AA; + test &= 0x55AA55AA; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_or_equals) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + compare |= 0x55AA55AA; + test |= 0x55AA55AA; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_xor_equals) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + compare ^= 0x55AA55AA; + test ^= 0x55AA55AA; + + CHECK_EQUAL((int)compare, (int)test); + } + + //========================================================================= + TEST(test_atomic_operator_integer_fetch_sub) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare.fetch_sub(2), (int)test.fetch_sub(2)); + } + + //========================================================================= + TEST(test_atomic_operator_pointer_fetch_sub) + { + int data[] = { 1, 2, 3, 4 }; + + std::atomic compare(&data[0]); + etl::atomic test(&data[0]); + + CHECK_EQUAL((int*)compare.fetch_add(std::ptrdiff_t(10)), (int*)test.fetch_add(std::ptrdiff_t(10))); + } + + //========================================================================= + TEST(test_atomic_operator_fetch_and) + { + std::atomic compare(0xFFFFFFFF); + etl::atomic test(0xFFFFFFFF); + + CHECK_EQUAL((int)compare.fetch_and(0x55AA55AA), (int)test.fetch_and(0x55AA55AA)); + } + + //========================================================================= + TEST(test_atomic_operator_fetch_or) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + CHECK_EQUAL((int)compare.fetch_or(0x55AA55AA), (int)test.fetch_or(0x55AA55AA)); + } + + //========================================================================= + TEST(test_atomic_operator_fetch_xor) + { + std::atomic compare(0x0000FFFF); + etl::atomic test(0x0000FFFF); + + CHECK_EQUAL((int)compare.fetch_xor(0x55AA55AA), (int)test.fetch_xor(0x55AA55AA)); + } + + //========================================================================= + TEST(test_atomic_integer_exchange) + { + std::atomic compare(1); + etl::atomic test(1); + + CHECK_EQUAL((int)compare.exchange(2), (int)test.exchange(2)); + } + + //========================================================================= + TEST(test_atomic_pointer_exchange) + { + int i; + int j; + + std::atomic compare(&i); + etl::atomic test(&i); + + CHECK_EQUAL((int*)compare.exchange(&j), (int*)test.exchange(&j)); + } + + //========================================================================= + TEST(test_atomic_compare_exchange_weak_fail) + { + std::atomic compare; + etl::atomic test; + + int actual = 1U; + + compare = actual; + test = actual; + + int compare_expected = 2U; + int test_expected = 2U; + int desired = 3U; + + bool compare_result = compare.compare_exchange_weak(compare_expected, desired); + bool test_result = test.compare_exchange_weak(test_expected, desired); + + CHECK_EQUAL(compare_result, test_result); + CHECK_EQUAL(compare_expected, test_expected); + CHECK_EQUAL(compare.load(), test.load()); + } + + //========================================================================= + TEST(test_atomic_compare_exchange_weak_pass) + { + std::atomic compare; + etl::atomic test; + + int actual = 1U; + + compare = actual; + test = actual; + + int compare_expected = actual; + int test_expected = actual; + int desired = 3U; + + bool compare_result = compare.compare_exchange_weak(compare_expected, desired); + bool test_result = test.compare_exchange_weak(test_expected, desired); + + CHECK_EQUAL(compare_result, test_result); + CHECK_EQUAL(compare_expected, test_expected); + CHECK_EQUAL(compare.load(), test.load()); + } + + //========================================================================= + TEST(test_atomic_compare_exchange_strong_fail) + { + std::atomic compare; + etl::atomic test; + + int actual = 1U; + + compare = actual; + test = actual; + + int compare_expected = 2U; + int test_expected = 2U; + int desired = 3U; + + bool compare_result = compare.compare_exchange_strong(compare_expected, desired); + bool test_result = test.compare_exchange_strong(test_expected, desired); + + CHECK_EQUAL(compare_result, test_result); + CHECK_EQUAL(compare_expected, test_expected); + CHECK_EQUAL(compare.load(), test.load()); + } + + //========================================================================= + TEST(test_atomic_compare_exchange_strong_pass) + { + std::atomic compare; + etl::atomic test; + + int actual = 1U; + + compare = actual; + test = actual; + + int compare_expected = actual; + int test_expected = actual; + int desired = 3U; + + bool compare_result = compare.compare_exchange_strong(compare_expected, desired); + bool test_result = test.compare_exchange_strong(test_expected, desired); + + CHECK_EQUAL(compare_result, test_result); + CHECK_EQUAL(compare_expected, test_expected); + CHECK_EQUAL(compare.load(), test.load()); + } + }; +} diff --git a/test/test_maths.cpp b/test/test_maths.cpp index 81832173..4d42f3c6 100644 --- a/test/test_maths.cpp +++ b/test/test_maths.cpp @@ -33,6 +33,8 @@ SOFTWARE. #include "fibonacci.h" #include "factorial.h" #include "sqrt.h" +#include "permutations.h" +#include "combinations.h" namespace { @@ -53,6 +55,36 @@ namespace return 1; } + size_t factorial(size_t n) + { + if (n == 1) + { + return 1; + } + else + { + return n * factorial(n - 1); + } + } + + size_t permutations(size_t n, size_t k) + { + size_t p = 1; + + for (size_t i = 0; i < k; ++i) + { + p *= n; + --n; + } + + return p; + } + + size_t combinations(size_t n, size_t k) + { + return permutations(n, k) / factorial(k); + } + SUITE(test_maths) { //************************************************************************* @@ -380,5 +412,38 @@ namespace CHECK_EQUAL(sqrt(9), etl::sqrt<9>::value); CHECK_EQUAL(sqrt(10), etl::sqrt<10>::value); } + + //************************************************************************* + TEST(test_permutations) + { + size_t actual; + + + CHECK_EQUAL((permutations( 6, 2)), (actual = etl::permutations< 6, 2>::value)); + CHECK_EQUAL((permutations( 7, 3)), (actual = etl::permutations< 7, 3>::value)); + CHECK_EQUAL((permutations( 8, 4)), (actual = etl::permutations< 8, 4>::value)); + CHECK_EQUAL((permutations( 9, 5)), (actual = etl::permutations< 9, 5>::value)); + CHECK_EQUAL((permutations(10, 6)), (actual = etl::permutations<10, 6>::value)); + CHECK_EQUAL((permutations(11, 7)), (actual = etl::permutations<11, 7>::value)); + CHECK_EQUAL((permutations(12, 8)), (actual = etl::permutations<12, 8>::value)); + CHECK_EQUAL((permutations(13, 9)), (actual = etl::permutations<13, 9>::value)); + CHECK_EQUAL((permutations(14, 10)), (actual = etl::permutations<14, 10>::value)); + } + + //************************************************************************* + TEST(test_combinations) + { + size_t actual; + + CHECK_EQUAL((combinations( 6, 2)), (actual = etl::combinations< 6, 2>::value)); + CHECK_EQUAL((combinations( 7, 3)), (actual = etl::combinations< 7, 3>::value)); + CHECK_EQUAL((combinations( 8, 4)), (actual = etl::combinations< 8, 4>::value)); + CHECK_EQUAL((combinations( 9, 5)), (actual = etl::combinations< 9, 5>::value)); + CHECK_EQUAL((combinations(10, 6)), (actual = etl::combinations<10, 6>::value)); + CHECK_EQUAL((combinations(11, 7)), (actual = etl::combinations<11, 7>::value)); + CHECK_EQUAL((combinations(12, 8)), (actual = etl::combinations<12, 8>::value)); + CHECK_EQUAL((combinations(13, 9)), (actual = etl::combinations<13, 9>::value)); + CHECK_EQUAL((combinations(14, 10)), (actual = etl::combinations<14, 10>::value)); + } }; } diff --git a/test/test_queue_mpmc_mutex.cpp b/test/test_queue_mpmc_mutex.cpp new file mode 100644 index 00000000..b9f39105 --- /dev/null +++ b/test/test_queue_mpmc_mutex.cpp @@ -0,0 +1,406 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://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 "queue_mpmc_mutex.h" + +#if defined(ETL_COMPILER_MICROSOFT) + #include +#endif + +#define REALTIME_TEST 1 + +namespace +{ + SUITE(test_queue_mpmc_mutex) + { + //************************************************************************* + TEST(test_constructor) + { + etl::queue_mpmc_mutex queue; + + CHECK_EQUAL(4U, queue.max_size()); + CHECK_EQUAL(4U, queue.capacity()); + } + + //************************************************************************* + TEST(test_size_push_pop) + { + etl::queue_mpmc_mutex 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) + { + etl::queue_mpmc_mutex queue; + + etl::iqueue_mpmc_mutex& 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) + { + etl::queue_mpmc_mutex 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_clear) + { + etl::queue_mpmc_mutex 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) + { + etl::queue_mpmc_mutex queue; + CHECK(queue.empty()); + + queue.push(1); + CHECK(!queue.empty()); + + queue.clear(); + CHECK(queue.empty()); + + queue.push(1); + CHECK(!queue.empty()); + } + + //************************************************************************* + TEST(test_full) + { + etl::queue_mpmc_mutex 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.cpp b/test/test_queue_spsc_atomic.cpp new file mode 100644 index 00000000..4e5542a4 --- /dev/null +++ b/test/test_queue_spsc_atomic.cpp @@ -0,0 +1,312 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://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 "queue_spsc_atomic.h" + +#if defined(ETL_COMPILER_MICROSOFT) + #include +#endif + +#define REALTIME_TEST 0 + +namespace +{ + SUITE(test_queue_atomic) + { + //************************************************************************* + TEST(test_constructor) + { + etl::queue_spsc_atomic queue; + + CHECK_EQUAL(4U, queue.max_size()); + CHECK_EQUAL(4U, queue.capacity()); + } + + //************************************************************************* + TEST(test_size_push_pop) + { + etl::queue_spsc_atomic 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) + { + etl::queue_spsc_atomic queue; + + etl::iqueue_spsc_atomic& 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) + { + etl::queue_spsc_atomic 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_clear) + { + etl::queue_spsc_atomic 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) + { + etl::queue_spsc_atomic queue; + CHECK(queue.empty()); + + queue.push(1); + CHECK(!queue.empty()); + + queue.clear(); + CHECK(queue.empty()); + + queue.push(1); + CHECK(!queue.empty()); + } + + //************************************************************************* + TEST(test_full) + { + etl::queue_spsc_atomic 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.cpp b/test/test_queue_spsc_isr.cpp new file mode 100644 index 00000000..91667eb9 --- /dev/null +++ b/test/test_queue_spsc_isr.cpp @@ -0,0 +1,569 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +http://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 "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; + + SUITE(test_queue_isr) + { + //************************************************************************* + TEST(test_constructor) + { + Access::clear(); + + etl::queue_spsc_isr 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(); + + etl::queue_spsc_isr 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(); + + etl::queue_spsc_isr queue; + + etl::iqueue_spsc_isr& 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(); + + etl::queue_spsc_isr 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_clear) + { + Access::clear(); + + etl::queue_spsc_isr 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(); + + etl::queue_spsc_isr 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(); + + etl::queue_spsc_isr 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 a7df379f..87c633a5 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -327,8 +327,10 @@ - + + + @@ -341,7 +343,12 @@ + + + + + @@ -355,6 +362,9 @@ + + + @@ -539,6 +549,7 @@ + @@ -666,6 +677,9 @@ + + + true true diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index 675dce75..d45f1a84 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -70,6 +70,9 @@ {4d08353c-b393-47c7-a9eb-c9f2f8c25887} + + {0bcdf7f9-8e2b-4f70-932b-bde56404f421} + @@ -561,9 +564,6 @@ ETL\Utilities\Atomic - - ETL\Utilities\Atomic - Source Files\ECL @@ -588,6 +588,39 @@ ECL + + ETL\Utilities\Atomic + + + ETL\Utilities\Atomic + + + ETL\Containers + + + ETL\Containers + + + ETL\Containers + + + ETL\Utilities\Mutex + + + ETL\Utilities\Mutex + + + ETL\Utilities\Mutex + + + ETL\Utilities + + + ETL\Maths + + + ETL\Maths + @@ -974,6 +1007,18 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files +