From 1e714940344f09523e6ab0faee86464b0eed0645 Mon Sep 17 00:00:00 2001 From: Thomas Novotny Date: Wed, 3 Aug 2016 00:50:07 +0200 Subject: [PATCH 1/4] minimal changes to allow single move values to be pushed and poped --- include/boost/lockfree/spsc_queue.hpp | 76 ++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/include/boost/lockfree/spsc_queue.hpp b/include/boost/lockfree/spsc_queue.hpp index 5ecfb2a..c1ea4f8 100644 --- a/include/boost/lockfree/spsc_queue.hpp +++ b/include/boost/lockfree/spsc_queue.hpp @@ -98,7 +98,24 @@ class ringbuffer_base return write_available(write_index, read_index, max_size); } - bool push(T const & t, T * buffer, size_t max_size) + bool push( T && t, T * buffer, size_t max_size ) + { + const size_t write_index = write_index_.load( memory_order_relaxed ); // only written from push thread + const size_t next = next_index( write_index, max_size ); + + if( next == read_index_.load( memory_order_acquire ) ) + return false; /* ringbuffer is full */ + + new (buffer + write_index) T(std::move(t)); // move-construct + + write_index_.store( next, memory_order_release ); + + return true; + } + + template + typename std::enable_if< std::is_copy_constructible::value, bool >::type + push(T const & t, T * buffer, size_t max_size) { const size_t write_index = write_index_.load(memory_order_relaxed); // only written from push thread const size_t next = next_index(write_index, max_size); @@ -382,18 +399,23 @@ class ringbuffer_base return write_index == read_index; } - template< class OutputIterator > - OutputIterator copy_and_delete( T * first, T * last, OutputIterator out ) + template< class OutputIterator> + typename std::enable_if< boost::has_trivial_destructor::value, OutputIterator >::type + copy_and_delete( T * first, T * last, OutputIterator out ) { - if (boost::has_trivial_destructor::value) { - return std::copy(first, last, out); // will use memcpy if possible - } else { - for (; first != last; ++first, ++out) { - *out = *first; - first->~T(); - } - return out; + return std::copy(first, last, out); // will use memcpy if possible + } + + + template + typename std::enable_if< ! boost::has_trivial_destructor::value, OutputIterator >::type + copy_and_delete( T * first, T * last, OutputIterator out ) + { + for (; first != last; ++first, ++out) { + *out = std::move(*first); + first->~T(); } + return out; } template< class Functor > @@ -445,11 +467,18 @@ class compile_time_sized_ringbuffer: } public: + + template< typename = std::enable_if< std::is_copy_constructible::value, bool >::type > bool push(T const & t) { return ringbuffer_base::push(t, data(), max_size); } + bool push(T&& t) + { + return ringbuffer_base::push(std::move(t), data(), max_size); + } + template bool consume_one(Functor & f) { @@ -558,11 +587,18 @@ class runtime_sized_ringbuffer: Alloc::deallocate(array_, max_elements_); } - bool push(T const & t) + template< typename = std::enable_if< std::is_copy_constructible::value, bool >::type > + bool + push(T const & t) { return ringbuffer_base::push(t, &*array_, max_elements_); } + bool push(T&& t) + { + return ringbuffer_base::push(std::move(t), &*array_, max_elements_); + } + template bool consume_one(Functor & f) { @@ -756,11 +792,25 @@ class spsc_queue: * * \note Thread-safe and wait-free * */ - bool push(T const & t) + template< typename = std::enable_if< std::is_copy_constructible::value, bool >::type > + bool push(T const & t) { return base_type::push(t); } + /** Pushes object t to the ringbuffer. + * + * \pre only one thread is allowed to push data to the spsc_queue + * \post object will be pushed to the spsc_queue, unless it is full. + * \return true, if the push operation is successful. + * + * \note Thread-safe and wait-free + * */ + bool push( T && t ) + { + return base_type::push( std::move( t ) ); + } + /** Pops one object from ringbuffer. * * \pre only one thread is allowed to pop data to the spsc_queue From 2329d4755d52094463d9cf12c3de4edf6f5a7677 Mon Sep 17 00:00:00 2001 From: Thomas Novotny Date: Sun, 23 Jul 2017 13:21:22 +0200 Subject: [PATCH 2/4] fixed compile issues for pop on spsc_queue> --- .../boost/lockfree/detail/move_payload.hpp | 75 +++++++++++++++++++ include/boost/lockfree/spsc_queue.hpp | 24 ++++-- 2 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 include/boost/lockfree/detail/move_payload.hpp diff --git a/include/boost/lockfree/detail/move_payload.hpp b/include/boost/lockfree/detail/move_payload.hpp new file mode 100644 index 0000000..45a8ec2 --- /dev/null +++ b/include/boost/lockfree/detail/move_payload.hpp @@ -0,0 +1,75 @@ +// boost lockfree: move_payload helper +// +// Copyright (C) 2011 Tim Blechmann +// +// Distributed under the Boost Software License, Version 1.0. (See +// accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_LOCKFREE_DETAIL_MOVE_PAYLOAD_HPP_INCLUDED +#define BOOST_LOCKFREE_DETAIL_MOVE_PAYLOAD_HPP_INCLUDED + +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4512) // assignment operator could not be generated +#endif + +namespace boost { +namespace lockfree { +namespace detail { + +struct move_convertible +{ + template + static void move(T & t, U & u) + { + u = std::move(t); + } +}; + +struct move_constructible_and_assignable +{ + template + static void ^move(T & t, U & u) + { + u = U(std::move(t)); + } +}; + +template +void move_payload(T & t, U & u) +{ + typedef typename boost::mpl::if_::type, + move_convertible, + move_constructible_and_assignable + >::type move_type; + move_type::move(t, u); +} + +template +struct consume_via_move +{ + consume_via_move(T & out): + out_(std::move(out)) + {} + + template + void operator()(U & element) + { + move_payload(element, out_); + } + + T & out_; +}; + + +}}} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif /* BOOST_LOCKFREE_DETAIL_COPY_PAYLOAD_HPP_INCLUDED */ diff --git a/include/boost/lockfree/spsc_queue.hpp b/include/boost/lockfree/spsc_queue.hpp index c1ea4f8..5e1ade2 100644 --- a/include/boost/lockfree/spsc_queue.hpp +++ b/include/boost/lockfree/spsc_queue.hpp @@ -12,19 +12,21 @@ #include #include +#include #include #include +#include // for BOOST_LIKELY #include #include #include -#include // for BOOST_LIKELY #include #include #include #include +#include #include #include @@ -825,22 +827,34 @@ class spsc_queue: return consume_one( consume_functor ); } - /** Pops one object from ringbuffer. + /** Pops one object from ringbuffer. If it is move assignable it will be moved, + * otherwise it will be copied. * * \pre only one thread is allowed to pop data to the spsc_queue - * \post if ringbuffer is not empty, object will be copied to ret. + * \post if ringbuffer is not empty, object will be assigned to ret. * \return true, if the pop operation is successful, false if ringbuffer was empty. * * \note Thread-safe and wait-free */ template - typename boost::enable_if::type, bool>::type - pop (U & ret) + typename boost::enable_if::value && !std::is_move_assignable::value>::type, bool>::type + pop ( U & ret) { detail::consume_via_copy consume_functor(ret); return consume_one( consume_functor ); } + template + typename boost::enable_if::value && std::is_move_assignable::value>::type, bool>::type + pop ( U & ret ) + { + detail::consume_via_move consume_functor( ret ); + return consume_one( consume_functor ); + } + + /** Pushes as many objects from the array t as there is space. * * \pre only one thread is allowed to push data to the spsc_queue From 5e214c2173455790b2468544103756750984d3c7 Mon Sep 17 00:00:00 2001 From: Thomas Novotny Date: Sun, 23 Jul 2017 14:26:35 +0200 Subject: [PATCH 3/4] added test case for spsc_queue> --- test/spsc_queue_test.cpp | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/spsc_queue_test.cpp b/test/spsc_queue_test.cpp index 99f393f..b6bb87a 100644 --- a/test/spsc_queue_test.cpp +++ b/test/spsc_queue_test.cpp @@ -16,8 +16,8 @@ #include #include -#include "test_helpers.hpp" #include "test_common.hpp" +#include "test_helpers.hpp" using namespace boost; using namespace boost::lockfree; @@ -405,3 +405,31 @@ BOOST_AUTO_TEST_CASE( spsc_queue_reset_test ) BOOST_REQUIRE(f.empty()); } + + +BOOST_AUTO_TEST_CASE( spsc_queue_unique_ptr_push_pop_test ) +{ + spsc_queue, capacity<64> > f; + + BOOST_REQUIRE(f.empty()); + + unique_ptr in; + unique_ptr out; + + const int fortytwo = 42; + + in.reset( new int[1] ); + in[0] = fortytwo; + int* data = in.get(); + + BOOST_REQUIRE( f.push( std::move(in) ) ); + BOOST_REQUIRE( f.pop(out) ); + + BOOST_REQUIRE( out.get() == data ); + BOOST_REQUIRE( out[0] == fortytwo ); + + f.reset(); + + BOOST_REQUIRE(f.empty()); +} + From 75dd3946e615b9190b03dcbf4b7dbd9e11e914df Mon Sep 17 00:00:00 2001 From: Thomas Novotny Date: Sun, 23 Jul 2017 14:30:09 +0200 Subject: [PATCH 4/4] fixed errors in move_payload that caused compile errors --- include/boost/lockfree/detail/move_payload.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/lockfree/detail/move_payload.hpp b/include/boost/lockfree/detail/move_payload.hpp index 45a8ec2..2196a84 100644 --- a/include/boost/lockfree/detail/move_payload.hpp +++ b/include/boost/lockfree/detail/move_payload.hpp @@ -33,7 +33,7 @@ struct move_convertible struct move_constructible_and_assignable { template - static void ^move(T & t, U & u) + static void move(T & t, U & u) { u = U(std::move(t)); } @@ -53,7 +53,7 @@ template struct consume_via_move { consume_via_move(T & out): - out_(std::move(out)) + out_(out) {} template