@@ -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>(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; | |||
} | |||
@@ -191,21 +191,21 @@ class edeque : public deque<Data_t, N, SemiAtomic> { | |||
} | |||
//! @} | |||
//! \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); | |||
@@ -293,9 +293,14 @@ class ring_iterator<Iter_t, N, true> { | |||
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<size_t>(++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<Iter_t, N, true> { | |||
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; | |||
} | |||
//! @} | |||
@@ -0,0 +1,42 @@ | |||
/*! | |||
* \file tbx.h | |||
* \brief | |||
* Main tbx header | |||
* | |||
* \copyright Copyright (C) 2021 Christos Choutouridis <christos@choutouridis.net> | |||
* | |||
* <dl class=\"section copyright\"><dt>License</dt><dd> | |||
* 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. | |||
* </dd></dl> | |||
*/ | |||
#ifndef TBX_H_ | |||
#define TBX_H_ | |||
#include <com/sequencer.h> | |||
#include <cont/deque.h> | |||
#include <cont/edeque.h> | |||
#include <cont/queue.h> | |||
#include <cont/equeue.h> | |||
#include <drv/cli_device.h> | |||
#endif /* TBX_H_ */ |
@@ -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 | |||
@@ -34,6 +34,14 @@ | |||
#include <array> | |||
#include <type_traits> | |||
#include <cstring> | |||
#ifndef WIN_TRHEADS | |||
#include <mutex> | |||
#include <thread> | |||
#else | |||
#include <mingw.thread.h> | |||
#include <mingw.mutex.h> | |||
#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<int, N, true> 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<N ; ) { | |||
result[i] = q.pop_front(); | |||
if (result[i] != int{}) | |||
++i; | |||
} | |||
}; | |||
auto pop_back = [&](){ | |||
for (size_t i=0 ; i<N ; ) { | |||
result[i] = q.pop_back(); | |||
if (result[i] != int{}) | |||
++i; | |||
} | |||
}; | |||
std::memset(result, 0, sizeof result); | |||
std::thread th1 (push_front); | |||
std::thread th2 (pop_back); | |||
th1.join(); | |||
th2.join(); | |||
for (size_t i=0 ; i<N ; ++i) | |||
EXPECT_EQ (result[i], (int)i+1); | |||
std::memset(result, 0, sizeof result); | |||
std::thread th3 (push_back); | |||
std::thread th4 (pop_front); | |||
th3.join(); | |||
th4.join(); | |||
for (size_t i=0 ; i<N ; ++i) | |||
EXPECT_EQ (result[i], (int)i+1); | |||
} | |||
} |