| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673 |
- // lock-free freelist
- //
- // Copyright (C) 2008-2016 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_FREELIST_HPP_INCLUDED
- #define BOOST_LOCKFREE_FREELIST_HPP_INCLUDED
- #include <array>
- #include <cstdint>
- #include <cstring>
- #include <limits>
- #include <memory>
- #include <stdexcept>
- #include <boost/align/align_up.hpp>
- #include <boost/align/aligned_allocator_adaptor.hpp>
- #include <boost/config.hpp>
- #include <boost/static_assert.hpp>
- #include <boost/throw_exception.hpp>
- #include <boost/lockfree/detail/atomic.hpp>
- #include <boost/lockfree/detail/parameter.hpp>
- #include <boost/lockfree/detail/tagged_ptr.hpp>
- #if defined( _MSC_VER )
- # pragma warning( push )
- # pragma warning( disable : 4100 ) // unreferenced formal parameter
- # pragma warning( disable : 4127 ) // conditional expression is constant
- #endif
- namespace boost { namespace lockfree { namespace detail {
- //----------------------------------------------------------------------------------------------------------------------
- template < typename T, typename Alloc = std::allocator< T > >
- class alignas( cacheline_bytes ) freelist_stack : Alloc
- {
- struct freelist_node
- {
- tagged_ptr< freelist_node > next;
- };
- typedef tagged_ptr< freelist_node > tagged_node_ptr;
- public:
- typedef T* index_t;
- typedef tagged_ptr< T > tagged_node_handle;
- template < typename Allocator >
- freelist_stack( Allocator const& alloc, std::size_t n = 0 ) :
- Alloc( alloc ),
- pool_( tagged_node_ptr( NULL ) )
- {
- for ( std::size_t i = 0; i != n; ++i ) {
- T* node = Alloc::allocate( 1 );
- std::memset( (void*)node, 0, sizeof( T ) );
- #ifdef BOOST_LOCKFREE_FREELIST_INIT_RUNS_DTOR
- destruct< false >( node );
- #else
- deallocate< false >( node );
- #endif
- }
- }
- template < bool ThreadSafe >
- void reserve( std::size_t count )
- {
- for ( std::size_t i = 0; i != count; ++i ) {
- T* node = Alloc::allocate( 1 );
- std::memset( (void*)node, 0, sizeof( T ) );
- deallocate< ThreadSafe >( node );
- }
- }
- template < bool ThreadSafe, bool Bounded >
- T* construct( void )
- {
- T* node = allocate< ThreadSafe, Bounded >();
- if ( node )
- new ( node ) T();
- return node;
- }
- template < bool ThreadSafe, bool Bounded, typename ArgumentType >
- T* construct( const ArgumentType& arg )
- {
- T* node = allocate< ThreadSafe, Bounded >();
- if ( node )
- new ( node ) T( arg );
- return node;
- }
- template < bool ThreadSafe, bool Bounded, typename ArgumentType >
- T* construct( ArgumentType&& arg )
- {
- T* node = allocate< ThreadSafe, Bounded >();
- if ( node )
- new ( node ) T( std::forward< ArgumentType >( arg ) );
- return node;
- }
- template < bool ThreadSafe, bool Bounded, typename ArgumentType1, typename ArgumentType2 >
- T* construct( ArgumentType1&& arg1, ArgumentType2&& arg2 )
- {
- T* node = allocate< ThreadSafe, Bounded >();
- if ( node )
- new ( node ) T( arg1, arg2 );
- return node;
- }
- template < bool ThreadSafe >
- void destruct( tagged_node_handle const& tagged_ptr )
- {
- T* n = tagged_ptr.get_ptr();
- n->~T();
- deallocate< ThreadSafe >( n );
- }
- template < bool ThreadSafe >
- void destruct( T* n )
- {
- n->~T();
- deallocate< ThreadSafe >( n );
- }
- ~freelist_stack( void )
- {
- tagged_node_ptr current = pool_.load();
- while ( current ) {
- freelist_node* current_ptr = current.get_ptr();
- if ( current_ptr )
- current = current_ptr->next;
- Alloc::deallocate( (T*)current_ptr, 1 );
- }
- }
- bool is_lock_free( void ) const
- {
- return pool_.is_lock_free();
- }
- T* get_handle( T* pointer ) const
- {
- return pointer;
- }
- T* get_handle( tagged_node_handle const& handle ) const
- {
- return get_pointer( handle );
- }
- T* get_pointer( tagged_node_handle const& tptr ) const
- {
- return tptr.get_ptr();
- }
- T* get_pointer( T* pointer ) const
- {
- return pointer;
- }
- T* null_handle( void ) const
- {
- return NULL;
- }
- protected: // allow use from subclasses
- template < bool ThreadSafe, bool Bounded >
- T* allocate( void )
- {
- if ( ThreadSafe )
- return allocate_impl< Bounded >();
- else
- return allocate_impl_unsafe< Bounded >();
- }
- private:
- template < bool Bounded >
- T* allocate_impl( void )
- {
- tagged_node_ptr old_pool = pool_.load( memory_order_consume );
- for ( ;; ) {
- if ( !old_pool.get_ptr() ) {
- if ( !Bounded ) {
- T* ptr = Alloc::allocate( 1 );
- std::memset( (void*)ptr, 0, sizeof( T ) );
- return ptr;
- } else
- return 0;
- }
- freelist_node* new_pool_ptr = old_pool->next.get_ptr();
- tagged_node_ptr new_pool( new_pool_ptr, old_pool.get_next_tag() );
- if ( pool_.compare_exchange_weak( old_pool, new_pool ) ) {
- void* ptr = old_pool.get_ptr();
- return reinterpret_cast< T* >( ptr );
- }
- }
- }
- template < bool Bounded >
- T* allocate_impl_unsafe( void )
- {
- tagged_node_ptr old_pool = pool_.load( memory_order_relaxed );
- if ( !old_pool.get_ptr() ) {
- if ( !Bounded ) {
- T* ptr = Alloc::allocate( 1 );
- std::memset( (void*)ptr, 0, sizeof( T ) );
- return ptr;
- } else
- return 0;
- }
- freelist_node* new_pool_ptr = old_pool->next.get_ptr();
- tagged_node_ptr new_pool( new_pool_ptr, old_pool.get_next_tag() );
- pool_.store( new_pool, memory_order_relaxed );
- void* ptr = old_pool.get_ptr();
- return reinterpret_cast< T* >( ptr );
- }
- protected:
- template < bool ThreadSafe >
- void deallocate( T* n )
- {
- if ( ThreadSafe )
- deallocate_impl( n );
- else
- deallocate_impl_unsafe( n );
- }
- private:
- void deallocate_impl( T* n )
- {
- void* node = n;
- tagged_node_ptr old_pool = pool_.load( memory_order_consume );
- freelist_node* new_pool_ptr = reinterpret_cast< freelist_node* >( node );
- for ( ;; ) {
- tagged_node_ptr new_pool( new_pool_ptr, old_pool.get_tag() );
- new_pool->next.set_ptr( old_pool.get_ptr() );
- if ( pool_.compare_exchange_weak( old_pool, new_pool ) )
- return;
- }
- }
- void deallocate_impl_unsafe( T* n )
- {
- void* node = n;
- tagged_node_ptr old_pool = pool_.load( memory_order_relaxed );
- freelist_node* new_pool_ptr = reinterpret_cast< freelist_node* >( node );
- tagged_node_ptr new_pool( new_pool_ptr, old_pool.get_tag() );
- new_pool->next.set_ptr( old_pool.get_ptr() );
- pool_.store( new_pool, memory_order_relaxed );
- }
- atomic< tagged_node_ptr > pool_;
- };
- class tagged_index
- {
- public:
- typedef std::uint16_t tag_t;
- typedef std::uint16_t index_t;
- /** uninitialized constructor */
- tagged_index( void ) noexcept //: index(0), tag(0)
- {}
- /** copy constructor */
- tagged_index( tagged_index const& rhs ) = default;
- explicit tagged_index( index_t i, tag_t t = 0 ) :
- index( i ),
- tag( t )
- {}
- /** index access */
- /* @{ */
- index_t get_index() const
- {
- return index;
- }
- void set_index( index_t i )
- {
- index = i;
- }
- /* @} */
- /** tag access */
- /* @{ */
- tag_t get_tag() const
- {
- return tag;
- }
- tag_t get_next_tag() const
- {
- tag_t next = ( get_tag() + 1u ) & ( std::numeric_limits< tag_t >::max )();
- return next;
- }
- void set_tag( tag_t t )
- {
- tag = t;
- }
- /* @} */
- bool operator==( tagged_index const& rhs ) const
- {
- return ( index == rhs.index ) && ( tag == rhs.tag );
- }
- bool operator!=( tagged_index const& rhs ) const
- {
- return !operator==( rhs );
- }
- protected:
- index_t index;
- tag_t tag;
- };
- //----------------------------------------------------------------------------------------------------------------------
- template < typename T, std::size_t size >
- struct alignas( cacheline_bytes ) compiletime_sized_freelist_storage
- {
- // array-based freelists only support a 16bit address space.
- BOOST_STATIC_ASSERT( size < 65536 );
- std::array< char, size * sizeof( T ) + 64 > data;
- // unused ... only for API purposes
- template < typename Allocator >
- compiletime_sized_freelist_storage( Allocator const& /* alloc */, std::size_t /* count */ )
- {
- data.fill( 0 );
- }
- T* nodes( void ) const
- {
- char* data_pointer = const_cast< char* >( data.data() );
- return reinterpret_cast< T* >( boost::alignment::align_up( data_pointer, cacheline_bytes ) );
- }
- std::size_t node_count( void ) const
- {
- return size;
- }
- };
- //----------------------------------------------------------------------------------------------------------------------
- template < typename T, typename Alloc = std::allocator< T > >
- struct runtime_sized_freelist_storage : boost::alignment::aligned_allocator_adaptor< Alloc, cacheline_bytes >
- {
- typedef boost::alignment::aligned_allocator_adaptor< Alloc, cacheline_bytes > allocator_type;
- T* nodes_;
- std::size_t node_count_;
- template < typename Allocator >
- runtime_sized_freelist_storage( Allocator const& alloc, std::size_t count ) :
- allocator_type( alloc ),
- node_count_( count )
- {
- if ( count > 65535 )
- boost::throw_exception( std::runtime_error( "boost.lockfree: freelist size is limited to a maximum of "
- "65535 objects" ) );
- nodes_ = allocator_type::allocate( count );
- std::memset( (void*)nodes_, 0, sizeof( T ) * count );
- }
- ~runtime_sized_freelist_storage( void )
- {
- allocator_type::deallocate( nodes_, node_count_ );
- }
- T* nodes( void ) const
- {
- return nodes_;
- }
- std::size_t node_count( void ) const
- {
- return node_count_;
- }
- };
- //----------------------------------------------------------------------------------------------------------------------
- template < typename T, typename NodeStorage = runtime_sized_freelist_storage< T > >
- class fixed_size_freelist : NodeStorage
- {
- struct freelist_node
- {
- tagged_index next;
- };
- void initialize( void )
- {
- T* nodes = NodeStorage::nodes();
- for ( std::size_t i = 0; i != NodeStorage::node_count(); ++i ) {
- tagged_index* next_index = reinterpret_cast< tagged_index* >( nodes + i );
- next_index->set_index( null_handle() );
- #ifdef BOOST_LOCKFREE_FREELIST_INIT_RUNS_DTOR
- destruct< false >( nodes + i );
- #else
- deallocate< false >( static_cast< index_t >( i ) );
- #endif
- }
- }
- public:
- typedef tagged_index tagged_node_handle;
- typedef tagged_index::index_t index_t;
- template < typename Allocator >
- fixed_size_freelist( Allocator const& alloc, std::size_t count ) :
- NodeStorage( alloc, count ),
- pool_( tagged_index( static_cast< index_t >( count ), 0 ) )
- {
- initialize();
- }
- fixed_size_freelist( void ) :
- pool_( tagged_index( NodeStorage::node_count(), 0 ) )
- {
- initialize();
- }
- template < bool ThreadSafe, bool Bounded >
- T* construct( void )
- {
- index_t node_index = allocate< ThreadSafe >();
- if ( node_index == null_handle() )
- return NULL;
- T* node = NodeStorage::nodes() + node_index;
- new ( node ) T();
- return node;
- }
- template < bool ThreadSafe, bool Bounded, typename ArgumentType >
- T* construct( const ArgumentType& arg )
- {
- index_t node_index = allocate< ThreadSafe >();
- if ( node_index == null_handle() )
- return NULL;
- T* node = NodeStorage::nodes() + node_index;
- new ( node ) T( arg );
- return node;
- }
- template < bool ThreadSafe, bool Bounded, typename ArgumentType >
- T* construct( ArgumentType&& arg )
- {
- index_t node_index = allocate< ThreadSafe >();
- if ( node_index == null_handle() )
- return NULL;
- T* node = NodeStorage::nodes() + node_index;
- new ( node ) T( std::forward< ArgumentType >( arg ) );
- return node;
- }
- template < bool ThreadSafe, bool Bounded, typename ArgumentType1, typename ArgumentType2 >
- T* construct( const ArgumentType1& arg1, const ArgumentType2& arg2 )
- {
- index_t node_index = allocate< ThreadSafe >();
- if ( node_index == null_handle() )
- return NULL;
- T* node = NodeStorage::nodes() + node_index;
- new ( node ) T( arg1, arg2 );
- return node;
- }
- template < bool ThreadSafe >
- void destruct( tagged_node_handle tagged_index )
- {
- index_t index = tagged_index.get_index();
- T* n = NodeStorage::nodes() + index;
- (void)n; // silence msvc warning
- n->~T();
- deallocate< ThreadSafe >( index );
- }
- template < bool ThreadSafe >
- void destruct( T* n )
- {
- n->~T();
- deallocate< ThreadSafe >( static_cast< index_t >( n - NodeStorage::nodes() ) );
- }
- bool is_lock_free( void ) const
- {
- return pool_.is_lock_free();
- }
- index_t null_handle( void ) const
- {
- return static_cast< index_t >( NodeStorage::node_count() );
- }
- index_t get_handle( T* pointer ) const
- {
- if ( pointer == NULL )
- return null_handle();
- else
- return static_cast< index_t >( pointer - NodeStorage::nodes() );
- }
- index_t get_handle( tagged_node_handle const& handle ) const
- {
- return handle.get_index();
- }
- T* get_pointer( tagged_node_handle const& tptr ) const
- {
- return get_pointer( tptr.get_index() );
- }
- T* get_pointer( index_t index ) const
- {
- if ( index == null_handle() )
- return 0;
- else
- return NodeStorage::nodes() + index;
- }
- T* get_pointer( T* ptr ) const
- {
- return ptr;
- }
- protected: // allow use from subclasses
- template < bool ThreadSafe >
- index_t allocate( void )
- {
- if ( ThreadSafe )
- return allocate_impl();
- else
- return allocate_impl_unsafe();
- }
- private:
- index_t allocate_impl( void )
- {
- tagged_index old_pool = pool_.load( memory_order_consume );
- for ( ;; ) {
- index_t index = old_pool.get_index();
- if ( index == null_handle() )
- return index;
- T* old_node = NodeStorage::nodes() + index;
- tagged_index* next_index = reinterpret_cast< tagged_index* >( old_node );
- tagged_index new_pool( next_index->get_index(), old_pool.get_next_tag() );
- if ( pool_.compare_exchange_weak( old_pool, new_pool ) )
- return old_pool.get_index();
- }
- }
- index_t allocate_impl_unsafe( void )
- {
- tagged_index old_pool = pool_.load( memory_order_consume );
- index_t index = old_pool.get_index();
- if ( index == null_handle() )
- return index;
- T* old_node = NodeStorage::nodes() + index;
- tagged_index* next_index = reinterpret_cast< tagged_index* >( old_node );
- tagged_index new_pool( next_index->get_index(), old_pool.get_next_tag() );
- pool_.store( new_pool, memory_order_relaxed );
- return old_pool.get_index();
- }
- template < bool ThreadSafe >
- void deallocate( index_t index )
- {
- if ( ThreadSafe )
- deallocate_impl( index );
- else
- deallocate_impl_unsafe( index );
- }
- void deallocate_impl( index_t index )
- {
- freelist_node* new_pool_node = reinterpret_cast< freelist_node* >( NodeStorage::nodes() + index );
- tagged_index old_pool = pool_.load( memory_order_consume );
- for ( ;; ) {
- tagged_index new_pool( index, old_pool.get_tag() );
- new_pool_node->next.set_index( old_pool.get_index() );
- if ( pool_.compare_exchange_weak( old_pool, new_pool ) )
- return;
- }
- }
- void deallocate_impl_unsafe( index_t index )
- {
- freelist_node* new_pool_node = reinterpret_cast< freelist_node* >( NodeStorage::nodes() + index );
- tagged_index old_pool = pool_.load( memory_order_consume );
- tagged_index new_pool( index, old_pool.get_tag() );
- new_pool_node->next.set_index( old_pool.get_index() );
- pool_.store( new_pool );
- }
- atomic< tagged_index > pool_;
- };
- //----------------------------------------------------------------------------------------------------------------------
- template < typename T, typename Alloc, bool IsCompileTimeSized, bool IsFixedSize, std::size_t Capacity >
- struct select_freelist
- {
- typedef std::conditional_t< IsCompileTimeSized,
- compiletime_sized_freelist_storage< T, Capacity >,
- runtime_sized_freelist_storage< T, Alloc > >
- fixed_sized_storage_type;
- typedef std::conditional_t< IsCompileTimeSized || IsFixedSize,
- fixed_size_freelist< T, fixed_sized_storage_type >,
- freelist_stack< T, Alloc > >
- type;
- };
- template < typename T, typename Alloc, bool IsCompileTimeSized, bool IsFixedSize, std::size_t Capacity >
- using select_freelist_t = typename select_freelist< T, Alloc, IsCompileTimeSized, IsFixedSize, Capacity >::type;
- //----------------------------------------------------------------------------------------------------------------------
- template < typename T, bool IsNodeBased >
- struct select_tagged_handle
- {
- typedef std::conditional_t< IsNodeBased, tagged_ptr< T >, tagged_index > tagged_handle_type;
- typedef std::conditional_t< IsNodeBased, T*, typename tagged_index::index_t > handle_type;
- };
- //----------------------------------------------------------------------------------------------------------------------
- }}} // namespace boost::lockfree::detail
- #if defined( _MSC_VER )
- # pragma warning( pop )
- #endif
- #endif /* BOOST_LOCKFREE_FREELIST_HPP_INCLUDED */
|