mirror of
https://github.com/boostorg/lockfree.git
synced 2026-07-21 13:23:29 +00:00
test: improve test coverage
This commit is contained in:
@@ -241,4 +241,150 @@ BOOST_AUTO_TEST_CASE( queue_uses_optional )
|
||||
BOOST_TEST_REQUIRE( pop_to_optional );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( queue_uses_optional_capacity )
|
||||
{
|
||||
boost::lockfree::queue< int, boost::lockfree::capacity< 64 > > stk;
|
||||
|
||||
bool pop_to_nullopt = stk.pop( boost::lockfree::uses_optional ) == std::nullopt;
|
||||
BOOST_TEST_REQUIRE( pop_to_nullopt );
|
||||
|
||||
stk.push( 53 );
|
||||
bool pop_to_optional = stk.pop( boost::lockfree::uses_optional ) == 53;
|
||||
BOOST_TEST_REQUIRE( pop_to_optional );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_CASE( fixed_size_queue_test_exhausted )
|
||||
{
|
||||
queue< int, capacity< 2 > > f;
|
||||
|
||||
BOOST_TEST_REQUIRE( f.push( 1 ) );
|
||||
BOOST_TEST_REQUIRE( f.push( 2 ) );
|
||||
BOOST_TEST_REQUIRE( !f.push( 3 ) );
|
||||
|
||||
int out;
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 1 );
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 2 );
|
||||
BOOST_TEST_REQUIRE( !f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( bounded_queue_test_exhausted )
|
||||
{
|
||||
queue< int > f( 2 );
|
||||
|
||||
BOOST_TEST_REQUIRE( f.bounded_push( 1 ) );
|
||||
BOOST_TEST_REQUIRE( f.bounded_push( 2 ) );
|
||||
BOOST_TEST_REQUIRE( !f.bounded_push( 3 ) );
|
||||
|
||||
int out;
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 1 );
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 2 );
|
||||
BOOST_TEST_REQUIRE( !f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( queue_unsynchronized_push_const_ref )
|
||||
{
|
||||
queue< int > f( 64 );
|
||||
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
|
||||
const int a = 42;
|
||||
const int b = 43;
|
||||
|
||||
f.unsynchronized_push( a );
|
||||
f.unsynchronized_push( b );
|
||||
|
||||
int i1( 0 ), i2( 0 );
|
||||
BOOST_TEST_REQUIRE( f.unsynchronized_pop( i1 ) );
|
||||
BOOST_TEST_REQUIRE( i1 == 42 );
|
||||
BOOST_TEST_REQUIRE( f.unsynchronized_pop( i2 ) );
|
||||
BOOST_TEST_REQUIRE( i2 == 43 );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( queue_consume_one_capacity_test )
|
||||
{
|
||||
queue< int, capacity< 64 > > f;
|
||||
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
|
||||
f.push( 10 );
|
||||
f.push( 20 );
|
||||
|
||||
bool success1 = f.consume_one( []( int i ) {
|
||||
BOOST_TEST_REQUIRE( i == 10 );
|
||||
} );
|
||||
|
||||
bool success2 = f.consume_one( []( int i ) {
|
||||
BOOST_TEST_REQUIRE( i == 20 );
|
||||
} );
|
||||
|
||||
BOOST_TEST_REQUIRE( success1 );
|
||||
BOOST_TEST_REQUIRE( success2 );
|
||||
BOOST_TEST_REQUIRE( !f.consume_one( []( int ) {} ) );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( queue_consume_all_capacity_test )
|
||||
{
|
||||
queue< int, capacity< 64 > > f;
|
||||
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
|
||||
f.push( 1 );
|
||||
f.push( 2 );
|
||||
f.push( 3 );
|
||||
|
||||
size_t consumed = f.consume_all( []( int ) {} );
|
||||
|
||||
BOOST_TEST_REQUIRE( consumed == 3u );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( queue_empty_pop_test )
|
||||
{
|
||||
queue< int > f( 64 );
|
||||
|
||||
int out = 0xDEAD;
|
||||
BOOST_TEST_REQUIRE( !f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( !f.unsynchronized_pop( out ) );
|
||||
BOOST_TEST_REQUIRE( !f.consume_one( []( int ) {} ) );
|
||||
BOOST_TEST_REQUIRE( f.consume_all( []( int ) {} ) == 0u );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( queue_push_pop_many )
|
||||
{
|
||||
queue< int > f( 64 );
|
||||
|
||||
for ( int i = 0; i < 100; ++i )
|
||||
BOOST_TEST_REQUIRE( f.push( i ) );
|
||||
|
||||
for ( int i = 0; i < 100; ++i ) {
|
||||
int out;
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == i );
|
||||
}
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( queue_push_pop_many_capacity )
|
||||
{
|
||||
queue< int, capacity< 128 > > f;
|
||||
|
||||
for ( int i = 0; i < 100; ++i )
|
||||
BOOST_TEST_REQUIRE( f.push( i ) );
|
||||
|
||||
for ( int i = 0; i < 100; ++i ) {
|
||||
int out;
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == i );
|
||||
}
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
@@ -23,3 +23,21 @@ BOOST_AUTO_TEST_CASE( queue_test_unbounded )
|
||||
boost::lockfree::queue< long > q( 128 );
|
||||
tester->run( q );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( queue_test_unbounded_asymmetric_many_writers )
|
||||
{
|
||||
typedef queue_stress_tester< false > tester_type;
|
||||
std::unique_ptr< tester_type > tester( new tester_type( 1, 4 ) );
|
||||
|
||||
boost::lockfree::queue< long > q( 128 );
|
||||
tester->run( q );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( queue_test_unbounded_asymmetric_many_readers )
|
||||
{
|
||||
typedef queue_stress_tester< false > tester_type;
|
||||
std::unique_ptr< tester_type > tester( new tester_type( 4, 1 ) );
|
||||
|
||||
boost::lockfree::queue< long > q( 128 );
|
||||
tester->run( q );
|
||||
}
|
||||
|
||||
@@ -395,4 +395,117 @@ BOOST_AUTO_TEST_CASE( queue_uses_optional )
|
||||
BOOST_TEST_REQUIRE( pop_to_optional );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_queue_uses_optional_capacity )
|
||||
{
|
||||
boost::lockfree::spsc_queue< int, boost::lockfree::capacity< 64 > > stk;
|
||||
|
||||
bool pop_to_nullopt = stk.pop( boost::lockfree::uses_optional ) == std::nullopt;
|
||||
BOOST_TEST_REQUIRE( pop_to_nullopt );
|
||||
|
||||
stk.push( 53 );
|
||||
bool pop_to_optional = stk.pop( boost::lockfree::uses_optional ) == 53;
|
||||
BOOST_TEST_REQUIRE( pop_to_optional );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_queue_empty_operations_test )
|
||||
{
|
||||
spsc_queue< int > f( 64 );
|
||||
|
||||
int out = 0xDEAD;
|
||||
BOOST_TEST_REQUIRE( !f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( !f.pop() );
|
||||
BOOST_TEST_REQUIRE( !f.consume_one( []( int ) {} ) );
|
||||
BOOST_TEST_REQUIRE( f.consume_all( []( int ) {} ) == 0u );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_queue_push_pop_many_runtime_sized )
|
||||
{
|
||||
spsc_queue< int > f( 128 );
|
||||
|
||||
for ( int i = 0; i < 100; ++i )
|
||||
BOOST_TEST_REQUIRE( f.push( i ) );
|
||||
|
||||
for ( int i = 0; i < 100; ++i ) {
|
||||
int out;
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == i );
|
||||
}
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_queue_push_pop_many_compile_time_sized )
|
||||
{
|
||||
spsc_queue< int, capacity< 128 > > f;
|
||||
|
||||
for ( int i = 0; i < 100; ++i )
|
||||
BOOST_TEST_REQUIRE( f.push( i ) );
|
||||
|
||||
for ( int i = 0; i < 100; ++i ) {
|
||||
int out;
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == i );
|
||||
}
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_queue_wraparound_test )
|
||||
{
|
||||
// Use a small queue to force wraparound
|
||||
spsc_queue< int, capacity< 4 > > f;
|
||||
|
||||
// Fill and drain multiple times to test wraparound
|
||||
for ( int round = 0; round < 10; ++round ) {
|
||||
BOOST_TEST_REQUIRE( f.push( round * 3 + 0 ) );
|
||||
BOOST_TEST_REQUIRE( f.push( round * 3 + 1 ) );
|
||||
BOOST_TEST_REQUIRE( f.push( round * 3 + 2 ) );
|
||||
|
||||
int out;
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == round * 3 + 0 );
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == round * 3 + 1 );
|
||||
BOOST_TEST_REQUIRE( f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == round * 3 + 2 );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_queue_span_pop_test )
|
||||
{
|
||||
spsc_queue< int, capacity< 64 > > f;
|
||||
|
||||
int data[ 4 ] = { 10, 20, 30, 40 };
|
||||
BOOST_TEST_REQUIRE( f.push( boost::span< const int >( data ) ) == size_t( 4 ) );
|
||||
|
||||
int out[ 4 ];
|
||||
BOOST_TEST_REQUIRE( f.pop( out, 4 ) == size_t( 4 ) );
|
||||
BOOST_TEST_REQUIRE( out[ 0 ] == 10 );
|
||||
BOOST_TEST_REQUIRE( out[ 1 ] == 20 );
|
||||
BOOST_TEST_REQUIRE( out[ 2 ] == 30 );
|
||||
BOOST_TEST_REQUIRE( out[ 3 ] == 40 );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_queue_consume_all_test_compile_time )
|
||||
{
|
||||
spsc_queue< int, capacity< 64 > > f;
|
||||
|
||||
f.push( 1 );
|
||||
f.push( 2 );
|
||||
f.push( 3 );
|
||||
|
||||
std::vector< int > consumed;
|
||||
size_t count = f.consume_all( [&]( int i ) {
|
||||
consumed.push_back( i );
|
||||
} );
|
||||
|
||||
BOOST_TEST_REQUIRE( count == 3u );
|
||||
BOOST_TEST_REQUIRE( consumed.size() == 3u );
|
||||
BOOST_TEST_REQUIRE( consumed[ 0 ] == 1 );
|
||||
BOOST_TEST_REQUIRE( consumed[ 1 ] == 2 );
|
||||
BOOST_TEST_REQUIRE( consumed[ 2 ] == 3 );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
@@ -119,3 +119,99 @@ BOOST_AUTO_TEST_CASE( spsc_value_test_move_only_type )
|
||||
std::unique_ptr< uint64_t > out;
|
||||
BOOST_TEST_REQUIRE( v.read( out ) == false );
|
||||
}
|
||||
|
||||
#if !defined( BOOST_NO_CXX17_HDR_OPTIONAL )
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_value_uses_optional )
|
||||
{
|
||||
boost::lockfree::spsc_value< uint64_t > v;
|
||||
|
||||
bool is_nullopt = v.read( boost::lockfree::uses_optional ) == std::nullopt;
|
||||
BOOST_TEST_REQUIRE( is_nullopt );
|
||||
|
||||
v.write( 42 );
|
||||
bool is_42 = v.read( boost::lockfree::uses_optional ) == 42;
|
||||
BOOST_TEST_REQUIRE( is_42 );
|
||||
|
||||
// no pending value
|
||||
bool is_nullopt2 = v.read( boost::lockfree::uses_optional ) == std::nullopt;
|
||||
BOOST_TEST_REQUIRE( is_nullopt2 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_value_uses_optional_allow_multiple_reads )
|
||||
{
|
||||
boost::lockfree::spsc_value< uint64_t, boost::lockfree::allow_multiple_reads< true > > v { 99 };
|
||||
|
||||
bool is_99 = v.read( boost::lockfree::uses_optional ) == 99;
|
||||
BOOST_TEST_REQUIRE( is_99 );
|
||||
|
||||
// Can read again
|
||||
bool is_99_2 = v.read( boost::lockfree::uses_optional ) == 99;
|
||||
BOOST_TEST_REQUIRE( is_99_2 );
|
||||
|
||||
v.write( 100 );
|
||||
bool is_100 = v.read( boost::lockfree::uses_optional ) == 100;
|
||||
BOOST_TEST_REQUIRE( is_100 );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_value_consume_test )
|
||||
{
|
||||
boost::lockfree::spsc_value< uint64_t > v;
|
||||
|
||||
BOOST_TEST_REQUIRE( v.consume( []( uint64_t ) {} ) == false );
|
||||
|
||||
v.write( 123 );
|
||||
bool consumed = v.consume( []( uint64_t val ) {
|
||||
BOOST_TEST_REQUIRE( val == 123 );
|
||||
} );
|
||||
BOOST_TEST_REQUIRE( consumed );
|
||||
|
||||
// no more value available
|
||||
BOOST_TEST_REQUIRE( v.consume( []( uint64_t ) {} ) == false );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_value_consume_allow_multiple_reads )
|
||||
{
|
||||
boost::lockfree::spsc_value< uint64_t, boost::lockfree::allow_multiple_reads< true > > v { 77 };
|
||||
|
||||
bool consumed = v.consume( []( uint64_t val ) {
|
||||
BOOST_TEST_REQUIRE( val == 77 );
|
||||
} );
|
||||
BOOST_TEST_REQUIRE( consumed );
|
||||
|
||||
// can consume again with allow_multiple_reads
|
||||
consumed = v.consume( []( uint64_t val ) {
|
||||
BOOST_TEST_REQUIRE( val == 77 );
|
||||
} );
|
||||
BOOST_TEST_REQUIRE( consumed );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_value_rapid_overwrite )
|
||||
{
|
||||
boost::lockfree::spsc_value< uint64_t > v;
|
||||
|
||||
// Write multiple values without reading
|
||||
for ( uint64_t i = 0; i < 100; ++i )
|
||||
v.write( i );
|
||||
|
||||
// Should read the most recently available value
|
||||
uint64_t out {};
|
||||
BOOST_TEST_REQUIRE( v.read( out ) == true );
|
||||
// The value may be any of the written values, but typically the last or second-to-last
|
||||
// due to the triple-buffer. Just verify we get *something* valid.
|
||||
BOOST_TEST_REQUIRE( out < 100 );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( spsc_value_initialized_with_value )
|
||||
{
|
||||
boost::lockfree::spsc_value< uint64_t > v { 42 };
|
||||
|
||||
uint64_t out {};
|
||||
BOOST_TEST_REQUIRE( v.read( out ) == true );
|
||||
BOOST_TEST_REQUIRE( out == 42 );
|
||||
|
||||
// second read should fail (not allow_multiple_reads)
|
||||
BOOST_TEST_REQUIRE( v.read( out ) == false );
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
#include <boost/lockfree/stack.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define BOOST_TEST_MAIN
|
||||
#ifdef BOOST_LOCKFREE_INCLUDE_TESTS
|
||||
# include <boost/test/included/unit_test.hpp>
|
||||
@@ -308,4 +311,160 @@ BOOST_AUTO_TEST_CASE( queue_uses_optional )
|
||||
BOOST_TEST_REQUIRE( pop_to_optional );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_uses_optional_capacity )
|
||||
{
|
||||
boost::lockfree::stack< int, boost::lockfree::capacity< 64 > > stk;
|
||||
|
||||
bool pop_to_nullopt = stk.pop( boost::lockfree::uses_optional ) == std::nullopt;
|
||||
BOOST_TEST_REQUIRE( pop_to_nullopt );
|
||||
|
||||
stk.push( 53 );
|
||||
bool pop_to_optional = stk.pop( boost::lockfree::uses_optional ) == 53;
|
||||
BOOST_TEST_REQUIRE( pop_to_optional );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_consume_all_atomic_order_test )
|
||||
{
|
||||
boost::lockfree::stack< int > f( 64 );
|
||||
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
|
||||
f.push( 1 );
|
||||
f.push( 2 );
|
||||
f.push( 3 );
|
||||
|
||||
// consume_all_atomic pops all atomically and then processes in stack order (LIFO: 3, 2, 1)
|
||||
std::vector< int > consumed_order;
|
||||
size_t consumed = f.consume_all_atomic( [&]( int i ) {
|
||||
consumed_order.push_back( i );
|
||||
} );
|
||||
|
||||
BOOST_TEST_REQUIRE( consumed == 3u );
|
||||
BOOST_TEST_REQUIRE( consumed_order.size() == 3u );
|
||||
BOOST_TEST_REQUIRE( consumed_order[ 0 ] == 3 );
|
||||
BOOST_TEST_REQUIRE( consumed_order[ 1 ] == 2 );
|
||||
BOOST_TEST_REQUIRE( consumed_order[ 2 ] == 1 );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_consume_all_atomic_reversed_order_test )
|
||||
{
|
||||
boost::lockfree::stack< int > f( 64 );
|
||||
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
|
||||
f.push( 1 );
|
||||
f.push( 2 );
|
||||
f.push( 3 );
|
||||
|
||||
// consume_all_atomic_reversed processes in FIFO order (1, 2, 3)
|
||||
std::vector< int > consumed_order;
|
||||
size_t consumed = f.consume_all_atomic_reversed( [&]( int i ) {
|
||||
consumed_order.push_back( i );
|
||||
} );
|
||||
|
||||
BOOST_TEST_REQUIRE( consumed == 3u );
|
||||
BOOST_TEST_REQUIRE( consumed_order.size() == 3u );
|
||||
BOOST_TEST_REQUIRE( consumed_order[ 0 ] == 1 );
|
||||
BOOST_TEST_REQUIRE( consumed_order[ 1 ] == 2 );
|
||||
BOOST_TEST_REQUIRE( consumed_order[ 2 ] == 3 );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_consume_all_atomic_empty_test )
|
||||
{
|
||||
boost::lockfree::stack< int > f( 64 );
|
||||
|
||||
BOOST_TEST_REQUIRE( f.consume_all_atomic( []( int ) {} ) == 0u );
|
||||
BOOST_TEST_REQUIRE( f.consume_all_atomic_reversed( []( int ) {} ) == 0u );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_empty_operations_test )
|
||||
{
|
||||
boost::lockfree::stack< int > f( 64 );
|
||||
|
||||
int out = 0xDEAD;
|
||||
BOOST_TEST_REQUIRE( !f.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( !f.unsynchronized_pop( out ) );
|
||||
BOOST_TEST_REQUIRE( !f.consume_one( []( int ) {} ) );
|
||||
BOOST_TEST_REQUIRE( f.consume_all( []( int ) {} ) == 0u );
|
||||
BOOST_TEST_REQUIRE( f.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_push_pop_many )
|
||||
{
|
||||
boost::lockfree::stack< long > stk( 128 );
|
||||
|
||||
for ( long i = 0; i < 100; ++i )
|
||||
BOOST_TEST_REQUIRE( stk.push( i ) );
|
||||
|
||||
for ( long i = 99; i >= 0; --i ) {
|
||||
long out;
|
||||
BOOST_TEST_REQUIRE( stk.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == i );
|
||||
}
|
||||
BOOST_TEST_REQUIRE( stk.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_push_pop_many_capacity )
|
||||
{
|
||||
boost::lockfree::stack< long, boost::lockfree::capacity< 128 > > stk;
|
||||
|
||||
for ( long i = 0; i < 100; ++i )
|
||||
BOOST_TEST_REQUIRE( stk.push( i ) );
|
||||
|
||||
for ( long i = 99; i >= 0; --i ) {
|
||||
long out;
|
||||
BOOST_TEST_REQUIRE( stk.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == i );
|
||||
}
|
||||
BOOST_TEST_REQUIRE( stk.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_move_unsynchronized )
|
||||
{
|
||||
boost::lockfree::stack< std::unique_ptr< int > > stk( 128 );
|
||||
|
||||
stk.unsynchronized_push( std::make_unique< int >( 42 ) );
|
||||
|
||||
std::unique_ptr< int > out;
|
||||
BOOST_TEST_REQUIRE( stk.unsynchronized_pop( out ) );
|
||||
BOOST_TEST_REQUIRE( *out == 42 );
|
||||
BOOST_TEST_REQUIRE( stk.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_bounded_push_range )
|
||||
{
|
||||
boost::lockfree::stack< long > stk( 128 );
|
||||
|
||||
long data[ 3 ] = { 10, 20, 30 };
|
||||
BOOST_TEST_REQUIRE( stk.bounded_push( data, data + 3 ) == data + 3 );
|
||||
|
||||
long out;
|
||||
BOOST_TEST_REQUIRE( stk.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 30 );
|
||||
BOOST_TEST_REQUIRE( stk.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 20 );
|
||||
BOOST_TEST_REQUIRE( stk.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 10 );
|
||||
BOOST_TEST_REQUIRE( stk.empty() );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_bounded_push_span )
|
||||
{
|
||||
boost::lockfree::stack< long > stk( 128 );
|
||||
|
||||
long data[ 3 ] = { 10, 20, 30 };
|
||||
BOOST_TEST_REQUIRE( stk.bounded_push( boost::span< const long >( data ) ) == size_t( 3 ) );
|
||||
|
||||
long out;
|
||||
BOOST_TEST_REQUIRE( stk.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 30 );
|
||||
BOOST_TEST_REQUIRE( stk.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 20 );
|
||||
BOOST_TEST_REQUIRE( stk.pop( out ) );
|
||||
BOOST_TEST_REQUIRE( out == 10 );
|
||||
BOOST_TEST_REQUIRE( stk.empty() );
|
||||
}
|
||||
|
||||
@@ -24,3 +24,30 @@ BOOST_AUTO_TEST_CASE( stack_test_unbounded )
|
||||
boost::lockfree::stack< long > q( 128 );
|
||||
tester->run( q );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_test_unbounded_asymmetric )
|
||||
{
|
||||
typedef queue_stress_tester< false > tester_type;
|
||||
std::unique_ptr< tester_type > tester( new tester_type( 1, 4 ) );
|
||||
|
||||
boost::lockfree::stack< long > q( 128 );
|
||||
tester->run( q );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_test_consume_all_atomic )
|
||||
{
|
||||
typedef stack_consume_all_atomic_stress_tester< false > tester_type;
|
||||
std::unique_ptr< tester_type > tester( new tester_type( 2, 4 ) );
|
||||
|
||||
boost::lockfree::stack< long > q( 128 );
|
||||
tester->run_atomic( q );
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE( stack_test_consume_all_atomic_reversed )
|
||||
{
|
||||
typedef stack_consume_all_atomic_stress_tester< false > tester_type;
|
||||
std::unique_ptr< tester_type > tester( new tester_type( 2, 4 ) );
|
||||
|
||||
boost::lockfree::stack< long > q( 128 );
|
||||
tester->run_atomic_reversed( q );
|
||||
}
|
||||
|
||||
@@ -155,3 +155,185 @@ struct queue_stress_tester
|
||||
} // namespace impl
|
||||
|
||||
using impl::queue_stress_tester;
|
||||
|
||||
namespace impl {
|
||||
|
||||
template < bool Bounded = false >
|
||||
struct stack_consume_all_atomic_stress_tester
|
||||
{
|
||||
static const unsigned int buckets = 1 << 13;
|
||||
#ifndef BOOST_LOCKFREE_STRESS_TEST
|
||||
static const long node_count = 5000;
|
||||
#else
|
||||
static const long node_count = 5000000;
|
||||
#endif
|
||||
const int reader_threads;
|
||||
const int writer_threads;
|
||||
|
||||
std::atomic< int > writers_finished;
|
||||
|
||||
static_hashed_set< long, buckets > data;
|
||||
static_hashed_set< long, buckets > dequeued;
|
||||
|
||||
std::atomic< int > push_count, pop_count;
|
||||
|
||||
stack_consume_all_atomic_stress_tester( int reader, int writer ) :
|
||||
reader_threads( reader ),
|
||||
writer_threads( writer ),
|
||||
push_count( 0 ),
|
||||
pop_count( 0 )
|
||||
{}
|
||||
|
||||
template < typename stack_type >
|
||||
void add_items( stack_type& stk )
|
||||
{
|
||||
for ( long i = 0; i != node_count; ++i ) {
|
||||
long id = generate_id< long >();
|
||||
|
||||
bool inserted = data.insert( id );
|
||||
assert( inserted );
|
||||
(void)inserted;
|
||||
|
||||
if ( Bounded )
|
||||
while ( stk.bounded_push( id ) == false ) {
|
||||
#ifdef __VXWORKS__
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
while ( stk.push( id ) == false ) {
|
||||
#ifdef __VXWORKS__
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
++push_count;
|
||||
}
|
||||
writers_finished += 1;
|
||||
}
|
||||
|
||||
template < typename stack_type >
|
||||
void get_items_atomic( stack_type& stk )
|
||||
{
|
||||
for ( ;; ) {
|
||||
size_t consumed = stk.consume_all_atomic( [ & ]( long id ) {
|
||||
bool erased = data.erase( id );
|
||||
bool inserted = dequeued.insert( id );
|
||||
(void)erased;
|
||||
(void)inserted;
|
||||
assert( erased );
|
||||
assert( inserted );
|
||||
++pop_count;
|
||||
} );
|
||||
|
||||
if ( consumed == 0 && writers_finished.load() == writer_threads )
|
||||
break;
|
||||
|
||||
#ifdef __VXWORKS__
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
// drain remaining
|
||||
stk.consume_all_atomic( [ & ]( long id ) {
|
||||
bool erased = data.erase( id );
|
||||
bool inserted = dequeued.insert( id );
|
||||
(void)erased;
|
||||
(void)inserted;
|
||||
assert( erased );
|
||||
assert( inserted );
|
||||
++pop_count;
|
||||
} );
|
||||
}
|
||||
|
||||
template < typename stack_type >
|
||||
void get_items_atomic_reversed( stack_type& stk )
|
||||
{
|
||||
for ( ;; ) {
|
||||
size_t consumed = stk.consume_all_atomic_reversed( [ & ]( long id ) {
|
||||
bool erased = data.erase( id );
|
||||
bool inserted = dequeued.insert( id );
|
||||
(void)erased;
|
||||
(void)inserted;
|
||||
assert( erased );
|
||||
assert( inserted );
|
||||
++pop_count;
|
||||
} );
|
||||
|
||||
if ( consumed == 0 && writers_finished.load() == writer_threads )
|
||||
break;
|
||||
|
||||
#ifdef __VXWORKS__
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
|
||||
// drain remaining
|
||||
stk.consume_all_atomic_reversed( [ & ]( long id ) {
|
||||
bool erased = data.erase( id );
|
||||
bool inserted = dequeued.insert( id );
|
||||
(void)erased;
|
||||
(void)inserted;
|
||||
assert( erased );
|
||||
assert( inserted );
|
||||
++pop_count;
|
||||
} );
|
||||
}
|
||||
|
||||
template < typename stack_type, typename ReaderFunc >
|
||||
void run_impl( stack_type& stk, ReaderFunc reader_func )
|
||||
{
|
||||
BOOST_WARN( stk.is_lock_free() );
|
||||
writers_finished.store( 0 );
|
||||
|
||||
boost::thread_group writer;
|
||||
boost::thread_group reader;
|
||||
|
||||
BOOST_TEST_REQUIRE( stk.empty() );
|
||||
|
||||
for ( int i = 0; i != reader_threads; ++i )
|
||||
reader.create_thread( [ &, reader_func ] {
|
||||
reader_func( stk );
|
||||
} );
|
||||
|
||||
for ( int i = 0; i != writer_threads; ++i )
|
||||
writer.create_thread( [ & ] {
|
||||
add_items( stk );
|
||||
} );
|
||||
|
||||
std::cout << "threads created" << std::endl;
|
||||
|
||||
writer.join_all();
|
||||
|
||||
std::cout << "writer threads joined, waiting for readers" << std::endl;
|
||||
|
||||
reader.join_all();
|
||||
|
||||
std::cout << "reader threads joined" << std::endl;
|
||||
|
||||
BOOST_TEST_REQUIRE( data.count_nodes() == (size_t)0 );
|
||||
BOOST_TEST_REQUIRE( stk.empty() );
|
||||
|
||||
BOOST_TEST_REQUIRE( push_count == pop_count );
|
||||
BOOST_TEST_REQUIRE( push_count == writer_threads * node_count );
|
||||
}
|
||||
|
||||
template < typename stack_type >
|
||||
void run_atomic( stack_type& stk )
|
||||
{
|
||||
run_impl( stk, [ this ]( stack_type& s ) {
|
||||
get_items_atomic( s );
|
||||
} );
|
||||
}
|
||||
|
||||
template < typename stack_type >
|
||||
void run_atomic_reversed( stack_type& stk )
|
||||
{
|
||||
run_impl( stk, [ this ]( stack_type& s ) {
|
||||
get_items_atomic_reversed( s );
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
using impl::stack_consume_all_atomic_stress_tester;
|
||||
|
||||
Reference in New Issue
Block a user