mirror of
https://github.com/boostorg/smart_ptr.git
synced 2025-07-30 04:47:12 +02:00
Merged 44636, 44640, 45094 (atomic access) from trunk to release.
[SVN r47349]
This commit is contained in:
35
include/boost/memory_order.hpp
Normal file
35
include/boost/memory_order.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef BOOST_MEMORY_ORDER_HPP_INCLUDED
|
||||
#define BOOST_MEMORY_ORDER_HPP_INCLUDED
|
||||
|
||||
// MS compatible compilers support #pragma once
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
// boost/memory_order.hpp
|
||||
//
|
||||
// Defines enum boost::memory_order per the C++0x working draft
|
||||
//
|
||||
// Copyright (c) 2008 Peter Dimov
|
||||
//
|
||||
// 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)
|
||||
|
||||
|
||||
namespace boost
|
||||
{
|
||||
|
||||
enum memory_order
|
||||
{
|
||||
memory_order_relaxed = 0,
|
||||
memory_order_acquire = 1,
|
||||
memory_order_release = 2,
|
||||
memory_order_acq_rel = 3, // acquire | release
|
||||
memory_order_seq_cst = 7, // acq_rel | 4
|
||||
};
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // #ifndef BOOST_MEMORY_ORDER_HPP_INCLUDED
|
@ -33,6 +33,11 @@
|
||||
#include <boost/detail/workaround.hpp>
|
||||
#include <boost/detail/sp_convertible.hpp>
|
||||
|
||||
#if !defined(BOOST_SP_NO_ATOMIC_ACCESS)
|
||||
#include <boost/detail/spinlock_pool.hpp>
|
||||
#include <boost/memory_order.hpp>
|
||||
#endif
|
||||
|
||||
#include <algorithm> // for std::swap
|
||||
#include <functional> // for std::less
|
||||
#include <typeinfo> // for std::bad_cast
|
||||
@ -498,6 +503,65 @@ public:
|
||||
return pn.get_deleter( ti );
|
||||
}
|
||||
|
||||
// atomic access
|
||||
|
||||
#if !defined(BOOST_SP_NO_ATOMIC_ACCESS)
|
||||
|
||||
shared_ptr<T> atomic_load( memory_order /*mo*/ = memory_order_seq_cst ) const
|
||||
{
|
||||
boost::detail::spinlock_pool<2>::scoped_lock lock( this );
|
||||
return *this;
|
||||
}
|
||||
|
||||
void atomic_store( shared_ptr<T> r, memory_order /*mo*/ = memory_order_seq_cst )
|
||||
{
|
||||
boost::detail::spinlock_pool<2>::scoped_lock lock( this );
|
||||
swap( r );
|
||||
}
|
||||
|
||||
shared_ptr<T> atomic_swap( shared_ptr<T> r, memory_order /*mo*/ = memory_order_seq_cst )
|
||||
{
|
||||
boost::detail::spinlock & sp = boost::detail::spinlock_pool<2>::spinlock_for( this );
|
||||
|
||||
sp.lock();
|
||||
swap( r );
|
||||
sp.unlock();
|
||||
|
||||
return r; // return std::move(r)
|
||||
}
|
||||
|
||||
bool atomic_compare_swap( shared_ptr<T> & v, shared_ptr<T> w )
|
||||
{
|
||||
boost::detail::spinlock & sp = boost::detail::spinlock_pool<2>::spinlock_for( this );
|
||||
|
||||
sp.lock();
|
||||
|
||||
if( px == v.px && pn == v.pn )
|
||||
{
|
||||
swap( w );
|
||||
|
||||
sp.unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
shared_ptr tmp( *this );
|
||||
|
||||
sp.unlock();
|
||||
|
||||
tmp.swap( v );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool atomic_compare_swap( shared_ptr<T> & v, shared_ptr<T> w, memory_order /*success*/, memory_order /*failure*/ )
|
||||
{
|
||||
return atomic_compare_swap( v, w ); // std::move( w )
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Tasteless as this may seem, making all members public allows member templates
|
||||
// to work in the absence of member template friends. (Matthew Langston)
|
||||
|
||||
|
@ -47,5 +47,6 @@ import testing ;
|
||||
[ run wp_convertible_test.cpp ]
|
||||
[ run ip_convertible_test.cpp ]
|
||||
[ run allocate_shared_test.cpp ]
|
||||
[ run sp_atomic_test.cpp ]
|
||||
;
|
||||
}
|
||||
|
247
test/sp_atomic_mt2_test.cpp
Normal file
247
test/sp_atomic_mt2_test.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
|
||||
// Copyright (c) 2008 Peter Dimov
|
||||
//
|
||||
// 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
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
#include <boost/thread/locks.hpp>
|
||||
|
||||
#include <boost/detail/lightweight_mutex.hpp>
|
||||
#include <boost/detail/lightweight_thread.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <ctime>
|
||||
|
||||
//
|
||||
|
||||
static void next_value( unsigned & v )
|
||||
{
|
||||
v = v % 2? 3 * v + 1: v / 2;
|
||||
}
|
||||
|
||||
struct X
|
||||
{
|
||||
std::vector<unsigned> v_;
|
||||
|
||||
explicit X( std::size_t n ): v_( n )
|
||||
{
|
||||
for( std::size_t i = 0; i < n; ++i )
|
||||
{
|
||||
v_[ i ] = i;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned get() const
|
||||
{
|
||||
return std::accumulate( v_.begin(), v_.end(), 0 );
|
||||
}
|
||||
|
||||
void set()
|
||||
{
|
||||
std::for_each( v_.begin(), v_.end(), next_value );
|
||||
}
|
||||
};
|
||||
|
||||
static boost::shared_ptr<X> ps;
|
||||
|
||||
static boost::detail::lightweight_mutex lm;
|
||||
static boost::shared_mutex rw;
|
||||
|
||||
enum prim_type
|
||||
{
|
||||
pt_mutex,
|
||||
pt_rwlock,
|
||||
pt_atomics
|
||||
};
|
||||
|
||||
int read_access( prim_type pt )
|
||||
{
|
||||
switch( pt )
|
||||
{
|
||||
case pt_mutex:
|
||||
{
|
||||
boost::detail::lightweight_mutex::scoped_lock lock( lm );
|
||||
return ps->get();
|
||||
}
|
||||
|
||||
case pt_rwlock:
|
||||
{
|
||||
boost::shared_lock<boost::shared_mutex> lock( rw );
|
||||
return ps->get();
|
||||
}
|
||||
|
||||
case pt_atomics:
|
||||
{
|
||||
boost::shared_ptr<X> p2 = ps.atomic_load();
|
||||
return p2->get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_access( prim_type pt )
|
||||
{
|
||||
switch( pt )
|
||||
{
|
||||
case pt_mutex:
|
||||
{
|
||||
boost::detail::lightweight_mutex::scoped_lock lock( lm );
|
||||
ps->set();
|
||||
}
|
||||
break;
|
||||
|
||||
case pt_rwlock:
|
||||
{
|
||||
boost::unique_lock<boost::shared_mutex> lock( rw );
|
||||
ps->set();
|
||||
}
|
||||
break;
|
||||
|
||||
case pt_atomics:
|
||||
{
|
||||
boost::shared_ptr<X> p1 = ps.atomic_load();
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
boost::shared_ptr<X> p2( new X( *p1 ) );
|
||||
p2->set();
|
||||
|
||||
if( ps.atomic_compare_swap( p1, p2 ) ) break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void worker( int k, prim_type pt, int n, int r )
|
||||
{
|
||||
++r;
|
||||
|
||||
unsigned s = 0, nr = 0, nw = 0;
|
||||
|
||||
for( int i = 0; i < n; ++i )
|
||||
{
|
||||
if( i % r )
|
||||
{
|
||||
s += read_access( pt );
|
||||
++nr;
|
||||
}
|
||||
else
|
||||
{
|
||||
write_access( pt );
|
||||
++s;
|
||||
++nw;
|
||||
}
|
||||
}
|
||||
|
||||
printf( "Worker %2d: %u:%u, %10u\n", k, nr, nw, s );
|
||||
}
|
||||
|
||||
#if defined( BOOST_HAS_PTHREADS )
|
||||
char const * thmodel = "POSIX";
|
||||
#else
|
||||
char const * thmodel = "Windows";
|
||||
#endif
|
||||
|
||||
char const * pt_to_string( prim_type pt )
|
||||
{
|
||||
switch( pt )
|
||||
{
|
||||
case pt_mutex:
|
||||
|
||||
return "mutex";
|
||||
|
||||
case pt_rwlock:
|
||||
|
||||
return "rwlock";
|
||||
|
||||
case pt_atomics:
|
||||
|
||||
return "atomics";
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_pt_option( std::string const & opt, prim_type & pt, prim_type pt2 )
|
||||
{
|
||||
if( opt == pt_to_string( pt2 ) )
|
||||
{
|
||||
pt = pt2;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_int_option( std::string const & opt, std::string const & prefix, int & k, int kmin, int kmax )
|
||||
{
|
||||
if( opt.substr( 0, prefix.size() ) == prefix )
|
||||
{
|
||||
int v = atoi( opt.substr( prefix.size() ).c_str() );
|
||||
|
||||
if( v >= kmin && v <= kmax )
|
||||
{
|
||||
k = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main( int ac, char const * av[] )
|
||||
{
|
||||
using namespace std; // printf, clock_t, clock
|
||||
|
||||
int m = 4; // threads
|
||||
int n = 10000; // vector size
|
||||
int k = 1000000; // iterations
|
||||
int r = 100; // read/write ratio, r:1
|
||||
|
||||
prim_type pt = pt_atomics;
|
||||
|
||||
for( int i = 1; i < ac; ++i )
|
||||
{
|
||||
handle_pt_option( av[i], pt, pt_mutex );
|
||||
handle_pt_option( av[i], pt, pt_rwlock );
|
||||
handle_pt_option( av[i], pt, pt_atomics );
|
||||
|
||||
handle_int_option( av[i], "n=", n, 1, INT_MAX );
|
||||
handle_int_option( av[i], "size=", n, 1, INT_MAX );
|
||||
|
||||
handle_int_option( av[i], "k=", k, 1, INT_MAX );
|
||||
handle_int_option( av[i], "iterations=", k, 1, INT_MAX );
|
||||
|
||||
handle_int_option( av[i], "m=", m, 1, INT_MAX );
|
||||
handle_int_option( av[i], "threads=", m, 1, INT_MAX );
|
||||
|
||||
handle_int_option( av[i], "r=", r, 1, INT_MAX );
|
||||
handle_int_option( av[i], "ratio=", r, 1, INT_MAX );
|
||||
}
|
||||
|
||||
printf( "%s: threads=%d size=%d iterations=%d ratio=%d %s\n\n", thmodel, m, n, k, r, pt_to_string( pt ) );
|
||||
|
||||
ps.reset( new X( n ) );
|
||||
|
||||
clock_t t = clock();
|
||||
|
||||
std::vector<pthread_t> a( m );
|
||||
|
||||
for( int i = 0; i < m; ++i )
|
||||
{
|
||||
boost::detail::lw_thread_create( a[ i ], boost::bind( worker, i, pt, k, r ) );
|
||||
}
|
||||
|
||||
for( int j = 0; j < m; ++j )
|
||||
{
|
||||
pthread_join( a[ j ], 0 );
|
||||
}
|
||||
|
||||
t = clock() - t;
|
||||
|
||||
double ts = static_cast<double>( t ) / CLOCKS_PER_SEC;
|
||||
printf( "%.3f seconds, %.3f accesses per microsecond.\n", ts, m * k / ts / 1e+6 );
|
||||
}
|
191
test/sp_atomic_mt_test.cpp
Normal file
191
test/sp_atomic_mt_test.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
|
||||
// Copyright (c) 2008 Peter Dimov
|
||||
//
|
||||
// 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
|
||||
|
||||
//#define USE_MUTEX
|
||||
//#define USE_RWLOCK
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#if defined( USE_RWLOCK )
|
||||
#include <boost/thread/shared_mutex.hpp>
|
||||
#include <boost/thread/locks.hpp>
|
||||
#endif
|
||||
|
||||
#include <boost/detail/lightweight_mutex.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
#include <boost/detail/lightweight_thread.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
|
||||
//
|
||||
|
||||
int const n = 1024 * 1024;
|
||||
|
||||
struct X
|
||||
{
|
||||
int v_; // version
|
||||
|
||||
unsigned a_;
|
||||
unsigned b_;
|
||||
|
||||
X(): v_( 0 ), a_( 1 ), b_( 1 )
|
||||
{
|
||||
}
|
||||
|
||||
int get() const
|
||||
{
|
||||
return a_ * 7 + b_ * 11;
|
||||
}
|
||||
|
||||
void set()
|
||||
{
|
||||
int tmp = get();
|
||||
|
||||
b_ = a_;
|
||||
a_ = tmp;
|
||||
|
||||
++v_;
|
||||
}
|
||||
};
|
||||
|
||||
static boost::shared_ptr<X> ps( new X );
|
||||
|
||||
static boost::detail::lightweight_mutex lm;
|
||||
|
||||
#if defined( USE_RWLOCK )
|
||||
static boost::shared_mutex rw;
|
||||
#endif
|
||||
|
||||
static int tr = 0;
|
||||
|
||||
void reader( int r )
|
||||
{
|
||||
int k = 0;
|
||||
unsigned s = 0;
|
||||
|
||||
for( int i = 0; i < n; ++k )
|
||||
{
|
||||
#if defined( USE_MUTEX )
|
||||
|
||||
boost::detail::lightweight_mutex::scoped_lock lock( lm );
|
||||
|
||||
s += ps->get();
|
||||
|
||||
BOOST_TEST( ps->v_ >= i );
|
||||
i = ps->v_;
|
||||
|
||||
#elif defined( USE_RWLOCK )
|
||||
|
||||
boost::shared_lock<boost::shared_mutex> lock( rw );
|
||||
|
||||
s += ps->get();
|
||||
|
||||
BOOST_TEST( ps->v_ >= i );
|
||||
i = ps->v_;
|
||||
|
||||
#else
|
||||
|
||||
boost::shared_ptr<X> p2 = ps.atomic_load();
|
||||
|
||||
s += p2->get();
|
||||
|
||||
BOOST_TEST( p2->v_ >= i );
|
||||
i = p2->v_;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
printf( "Reader %d: %9d iterations (%6.3fx), %u\n", r, k, (double)k / n, s );
|
||||
|
||||
boost::detail::lightweight_mutex::scoped_lock lock( lm );
|
||||
tr += k;
|
||||
}
|
||||
|
||||
void writer()
|
||||
{
|
||||
for( int i = 0; i < n; ++i )
|
||||
{
|
||||
#if defined( USE_MUTEX )
|
||||
|
||||
boost::detail::lightweight_mutex::scoped_lock lock( lm );
|
||||
|
||||
BOOST_TEST( ps->v_ == i );
|
||||
ps->set();
|
||||
|
||||
#elif defined( USE_RWLOCK )
|
||||
|
||||
boost::unique_lock<boost::shared_mutex> lock( rw );
|
||||
|
||||
BOOST_TEST( ps->v_ == i );
|
||||
ps->set();
|
||||
|
||||
#else
|
||||
|
||||
boost::shared_ptr<X> p2( new X( *ps ) );
|
||||
|
||||
BOOST_TEST( p2->v_ == i );
|
||||
p2->set();
|
||||
|
||||
ps.atomic_store( p2 );
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined( BOOST_HAS_PTHREADS )
|
||||
char const * thmodel = "POSIX";
|
||||
#else
|
||||
char const * thmodel = "Windows";
|
||||
#endif
|
||||
|
||||
int const mr = 8; // reader threads
|
||||
int const mw = 1; // writer thread
|
||||
|
||||
#if defined( USE_MUTEX )
|
||||
char const * prim = "mutex";
|
||||
#elif defined( USE_RWLOCK )
|
||||
char const * prim = "rwlock";
|
||||
#else
|
||||
char const * prim = "atomics";
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace std; // printf, clock_t, clock
|
||||
|
||||
printf( "Using %s threads: %dR + %dW threads, %d iterations, %s\n\n", thmodel, mr, mw, n, prim );
|
||||
|
||||
clock_t t = clock();
|
||||
|
||||
pthread_t a[ mr+mw ];
|
||||
|
||||
for( int i = 0; i < mr; ++i )
|
||||
{
|
||||
boost::detail::lw_thread_create( a[ i ], boost::bind( reader, i ) );
|
||||
}
|
||||
|
||||
for( int i = mr; i < mr+mw; ++i )
|
||||
{
|
||||
boost::detail::lw_thread_create( a[ i ], writer );
|
||||
}
|
||||
|
||||
for( int j = 0; j < mr+mw; ++j )
|
||||
{
|
||||
pthread_join( a[ j ], 0 );
|
||||
}
|
||||
|
||||
t = clock() - t;
|
||||
|
||||
double ts = static_cast<double>( t ) / CLOCKS_PER_SEC;
|
||||
printf( "%.3f seconds, %.3f reads per microsecond.\n", ts, tr / ts / 1e+6 );
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
87
test/sp_atomic_test.cpp
Normal file
87
test/sp_atomic_test.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include <boost/config.hpp>
|
||||
|
||||
// sp_atomic_test.cpp
|
||||
//
|
||||
// Copyright (c) 2008 Peter Dimov
|
||||
//
|
||||
// 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
|
||||
|
||||
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
//
|
||||
|
||||
struct X
|
||||
{
|
||||
};
|
||||
|
||||
#define BOOST_TEST_SP_EQ( p, q ) BOOST_TEST( p == q && !( p < q ) && !( q < p ) )
|
||||
|
||||
int main()
|
||||
{
|
||||
boost::shared_ptr<X> px( new X );
|
||||
|
||||
{
|
||||
boost::shared_ptr<X> p2 = px.atomic_load();
|
||||
BOOST_TEST_SP_EQ( p2, px );
|
||||
|
||||
boost::shared_ptr<X> px2( new X );
|
||||
px.atomic_store( px2 );
|
||||
BOOST_TEST_SP_EQ( px, px2 );
|
||||
|
||||
p2 = px.atomic_load();
|
||||
BOOST_TEST_SP_EQ( p2, px );
|
||||
BOOST_TEST_SP_EQ( p2, px2 );
|
||||
|
||||
boost::shared_ptr<X> px3( new X );
|
||||
boost::shared_ptr<X> p3 = px.atomic_swap( px3 );
|
||||
BOOST_TEST_SP_EQ( p3, px2 );
|
||||
BOOST_TEST_SP_EQ( px, px3 );
|
||||
|
||||
boost::shared_ptr<X> px4( new X );
|
||||
boost::shared_ptr<X> cmp;
|
||||
|
||||
bool r = px.atomic_compare_swap( cmp, px4 );
|
||||
BOOST_TEST( !r );
|
||||
BOOST_TEST_SP_EQ( px, px3 );
|
||||
BOOST_TEST_SP_EQ( cmp, px3 );
|
||||
|
||||
r = px.atomic_compare_swap( cmp, px4 );
|
||||
BOOST_TEST( r );
|
||||
BOOST_TEST_SP_EQ( px, px4 );
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
px.reset();
|
||||
|
||||
{
|
||||
boost::shared_ptr<X> p2 = px.atomic_load( boost::memory_order_acquire );
|
||||
BOOST_TEST_SP_EQ( p2, px );
|
||||
|
||||
boost::shared_ptr<X> px2( new X );
|
||||
px.atomic_store( px2, boost::memory_order_release );
|
||||
BOOST_TEST_SP_EQ( px, px2 );
|
||||
|
||||
boost::shared_ptr<X> p3 = px.atomic_swap( boost::shared_ptr<X>(), boost::memory_order_acq_rel );
|
||||
BOOST_TEST_SP_EQ( p3, px2 );
|
||||
BOOST_TEST_SP_EQ( px, p2 );
|
||||
|
||||
boost::shared_ptr<X> px4( new X );
|
||||
boost::shared_ptr<X> cmp( px2 );
|
||||
|
||||
bool r = px.atomic_compare_swap( cmp, px4, boost::memory_order_acquire, boost::memory_order_relaxed );
|
||||
BOOST_TEST( !r );
|
||||
BOOST_TEST_SP_EQ( px, p2 );
|
||||
BOOST_TEST_SP_EQ( cmp, p2 );
|
||||
|
||||
r = px.atomic_compare_swap( cmp, px4, boost::memory_order_release, boost::memory_order_acquire );
|
||||
BOOST_TEST( r );
|
||||
BOOST_TEST_SP_EQ( px, px4 );
|
||||
}
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
Reference in New Issue
Block a user