From 8589b4f4a7f22ac61799e6f29c2feff74dc26d34 Mon Sep 17 00:00:00 2001 From: Josh Kilmer Date: Thu, 2 Mar 2017 04:18:00 -0600 Subject: [PATCH] spsc_queue: Add tests to verify compatibility with move-only and copy-only types Also: * Fix an issue spsc_queue::reset() would not compile for move-only types * Fix an issue where having BOOST_NO_CXX11_RVALUE_REFERENCES defined would break compiles --- include/boost/lockfree/spsc_queue.hpp | 5 +- test/spsc_queue_test.cpp | 159 ++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 2 deletions(-) diff --git a/include/boost/lockfree/spsc_queue.hpp b/include/boost/lockfree/spsc_queue.hpp index ea84359..449fed4 100644 --- a/include/boost/lockfree/spsc_queue.hpp +++ b/include/boost/lockfree/spsc_queue.hpp @@ -441,6 +441,8 @@ class ringbuffer_base template< class OutputIterator > #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES typename boost::disable_if< typename boost::is_constructible::type, OutputIterator>::type +#else + OutputIterator #endif move_and_delete( T * first, T * last, OutputIterator out ) { @@ -1092,8 +1094,7 @@ class spsc_queue: if ( !boost::has_trivial_destructor::value ) { // make sure to call all destructors! - T dummy_element; - while (pop(dummy_element)) + while (pop()) {} } else { base_type::write_index_.store(0, memory_order_relaxed); diff --git a/test/spsc_queue_test.cpp b/test/spsc_queue_test.cpp index 99f393f..88d7a23 100644 --- a/test/spsc_queue_test.cpp +++ b/test/spsc_queue_test.cpp @@ -15,6 +15,7 @@ #include #include +#include #include "test_helpers.hpp" #include "test_common.hpp" @@ -405,3 +406,161 @@ BOOST_AUTO_TEST_CASE( spsc_queue_reset_test ) BOOST_REQUIRE(f.empty()); } + +struct immovable_type +{ + int value; + + immovable_type() : value(0) {} + immovable_type(int x) : value(x) {} + immovable_type(const immovable_type& other) : value(other.value) {} + + immovable_type& operator=(const immovable_type& other) + { + this->value = other.value; + return *this; + } + +#ifndef BOOST_NO_CXX11_DELETED_FUNCTIONS +#ifndef BOOST_NO_CX11_RVALUE_REFERENCES + immovable_type(immovable_type&&) = delete; + immovable_type& operator=(immovable_type&&) = delete; +#endif +#endif +}; + +struct test_immovable_object +{ + test_immovable_object(int i): + i(i) + {} + + void operator()(immovable_type arg) const + { + BOOST_REQUIRE_EQUAL(arg.value, i); + } + + int i; +}; + +BOOST_AUTO_TEST_CASE( spsc_queue_immovable_object_test ) +{ + spsc_queue > f; + + f.emplace( 9001 ); + f.emplace( 9002 ); + f.push( immovable_type(9003) ); + + immovable_type x(9004); + immovable_type push_array[2]; + push_array[0] = x; + push_array[1] = x; + f.push(push_array, 2); + + immovable_type pop_array[2]; + f.pop(pop_array, 2); + BOOST_CHECK_EQUAL( pop_array[0].value, 9001 ); + BOOST_CHECK_EQUAL( pop_array[1].value, 9002 ); + + f.consume_one( test_immovable_object(9003) ); + f.consume_all( test_immovable_object(9004) ); + + f.emplace( 42 ); + f.emplace( 42 ); + f.reset(); +} + +#ifndef BOOST_NO_CX11_RVALUE_REFERENCES +struct refcount_handle +{ + int* p_refcount; + + refcount_handle() : p_refcount( nullptr ) {} + refcount_handle(int* x) : p_refcount(x) + { + if (p_refcount != nullptr) + { + *p_refcount += 1; + } + } + ~refcount_handle() + { + if (p_refcount != nullptr) + { + *p_refcount -= 1; + } + } + + refcount_handle(const refcount_handle& other) = delete; + refcount_handle& operator=(const refcount_handle&) = delete; + + refcount_handle& operator=(refcount_handle&& other) + { + if (this->p_refcount != nullptr) + { + *p_refcount -= 1; + } + + this->p_refcount = other.p_refcount; + other.p_refcount = nullptr; + return *this; + } + refcount_handle(refcount_handle&& other) + : p_refcount( other.p_refcount ) + { + other.p_refcount = nullptr; + } +}; + +struct test_refcount_handle +{ + test_refcount_handle(int* p_refcount): + p_refcount(p_refcount) + {} + + void operator()(refcount_handle& arg) const + { + BOOST_REQUIRE_EQUAL(arg.p_refcount, p_refcount); + } + + int* p_refcount; +}; + +BOOST_AUTO_TEST_CASE( spsc_queue_refcount_test ) +{ + spsc_queue > f; + + int refcount = 0; + + f.emplace( &refcount ); + f.emplace( &refcount ); + f.emplace( &refcount ); + f.emplace( &refcount ); + BOOST_CHECK_EQUAL( refcount, 4 ); + + f.push( std::move( refcount_handle(&refcount) ) ); + + BOOST_CHECK_EQUAL( refcount, 5 ); + + { + refcount_handle pop_array[2]; + f.pop(pop_array, 2); + BOOST_CHECK_EQUAL( pop_array[0].p_refcount, &refcount ); + BOOST_CHECK_EQUAL( pop_array[1].p_refcount, &refcount ); + + BOOST_CHECK_EQUAL( refcount, 5 ); + } + BOOST_CHECK_EQUAL( refcount, 3 ); + + f.consume_one( test_refcount_handle(&refcount) ); + BOOST_CHECK_EQUAL( refcount, 2 ); + f.consume_all( test_refcount_handle(&refcount) ); + BOOST_CHECK_EQUAL( refcount, 0 ); + + f.emplace( &refcount ); + f.emplace( &refcount ); + BOOST_CHECK_EQUAL( refcount, 2 ); + f.reset(); + BOOST_CHECK_EQUAL( refcount, 0 ); +} +#endif