test: improve test coverage

This commit is contained in:
Tim Blechmann
2026-04-12 12:46:50 +08:00
parent 32cda5d48c
commit 39850eb96b
7 changed files with 741 additions and 0 deletions
+146
View File
@@ -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() );
}
+18
View File
@@ -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 );
}
+113
View File
@@ -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() );
}
+96
View File
@@ -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 );
}
+159
View File
@@ -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() );
}
+27
View File
@@ -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 );
}
+182
View File
@@ -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;