diff --git a/include/cont/deque.h b/include/cont/deque.h index 8bcd979..ea4c27b 100644 --- a/include/cont/deque.h +++ b/include/cont/deque.h @@ -86,13 +86,18 @@ class deque { constexpr deque () noexcept : data_{}, f{data_.data(), N}, - r{data_.data()} { } + r{data_.data()} { + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); + } //! fill contructor constexpr deque(const Data_t& value) noexcept { data_.fill(value); f = iterator(data_.data(), N); r = iterator(data_.data(), N); + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); } //! Initializer list contructor @@ -100,7 +105,10 @@ class deque { constexpr deque(It&& ...it) noexcept : data_{{std::forward(it)...}}, f(data_.data(), N), - r(data_.data(), sizeof...(It)) { } + r(data_.data(), sizeof...(It)) { + if constexpr (SemiAtomic) + std::atomic_thread_fence(std::memory_order_release); + } deque(const deque&) = delete; //!< No copies deque& operator= (const deque&) = delete; //!< No copy assignments @@ -132,10 +140,10 @@ class deque { public: //! \return The size of the deque. The items currently in queue. constexpr size_t size() noexcept { - return full() ? N: (r - f) -1; + return r - (f +1); } constexpr size_t size() const noexcept { - return full() ? N: (r - f) -1; + return r - (f +1); } //! \return The maximum size of the deque. The items the queue can hold. constexpr size_t max_size() noexcept { return N; } @@ -144,11 +152,8 @@ class deque { //! \return True if the deque is empty constexpr bool empty() noexcept { return size() == 0 ? true : false; } //! \return True if the deque is full - constexpr bool full() noexcept { - if constexpr (SemiAtomic) - std::atomic_thread_fence(std::memory_order_acquire); - return (r == f) ? true : false; - } + constexpr bool full() noexcept { return size() == N ? true : false; } + //! @} //! \name Member access @@ -166,36 +171,26 @@ class deque { //! \param it The item to push constexpr void push_front (const Data_t& it) noexcept { if (full()) return; - if constexpr (SemiAtomic) - std::atomic_thread_fence(std::memory_order_acquire); - *f-- = it; - if constexpr (SemiAtomic) - std::atomic_thread_fence(std::memory_order_release); + *f = it; + --f; // keep this separate for thread safety + } + //! \brief Push an item in the back of the deque + //! \param it The item to push + constexpr void push_back (const Data_t& it) noexcept { + if (full()) return; + *r = it; + ++r; // keep this separate for thread safety } //! \brief Extract an item from the front of the deque and remove it from the deque //! \param it The item to push constexpr Data_t pop_front () noexcept { if (empty()) return Data_t{}; - if constexpr (SemiAtomic) - std::atomic_thread_fence(std::memory_order_acquire); return *++f; } - //! \brief Push an item in the back of the deque - //! \param it The item to push - constexpr void push_back (const Data_t& it) noexcept { - if (full()) return; - if constexpr (SemiAtomic) - std::atomic_thread_fence(std::memory_order_acquire); - *r++ = it; - if constexpr (SemiAtomic) - std::atomic_thread_fence(std::memory_order_release); - } //! \brief Extract an item from the back of the deque and remove it from the deque //! \param it The item to push constexpr Data_t pop_back () noexcept { if (empty()) return Data_t{}; - if constexpr (SemiAtomic) - std::atomic_thread_fence(std::memory_order_acquire); return *--r; } diff --git a/include/cont/edeque.h b/include/cont/edeque.h index 146770e..6a33f44 100644 --- a/include/cont/edeque.h +++ b/include/cont/edeque.h @@ -191,21 +191,21 @@ class edeque : public deque { } //! @} - //! \name Base class overwrites + //! \name Base class uses and overwrites //! @{ void push_front (const Data_t& it) noexcept { base_type::push_front(it); check_trigger_push_async_(it); } + void push_back (const Data_t& it) noexcept { + base_type::push_back(it); + check_trigger_push_async_(it); + } Data_t pop_front () noexcept { Data_t t = base_type::pop_front(); check_trigger_pop_async_(t); return t; } - void push_back (const Data_t& it) noexcept { - base_type::push_back(it); - check_trigger_push_async_(it); - } Data_t pop_back () noexcept { Data_t t = base_type::pop_back(); check_trigger_pop_async_(t); diff --git a/include/core/ring_iterator.h b/include/core/ring_iterator.h index 987cefe..ff399e6 100644 --- a/include/core/ring_iterator.h +++ b/include/core/ring_iterator.h @@ -293,9 +293,14 @@ class ring_iterator { return *this; } constexpr ring_iterator operator++(int) noexcept { - ring_iterator it = *this; - this->operator ++(); - return it; + ring_iterator ret = *this; + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + if (static_cast(++itnew - base_) >= N) + itnew = base_; + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acq_rel)); + return ret; } //! @} @@ -312,9 +317,14 @@ class ring_iterator { return *this; } constexpr ring_iterator operator--(int) noexcept { - ring_iterator it = *this; - this->operator --(); - return it; + ring_iterator ret = *this; + Iter_t itnew, it = iter_.load(std::memory_order_acquire); + do { + itnew = it; + if (--itnew < base_) + itnew = base_ + N -1; + } while (!iter_.compare_exchange_weak(it, itnew, std::memory_order_acq_rel)); + return ret; } //! @} diff --git a/include/tbx.h b/include/tbx.h new file mode 100644 index 0000000..df5c025 --- /dev/null +++ b/include/tbx.h @@ -0,0 +1,42 @@ +/*! + * \file tbx.h + * \brief + * Main tbx header + * + * \copyright Copyright (C) 2021 Christos Choutouridis + * + *
License
+ * The MIT License (MIT) + * + * 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 TBX_H_ +#define TBX_H_ + +#include + +#include +#include +#include +#include + +#include + +#endif /* TBX_H_ */ diff --git a/test/Makefile b/test/Makefile index 05f7a68..cf90993 100644 --- a/test/Makefile +++ b/test/Makefile @@ -188,7 +188,7 @@ $(BUILD_DIR)/$(TARGET): $(OBJ) @mkdir -p $(@D) @echo Linking to target: $(TARGET) $(DOCKER) $(CXX) $(LDFLAGS) $(MAP_FLAG) -o $(@D)/$(TARGET) $(OBJ) - $(DOCKER) $(ODUMP) -h -S $(BUILD_DIR)/$(TARGET) > $(BUILD_DIR)/$(basename $(TARGET)).list +# $(DOCKER) $(ODUMP) -h -S $(BUILD_DIR)/$(TARGET) > $(BUILD_DIR)/$(basename $(TARGET)).list # $(DOCKER) $(OCOPY) -O ihex $(BUILD_DIR)/$(TARGET) $(BUILD_DIR)/$(basename $(TARGET)).hex @echo @echo Print size information diff --git a/test/tests/deque.cpp b/test/tests/deque.cpp index 9bc9516..e789ad7 100644 --- a/test/tests/deque.cpp +++ b/test/tests/deque.cpp @@ -34,6 +34,14 @@ #include #include +#include +#ifndef WIN_TRHEADS +#include +#include +#else +#include +#include +#endif namespace Tdeque { using namespace tbx; @@ -415,4 +423,47 @@ namespace Tdeque { EXPECT_EQ(9, check_it); // run through all } + + TEST(Tdeque, race) { + constexpr size_t N = 1000000; + deque q; + int result[N]; + + auto push_front = [&](){ + for (size_t i=1 ; i<=N ; ++i) q.push_front(i); + }; + auto push_back = [&](){ + for (size_t i=1 ; i<=N ; ++i) q.push_back(i); + }; + auto pop_front = [&](){ + for (size_t i=0 ; i