From: khizmax Date: Sun, 9 Aug 2015 21:01:35 +0000 (+0300) Subject: intrusive MultiLevelHashSet: X-Git-Tag: v2.1.0~165 X-Git-Url: http://plrg.eecs.uci.edu/git/?a=commitdiff_plain;h=ced7a1bcbb3351e10502458d4e6160a7a153c651;p=libcds.git intrusive MultiLevelHashSet: - added single-threaded interface sufficiency test - fixed found bugs --- diff --git a/cds/algo/split_bitstring.h b/cds/algo/split_bitstring.h index 19c183fd..432c4038 100644 --- a/cds/algo/split_bitstring.h +++ b/cds/algo/split_bitstring.h @@ -9,7 +9,7 @@ namespace cds { namespace algo { /// Cuts a bit sequence from fixed-size bit-string /** - The splitter an be used as iterator over bit-string. + The splitter can be used as iterator over bit-string. Each call of \p cut() or \p safe_cut() cuts the bit count specified and keeps the position inside bit-string for the next call. @@ -31,7 +31,7 @@ namespace cds { namespace algo { //@endcond public: - /// Initializises the splitter with reference to \p h + /// Initializises the splitter with reference to \p h and zero start bit offset explicit split_bitstring( bitstring const& h ) : m_ptr(reinterpret_cast( &h )) , m_pos(0) @@ -41,6 +41,17 @@ namespace cds { namespace algo { # endif {} + /// Initializises the splitter with reference to \p h and start bit offset \p nBitOffset + split_bitstring( bitstring const& h, size_t nBitOffset ) + : m_ptr( reinterpret_cast( &h ) + nBitOffset / c_nBitPerInt ) + , m_pos( nBitOffset % c_nBitPerInt ) + , m_first( m_ptr ) +# ifdef _DEBUG + , m_last( m_ptr + c_nHashSize ) +# endif + {} + + /// Returns \p true if end-of-string is not reached yet explicit operator bool() const { diff --git a/cds/gc/details/hp.h b/cds/gc/details/hp.h index 8170b8f1..b0a3b5e1 100644 --- a/cds/gc/details/hp.h +++ b/cds/gc/details/hp.h @@ -635,7 +635,7 @@ namespace cds { /// Auto hp_guard. /** - This class encapsulates Hazard Pointer guard to protect a pointer against deletion . + This class encapsulates Hazard Pointer guard to protect a pointer against deletion. It allocates one HP from thread's HP array in constructor and free the hazard pointer allocated in destructor. */ diff --git a/cds/gc/impl/hp_decl.h b/cds/gc/impl/hp_decl.h index 4625cc06..f2e42084 100644 --- a/cds/gc/impl/hp_decl.h +++ b/cds/gc/impl/hp_decl.h @@ -435,15 +435,16 @@ namespace cds { namespace gc { /// Creates empty guarded pointer guarded_ptr() CDS_NOEXCEPT : m_pGuard(nullptr) - {} + { + alloc_guard(); + } //@cond /// Initializes guarded pointer with \p p explicit guarded_ptr( guarded_type * p ) CDS_NOEXCEPT + : m_pGuard( nullptr ) { - alloc_guard(); - assert( m_pGuard ); - m_pGuard->set(p); + reset(p); } explicit guarded_ptr( std::nullptr_t ) CDS_NOEXCEPT : m_pGuard( nullptr ) @@ -539,6 +540,13 @@ namespace cds { namespace gc { assert( m_pGuard ); return *m_pGuard; } + + void reset(guarded_type * p) CDS_NOEXCEPT + { + alloc_guard(); + assert( m_pGuard ); + m_pGuard->set(p); + } //@endcond private: diff --git a/cds/intrusive/details/multilevel_hashset_base.h b/cds/intrusive/details/multilevel_hashset_base.h index 63d57bb4..c47ea817 100644 --- a/cds/intrusive/details/multilevel_hashset_base.h +++ b/cds/intrusive/details/multilevel_hashset_base.h @@ -85,9 +85,11 @@ namespace cds { namespace intrusive { event_counter m_nSlotChanged; ///< Number of array node slot changing by other thread during an operation event_counter m_nSlotConverting; ///< Number of events when we encounter a slot while it is converting to array node + event_counter m_nArrayNodeCount; ///< Number of array nodes + //@cond void onInsertSuccess() { ++m_nInsertSuccess; } - void onInserFailed() { ++m_nInsertFailed; } + void onInsertFailed() { ++m_nInsertFailed; } void onInsertRetry() { ++m_nInsertRetry; } void onUpdateNew() { ++m_nUpdateNew; } void onUpdateExisting() { ++m_nUpdateExisting; } @@ -103,6 +105,7 @@ namespace cds { namespace intrusive { void onExpandNodeFailed() { ++m_nExpandNodeFailed; } void onSlotChanged() { ++m_nSlotChanged; } void onSlotConverting() { ++m_nSlotConverting; } + void onArrayNodeCreated() { ++m_nArrayNodeCount; } //@endcond }; @@ -126,6 +129,7 @@ namespace cds { namespace intrusive { void onExpandNodeFailed() const {} void onSlotChanged() const {} void onSlotConverting() const {} + void onArrayNodeCreated() const {} //@endcond }; diff --git a/cds/intrusive/impl/multilevel_hashset.h b/cds/intrusive/impl/multilevel_hashset.h index 9f0db90a..4924713d 100644 --- a/cds/intrusive/impl/multilevel_hashset.h +++ b/cds/intrusive/impl/multilevel_hashset.h @@ -163,7 +163,7 @@ namespace cds { namespace intrusive { public: /// Creates empty set /** - @param head_bits: 2head_bits specifies the size of head array, minimum is 8. + @param head_bits: 2head_bits specifies the size of head array, minimum is 4. @param array_bits: 2array_bits specifies the size of array node, minimum is 2. Equation for \p head_bits and \p array_bits: @@ -224,6 +224,7 @@ namespace cds { namespace intrusive { typename gc::Guard guard; back_off bkoff; + size_t nOffset = m_Metrics.head_node_size_log; atomic_node_ptr * pArr = m_Head; size_t nSlot = splitter.cut( m_Metrics.head_node_size_log ); assert( nSlot < m_Metrics.head_node_size ); @@ -236,6 +237,7 @@ namespace cds { namespace intrusive { nSlot = splitter.cut( m_Metrics.array_node_size_log ); assert( nSlot < m_Metrics.array_node_size ); pArr = to_array( slot.ptr() ); + nOffset += m_Metrics.array_node_size_log; } else if ( slot.bits() == array_converting ) { // the slot is converting to array node right now @@ -259,13 +261,7 @@ namespace cds { namespace intrusive { } // the slot must be expanded - atomic_node_ptr * pNewArr; - size_t nNewSlot; - std::tie( pNewArr, nNewSlot ) = expand_slot( pArr[ nSlot ], slot, splitter ); - if ( pNewArr ) { - pArr = pNewArr; - nSlot = nNewSlot; - } + expand_slot( pArr[ nSlot ], slot, nOffset ); } else { // the slot is empty, try to insert data node @@ -302,7 +298,6 @@ namespace cds { namespace intrusive { (i.e. the item has been inserted or updated), \p second is \p true if new item has been added or \p false if the set contains that hash. */ - template std::pair update( value_type& val, bool bInsert = true ) { hash_type const& hash = hash_accessor()( val ); @@ -314,6 +309,7 @@ namespace cds { namespace intrusive { atomic_node_ptr * pArr = m_Head; size_t nSlot = splitter.cut( m_Metrics.head_node_size_log ); assert( nSlot < m_Metrics.head_node_size ); + size_t nOffset = m_Metrics.head_node_size_log; while ( true ) { node_ptr slot = pArr[nSlot].load( memory_model::memory_order_acquire ); @@ -323,6 +319,7 @@ namespace cds { namespace intrusive { nSlot = splitter.cut( m_Metrics.array_node_size_log ); assert( nSlot < m_Metrics.array_node_size ); pArr = to_array( slot.ptr() ); + nOffset += m_Metrics.array_node_size_log; } else if ( slot.bits() == array_converting ) { // the slot is converting to array node right now @@ -342,7 +339,12 @@ namespace cds { namespace intrusive { if ( cmp( hash, hash_accessor()( *slot.ptr() )) == 0 ) { // the item with that hash value already exists // Replace it with val - if ( pArr[nSlot].compare_exchange_strong( slot, node_ptr( &val ), memory_model::memory_order_release, atomics::memory_order_relaxed ) ) { + if ( slot.ptr() == &val ) { + m_Stat.onUpdateExisting(); + return std::make_pair( true, false ); + } + + if ( pArr[nSlot].compare_exchange_strong( slot, node_ptr( &val ), memory_model::memory_order_release, atomics::memory_order_relaxed )) { // slot can be disposed gc::template retire( slot.ptr() ); m_Stat.onUpdateExisting(); @@ -354,13 +356,7 @@ namespace cds { namespace intrusive { } // the slot must be expanded - atomic_node_ptr * pNewArr; - size_t nNewSlot; - std::tie( pNewArr, nNewSlot ) = expand_slot( pArr[ nSlot ], slot, splitter ); - if ( pNewArr ) { - pArr = pNewArr; - nSlot = nNewSlot; - } + expand_slot( pArr[ nSlot ], slot, nOffset ); } else { // the slot is empty, try to insert data node @@ -369,7 +365,6 @@ namespace cds { namespace intrusive { if ( pArr[nSlot].compare_exchange_strong( pNull, node_ptr( &val ), memory_model::memory_order_release, atomics::memory_order_relaxed )) { // the new data node has been inserted - f( val ); ++m_ItemCounter; m_Stat.onUpdateNew(); return std::make_pair( true, true ); @@ -395,7 +390,7 @@ namespace cds { namespace intrusive { The function returns \p true if success and \p false otherwise. */ - bool unlink( value_type& val ) + bool unlink( value_type const& val ) { typename gc::Guard guard; auto pred = [&val](value_type const& item) -> bool { return &item == &val; }; @@ -485,17 +480,20 @@ namespace cds { namespace intrusive { */ guarded_ptr extract( hash_type const& hash ) { - typename gc::Guard guard; - value_type * p = do_erase( hash, guard, []( value_type const&) -> bool {return true; } ); - - // p is guarded by HP - if ( p ) { - gc::template retire( p ); - --m_ItemCounter; - m_Stat.onEraseSuccess(); - return guarded_ptr(p); + guarded_ptr gp; + { + typename gc::Guard guard; + value_type * p = do_erase( hash, guard, []( value_type const&) -> bool {return true; } ); + + // p is guarded by HP + if ( p ) { + gc::template retire( p ); + --m_ItemCounter; + m_Stat.onEraseSuccess(); + gp.reset( p ); + } } - return guarded_ptr(); + return gp; } /// Finds an item by it's \p hash @@ -565,13 +563,12 @@ namespace cds { namespace intrusive { */ guarded_ptr get( hash_type const& hash ) { - typename gc::Guard guard; - value_type * p = search( hash, guard ); - - // p is guarded by HP - if ( p ) - return guarded_ptr( p ); - return guarded_ptr(); + guarded_ptr gp; + { + typename gc::Guard guard; + gp.reset( search( hash, guard )); + } + return gp; } /// Clears the set (non-atomic) @@ -629,8 +626,8 @@ namespace cds { namespace intrusive { if ( array_bits < 2 ) array_bits = 2; - if ( head_bits < 8 ) - head_bits = 8; + if ( head_bits < 4 ) + head_bits = 4; if ( head_bits > hash_bits ) head_bits = hash_bits; if ( (hash_bits - head_bits) % array_bits != 0 ) @@ -713,9 +710,11 @@ namespace cds { namespace intrusive { else { // data node if ( pArr->compare_exchange_strong( slot, node_ptr(), memory_model::memory_order_acquire, atomics::memory_order_relaxed )) { - gc::template retire( slot.ptr() ); - --m_ItemCounter; - m_Stat.onEraseSuccess(); + if ( slot.ptr() ) { + gc::template retire( slot.ptr() ); + --m_ItemCounter; + m_Stat.onEraseSuccess(); + } break; } } @@ -740,21 +739,26 @@ namespace cds { namespace intrusive { { return converter( p ).pArr; } - static node_ptr * to_node( atomic_node_ptr * p ) + static value_type * to_node( atomic_node_ptr * p ) { return converter( p ).pData; } - std::pair< atomic_node_ptr *, size_t > expand_slot( atomic_node_ptr& slot, node_ptr current, hash_splitter& splitter ) + bool expand_slot( atomic_node_ptr& slot, node_ptr current, size_t nOffset ) { + assert( current.bits() == 0 ); + assert( current.ptr() ); + + size_t idx = hash_splitter(hash_accessor()(*current.ptr()), nOffset).cut( m_Metrics.array_node_size_log ); + atomic_node_ptr * pArr = alloc_array_node(); + node_ptr cur(current.ptr()); if ( !slot.compare_exchange_strong( cur, cur | array_converting, memory_model::memory_order_release, atomics::memory_order_relaxed )) { m_Stat.onExpandNodeFailed(); - return std::make_pair(static_cast(nullptr), size_t(0)); + free_array_node( pArr ); + return false; } - atomic_node_ptr * pArr = alloc_array_node(); - size_t idx = splitter.cut( m_Metrics.array_node_size_log ); pArr[idx].store( current, memory_model::memory_order_release ); cur = cur | array_converting; @@ -762,7 +766,8 @@ namespace cds { namespace intrusive { slot.compare_exchange_strong( cur, node_ptr( to_node( pArr ), array_node ), memory_model::memory_order_release, atomics::memory_order_relaxed ) ); - return std::make_pair( pArr, idx ); + m_Stat.onArrayNodeCreated(); + return true; } value_type * search( hash_type const& hash, typename gc::Guard& guard ) @@ -800,7 +805,7 @@ namespace cds { namespace intrusive { } else if ( slot.ptr() && cmp( hash, hash_accessor()( *slot.ptr() )) == 0 ) { // item found - m_Stat.onFindSucces(); + m_Stat.onFindSuccess(); return slot.ptr(); } m_Stat.onFindFailed(); diff --git a/projects/Win/vc12/cds.sln b/projects/Win/vc12/cds.sln index 3f7bde00..c63ed1af 100644 --- a/projects/Win/vc12/cds.sln +++ b/projects/Win/vc12/cds.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Express 2013 for Windows Desktop -VisualStudioVersion = 12.0.31101.0 +VisualStudioVersion = 12.0.40629.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cds", "cds.vcxproj", "{408FE9BC-44F0-4E6A-89FA-D6F952584239}" EndProject @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "unit-test", "unit-test", "{ ..\..\..\tests\unit\print_cuckoo_stat.h = ..\..\..\tests\unit\print_cuckoo_stat.h ..\..\..\tests\unit\print_ellenbintree_stat.h = ..\..\..\tests\unit\print_ellenbintree_stat.h ..\..\..\tests\unit\print_mspriorityqueue_stat.h = ..\..\..\tests\unit\print_mspriorityqueue_stat.h + ..\..\..\tests\unit\print_multilevel_hashset_stat.h = ..\..\..\tests\unit\print_multilevel_hashset_stat.h ..\..\..\tests\unit\print_segmentedqueue_stat.h = ..\..\..\tests\unit\print_segmentedqueue_stat.h ..\..\..\tests\unit\print_skip_list_stat.h = ..\..\..\tests\unit\print_skip_list_stat.h ..\..\..\tests\unit\print_split_list_stat.h = ..\..\..\tests\unit\print_split_list_stat.h diff --git a/tests/test-hdr/set/hdr_intrusive_multilevel_hashset.h b/tests/test-hdr/set/hdr_intrusive_multilevel_hashset.h index 8eb17e37..d882c6a2 100644 --- a/tests/test-hdr/set/hdr_intrusive_multilevel_hashset.h +++ b/tests/test-hdr/set/hdr_intrusive_multilevel_hashset.h @@ -22,6 +22,16 @@ namespace set { { unsigned int nDisposeCount ; // count of disposer calling Hash hash; + unsigned int nInsertCall; + unsigned int nFindCall; + unsigned int nEraseCall; + + Item() + : nDisposeCount(0) + , nInsertCall(0) + , nFindCall(0) + , nEraseCall(0) + {} }; template @@ -33,16 +43,194 @@ namespace set { } }; + struct item_disposer { + template + void operator()( Item * p ) + { + ++p->nDisposeCount; + } + }; + template void test_hp() { - Set s; + typedef typename Set::hash_type hash_type; + typedef typename Set::value_type value_type; + + std::hash hasher; + + size_t const arrCapacity = 1000; + std::vector< value_type > arrValue; + arrValue.reserve( arrCapacity ); + for ( size_t i = 0; i < arrCapacity; ++i ) { + arrValue.emplace_back( value_type() ); + arrValue.back().hash = hasher( i ); + } + CPPUNIT_ASSERT( arrValue.size() == arrCapacity ); + + Set s( 4, 2 ); + CPPUNIT_ASSERT(s.head_size() == 16 ); + CPPUNIT_ASSERT(s.array_node_size() == 4 ); + + // insert() test + CPPUNIT_ASSERT(s.size() == 0 ); + CPPUNIT_ASSERT(s.empty() ); + for ( auto& el : arrValue ) { + CPPUNIT_ASSERT( s.insert( el )); + CPPUNIT_ASSERT(s.contains( el.hash )); + } + CPPUNIT_ASSERT(s.size() == arrCapacity ); + for ( auto& el : arrValue ) { + CPPUNIT_ASSERT(s.contains( el.hash )); + CPPUNIT_ASSERT( !s.insert( el ) ); + } + CPPUNIT_ASSERT(s.size() == arrCapacity ); + CPPUNIT_ASSERT( !s.empty() ); + + // update() exists test + for ( auto& el : arrValue ) { + bool bOp, bInsert; + std::tie(bOp, bInsert) = s.update( el, false ); + CPPUNIT_ASSERT( bOp ); + CPPUNIT_ASSERT( !bInsert ); + CPPUNIT_ASSERT( el.nFindCall == 0 ); + CPPUNIT_ASSERT(s.find(el.hash, [](value_type& v) { v.nFindCall++; } )); + CPPUNIT_ASSERT( el.nFindCall == 1 ); + } + + // unlink test + CPPUNIT_ASSERT(s.size() == arrCapacity ); + for ( auto const& el : arrValue ) { + CPPUNIT_ASSERT(s.unlink( el )); + CPPUNIT_ASSERT(!s.contains( el.hash )); + } + CPPUNIT_ASSERT(s.size() == 0 ); + Set::gc::force_dispose(); + for ( auto const& el : arrValue ) { + CPPUNIT_ASSERT( el.nDisposeCount == 1 ); + } + + // new hash values + for ( auto& el : arrValue ) + el.hash = hasher( el.hash ); + + // insert( func ) + CPPUNIT_ASSERT(s.size() == 0 ); + for ( auto& el : arrValue ) { + CPPUNIT_ASSERT( s.insert( el, []( value_type& v ) { ++v.nInsertCall; } )); + CPPUNIT_ASSERT(s.contains( el.hash )); + CPPUNIT_ASSERT( el.nInsertCall == 1 ); + } + CPPUNIT_ASSERT(s.size() == arrCapacity ); + for ( auto& el : arrValue ) { + CPPUNIT_ASSERT(s.contains( el.hash )); + CPPUNIT_ASSERT( !s.insert( el ) ); + } + CPPUNIT_ASSERT(s.size() == arrCapacity ); + CPPUNIT_ASSERT( !s.empty() ); + + for ( auto& el : arrValue ) + el.nDisposeCount = 0; + + s.clear(); + CPPUNIT_ASSERT(s.size() == 0 ); + Set::gc::force_dispose(); + for ( auto const& el : arrValue ) { + CPPUNIT_ASSERT( el.nDisposeCount == 1 ); + } + + // new hash values + for ( auto& el : arrValue ) + el.hash = hasher( el.hash ); + + // update test + for ( auto& el : arrValue ) { + bool bOp, bInsert; + std::tie(bOp, bInsert) = s.update( el, false ); + CPPUNIT_ASSERT( !bOp ); + CPPUNIT_ASSERT( !bInsert ); + CPPUNIT_ASSERT( !s.contains( el.hash )); + + std::tie(bOp, bInsert) = s.update( el, true ); + CPPUNIT_ASSERT( bOp ); + CPPUNIT_ASSERT( bInsert ); + CPPUNIT_ASSERT( s.contains( el.hash )); + } + CPPUNIT_ASSERT(s.size() == arrCapacity ); + + // erase test + for ( auto& el : arrValue ) { + el.nDisposeCount = 0; + CPPUNIT_ASSERT( s.contains( el.hash )); + CPPUNIT_ASSERT(s.erase( el.hash )); + CPPUNIT_ASSERT( !s.contains( el.hash )); + CPPUNIT_ASSERT( !s.erase( el.hash )); + } + CPPUNIT_ASSERT(s.size() == 0 ); + Set::gc::force_dispose(); + for ( auto& el : arrValue ) { + CPPUNIT_ASSERT( el.nDisposeCount == 1 ); + CPPUNIT_ASSERT(s.insert( el )); + } + + // erase with functor, get() test + for ( auto& el : arrValue ) { + el.nDisposeCount = 0; + CPPUNIT_ASSERT( s.contains( el.hash ) ); + { + typename Set::guarded_ptr gp{ s.get( el.hash ) }; + CPPUNIT_ASSERT( gp ); + CPPUNIT_ASSERT( gp->nEraseCall == 0); + CPPUNIT_ASSERT(s.erase( gp->hash, []( value_type& i ) { ++i.nEraseCall; } )); + CPPUNIT_ASSERT( gp->nEraseCall == 1); + Set::gc::force_dispose(); + CPPUNIT_ASSERT( gp->nDisposeCount == 0 ); + } + CPPUNIT_ASSERT( !s.contains( el.hash )); + CPPUNIT_ASSERT( !s.erase( el.hash )); + CPPUNIT_ASSERT( el.nEraseCall == 1 ); + Set::gc::force_dispose(); + CPPUNIT_ASSERT( el.nDisposeCount == 1 ); + } + CPPUNIT_ASSERT(s.size() == 0 ); + + // new hash values + for ( auto& el : arrValue ) { + el.hash = hasher( el.hash ); + el.nDisposeCount = 0; + bool bOp, bInsert; + std::tie(bOp, bInsert) = s.update( el ); + CPPUNIT_ASSERT( bOp ); + CPPUNIT_ASSERT( bInsert ); + } + CPPUNIT_ASSERT(s.size() == arrCapacity ); + + // extract test + for ( auto& el : arrValue ) { + CPPUNIT_ASSERT( s.contains( el.hash ) ); + typename Set::guarded_ptr gp = s.extract( el.hash ); + CPPUNIT_ASSERT( gp ); + Set::gc::force_dispose(); + CPPUNIT_ASSERT( el.nDisposeCount == 0 ); + CPPUNIT_ASSERT( gp->nDisposeCount == 0 ); + gp = s.get( el.hash ); + CPPUNIT_ASSERT( !gp ); + Set::gc::force_dispose(); + CPPUNIT_ASSERT( el.nDisposeCount == 1 ); + CPPUNIT_ASSERT( !s.contains( el.hash ) ); + } + CPPUNIT_ASSERT(s.size() == 0 ); + CPPUNIT_ASSERT(s.empty() ); + + CPPUNIT_MSG( s.statistics() ); } void hp_stdhash(); + void hp_stdhash_stat(); CPPUNIT_TEST_SUITE(IntrusiveMultiLevelHashSetHdrTest) CPPUNIT_TEST(hp_stdhash) + CPPUNIT_TEST(hp_stdhash_stat) CPPUNIT_TEST_SUITE_END() }; } // namespace set diff --git a/tests/test-hdr/set/hdr_intrusive_multilevel_hashset_hp.cpp b/tests/test-hdr/set/hdr_intrusive_multilevel_hashset_hp.cpp index fb80ee16..83f2fd3a 100644 --- a/tests/test-hdr/set/hdr_intrusive_multilevel_hashset_hp.cpp +++ b/tests/test-hdr/set/hdr_intrusive_multilevel_hashset_hp.cpp @@ -2,6 +2,7 @@ #include "set/hdr_intrusive_multilevel_hashset.h" #include +#include "unit/print_multilevel_hashset_stat.h" namespace set { namespace { @@ -15,6 +16,7 @@ namespace set { struct traits: public ci::multilevel_hashset::traits { typedef get_hash hash_accessor; + typedef item_disposer disposer; }; typedef ci::MultiLevelHashSet< gc_type, Item, traits > set_type; test_hp(); @@ -24,10 +26,37 @@ namespace set { Item, typename ci::multilevel_hashset::make_traits< ci::multilevel_hashset::hash_accessor< get_hash> + , ci::opt::disposer< item_disposer > >::type > set_type2; test_hp(); } + + void IntrusiveMultiLevelHashSetHdrTest::hp_stdhash_stat() + { + typedef size_t hash_type; + + struct traits: public ci::multilevel_hashset::traits + { + typedef get_hash hash_accessor; + typedef item_disposer disposer; + typedef ci::multilevel_hashset::stat<> stat; + }; + typedef ci::MultiLevelHashSet< gc_type, Item, traits > set_type; + test_hp(); + + typedef ci::MultiLevelHashSet< + gc_type, + Item, + typename ci::multilevel_hashset::make_traits< + ci::multilevel_hashset::hash_accessor< get_hash> + , ci::opt::disposer< item_disposer > + ,co::stat< ci::multilevel_hashset::stat<>> + >::type + > set_type2; + test_hp(); + } + } // namespace set CPPUNIT_TEST_SUITE_REGISTRATION(set::IntrusiveMultiLevelHashSetHdrTest); diff --git a/tests/unit/print_multilevel_hashset_stat.h b/tests/unit/print_multilevel_hashset_stat.h new file mode 100644 index 00000000..2183328e --- /dev/null +++ b/tests/unit/print_multilevel_hashset_stat.h @@ -0,0 +1,41 @@ +//$$CDS-header$$ + +#ifndef CDSUNIT_PRINT_MULTILEVEL_HASHSET_STAT_H +#define CDSUNIT_PRINT_MULTILEVEL_HASHSET_STAT_H + +#include +#include + +namespace std { + + static inline ostream& operator <<( ostream& o, cds::intrusive::multilevel_hashset::stat<> const& s ) + { + return + o << "Stat [cds::intrusive::multilevel_hashset::stat]\n" + << "\t\t m_nInsertSuccess: " << s.m_nInsertSuccess.get() << "\n" + << "\t\t m_nInsertFailed: " << s.m_nInsertFailed.get() << "\n" + << "\t\t m_nInsertRetry: " << s.m_nInsertRetry.get() << "\n" + << "\t\t m_nUpdateNew: " << s.m_nUpdateNew.get() << "\n" + << "\t\t m_nUpdateExisting: " << s.m_nUpdateExisting.get() << "\n" + << "\t\t m_nUpdateFailed: " << s.m_nUpdateFailed.get() << "\n" + << "\t\t m_nUpdateRetry: " << s.m_nUpdateRetry.get() << "\n" + << "\t\t m_nEraseSuccess: " << s.m_nEraseSuccess.get() << "\n" + << "\t\t m_nEraseFailed: " << s.m_nEraseFailed.get() << "\n" + << "\t\t m_nEraseRetry: " << s.m_nEraseRetry.get() << "\n" + << "\t\t m_nFindSuccess: " << s.m_nFindSuccess.get() << "\n" + << "\t\t m_nFindFailed: " << s.m_nFindFailed.get() << "\n" + << "\t\t m_nExpandNodeSuccess: " << s.m_nExpandNodeSuccess.get() << "\n" + << "\t\t m_nExpandNodeFailed: " << s.m_nExpandNodeFailed.get() << "\n" + << "\t\t m_nSlotChanged: " << s.m_nSlotChanged.get() << "\n" + << "\t\t m_nSlotConverting: " << s.m_nSlotConverting.get() << "\n" + << "\t\t m_nArrayNodeCount: " << s.m_nArrayNodeCount.get() << "\n"; + } + + static inline ostream& operator <<( ostream& o, cds::intrusive::multilevel_hashset::empty_stat const& /*s*/ ) + { + return o; + } + +} // namespace std + +#endif // #ifndef CDSUNIT_PRINT_MULTILEVEL_HASHSET_STAT_H