2 This file is a part of libcds - Concurrent Data Structures library
4 (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
6 Source code repo: http://github.com/khizmax/libcds/
7 Download: http://sourceforge.net/projects/libcds/files/
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <cds_test/city.h>
36 // Test for set's thread-safe iterator:
37 // Several thread inserts/erases elemets from the set.
38 // Dedicated Iterator thread iterates over the set, calculates CityHash for each element
39 // and stores it in the element.
40 // Test goal: no crash
42 #define TEST_CASE(TAG, X) void X();
44 class Set_Iteration: public cds_test::stress_fixture
47 static size_t s_nSetSize; // set size
48 static size_t s_nInsertThreadCount; // count of insertion thread
49 static size_t s_nDeleteThreadCount; // count of deletion thread
50 static size_t s_nThreadPassCount; // pass count for each thread
51 static size_t s_nMaxLoadFactor; // maximum load factor
53 static size_t s_nCuckooInitialSize; // initial size for CuckooSet
54 static size_t s_nCuckooProbesetSize; // CuckooSet probeset size (only for list-based probeset)
55 static size_t s_nCuckooProbesetThreshold; // CUckooSet probeset threshold (0 - use default)
57 static size_t s_nFeldmanSet_HeadBits;
58 static size_t s_nFeldmanSet_ArrayBits;
60 static size_t s_nLoadFactor;
61 static std::vector<std::string> m_arrString;
63 static void SetUpTestCase();
64 static void TearDownTestCase();
66 void on_modifier_done()
68 m_nModifierCount.fetch_sub( 1, atomics::memory_order_relaxed );
71 bool all_modifiers_done() const
73 return m_nModifierCount.load( atomics::memory_order_relaxed ) == 0;
76 typedef std::string key_type;
83 explicit value_type( size_t v )
97 atomics::atomic<size_t> m_nModifierCount;
100 class Inserter: public cds_test::thread
102 typedef cds_test::thread base_class;
105 typedef typename Set::value_type keyval_type;
108 size_t m_nInsertSuccess = 0;
109 size_t m_nInsertFailed = 0;
112 Inserter( cds_test::thread_pool& pool, Set& set )
113 : base_class( pool, insert_thread )
117 Inserter( Inserter& src )
122 virtual thread * clone()
124 return new Inserter( *this );
131 Set_Iteration& fixture = pool().template fixture<Set_Iteration>();
132 size_t nArrSize = m_arrString.size();
133 size_t const nSetSize = fixture.s_nSetSize;
134 size_t const nPassCount = fixture.s_nThreadPassCount;
137 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
138 for ( size_t nItem = 0; nItem < nSetSize; ++nItem ) {
139 if ( rSet.insert( keyval_type( m_arrString[nItem % nArrSize], nItem * 8 )))
147 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
148 for ( size_t nItem = nSetSize; nItem > 0; --nItem ) {
149 if ( rSet.insert( keyval_type( m_arrString[nItem % nArrSize], nItem * 8 )))
157 fixture.on_modifier_done();
162 class Deleter: public cds_test::thread
164 typedef cds_test::thread base_class;
168 size_t m_nDeleteSuccess = 0;
169 size_t m_nDeleteFailed = 0;
172 Deleter( cds_test::thread_pool& pool, Set& set )
173 : base_class( pool, delete_thread )
177 Deleter( Deleter& src )
182 virtual thread * clone()
184 return new Deleter( *this );
191 Set_Iteration& fixture = pool().template fixture<Set_Iteration>();
192 size_t nArrSize = m_arrString.size();
193 size_t const nSetSize = fixture.s_nSetSize;
194 size_t const nPassCount = fixture.s_nThreadPassCount;
197 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
198 for ( size_t nItem = 0; nItem < nSetSize; ++nItem ) {
199 if ( rSet.erase( m_arrString[nItem % nArrSize] ))
207 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
208 for ( size_t nItem = nSetSize; nItem > 0; --nItem ) {
209 if ( rSet.erase( m_arrString[nItem % nArrSize] ))
217 fixture.on_modifier_done();
221 template <typename GC, class Set>
222 class Extractor: public cds_test::thread
224 typedef cds_test::thread base_class;
228 size_t m_nDeleteSuccess = 0;
229 size_t m_nDeleteFailed = 0;
232 Extractor( cds_test::thread_pool& pool, Set& set )
233 : base_class( pool, extract_thread )
237 Extractor( Extractor& src )
242 virtual thread * clone()
244 return new Extractor( *this );
251 typename Set::guarded_ptr gp;
253 Set_Iteration& fixture = pool().template fixture<Set_Iteration>();
254 size_t nArrSize = m_arrString.size();
255 size_t const nSetSize = fixture.s_nSetSize;
256 size_t const nPassCount = fixture.s_nThreadPassCount;
259 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
260 for ( size_t nItem = 0; nItem < nSetSize; ++nItem ) {
261 gp = rSet.extract( m_arrString[nItem % nArrSize] );
271 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
272 for ( size_t nItem = nSetSize; nItem > 0; --nItem ) {
273 gp = rSet.extract( m_arrString[nItem % nArrSize] );
283 fixture.on_modifier_done();
287 template <typename RCU, class Set>
288 class Extractor<cds::urcu::gc<RCU>, Set >: public cds_test::thread
290 typedef cds_test::thread base_class;
294 size_t m_nDeleteSuccess = 0;
295 size_t m_nDeleteFailed = 0;
298 Extractor( cds_test::thread_pool& pool, Set& set )
299 : base_class( pool, extract_thread )
303 Extractor( Extractor& src )
308 virtual thread * clone()
310 return new Extractor( *this );
317 typename Set::exempt_ptr xp;
319 Set_Iteration& fixture = pool().template fixture<Set_Iteration>();
320 size_t nArrSize = m_arrString.size();
321 size_t const nSetSize = fixture.s_nSetSize;
322 size_t const nPassCount = fixture.s_nThreadPassCount;
325 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
326 for ( size_t nItem = 0; nItem < nSetSize; ++nItem ) {
327 if ( Set::c_bExtractLockExternal ) {
328 typename Set::rcu_lock l;
329 xp = rSet.extract( m_arrString[nItem % nArrSize] );
336 xp = rSet.extract( m_arrString[nItem % nArrSize] );
347 for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
348 for ( size_t nItem = nSetSize; nItem > 0; --nItem ) {
349 if ( Set::c_bExtractLockExternal ) {
350 typename Set::rcu_lock l;
351 xp = rSet.extract( m_arrString[nItem % nArrSize] );
358 xp = rSet.extract( m_arrString[nItem % nArrSize] );
369 fixture.on_modifier_done();
373 template <typename GC, class Set>
374 class Iterator: public cds_test::thread
376 typedef cds_test::thread base_class;
379 typedef typename Set::value_type keyval_type;
382 size_t m_nPassCount = 0;
383 size_t m_nVisitCount = 0; // how many items the iterator visited
386 Iterator( cds_test::thread_pool& pool, Set& set )
387 : base_class( pool, iterator_thread )
391 Iterator( Iterator& src )
396 virtual thread * clone()
398 return new Iterator( *this );
405 Set_Iteration& fixture = pool().template fixture<Set_Iteration>();
406 while ( !fixture.all_modifiers_done() ) {
408 typename Set::iterator it;
409 typename Set::iterator itEnd;
411 for ( it = rSet.begin(); it != itEnd; ++it ) {
412 it->val.hash = CityHash64( it->key.c_str(), it->key.length());
419 template <typename RCU, class Set>
420 class Iterator<cds::urcu::gc<RCU>, Set>: public cds_test::thread
422 typedef cds_test::thread base_class;
425 typedef typename Set::value_type keyval_type;
428 size_t m_nPassCount = 0;
429 size_t m_nVisitCount = 0; // how many items the iterator visited
432 Iterator( cds_test::thread_pool& pool, Set& set )
433 : base_class( pool, iterator_thread )
437 Iterator( Iterator& src )
442 virtual thread * clone()
444 return new Iterator( *this );
451 Set_Iteration& fixture = pool().template fixture<Set_Iteration>();
452 while ( !fixture.all_modifiers_done() ) {
454 typename Set::rcu_lock l;
455 for ( auto it = rSet.begin(); it != rSet.end(); ++it ) {
456 it->val.hash = CityHash64( it->key.c_str(), it->key.length() );
465 void do_test( Set& testSet )
467 typedef Inserter<Set> InserterThread;
468 typedef Deleter<Set> DeleterThread;
469 typedef Iterator<typename Set::gc, Set> IteratorThread;
471 cds_test::thread_pool& pool = get_pool();
472 pool.add( new InserterThread( pool, testSet ), s_nInsertThreadCount );
473 pool.add( new DeleterThread( pool, testSet ), s_nDeleteThreadCount );
475 m_nModifierCount.store( pool.size(), atomics::memory_order_relaxed );
476 pool.add( new IteratorThread( pool, testSet ), 1 );
478 propout() << std::make_pair( "insert_thread_count", s_nInsertThreadCount )
479 << std::make_pair( "delete_thread_count", s_nDeleteThreadCount )
480 << std::make_pair( "thread_pass_count", s_nThreadPassCount )
481 << std::make_pair( "set_size", s_nSetSize );
483 std::chrono::milliseconds duration = pool.run();
485 propout() << std::make_pair( "duration", duration );
487 size_t nInsertSuccess = 0;
488 size_t nInsertFailed = 0;
489 size_t nDeleteSuccess = 0;
490 size_t nDeleteFailed = 0;
491 size_t nIteratorPassCount = 0;
492 size_t nIteratorVisitCount = 0;
493 for ( size_t i = 0; i < pool.size(); ++i ) {
494 cds_test::thread& thr = pool.get( i );
495 switch ( thr.type() ) {
498 InserterThread& inserter = static_cast<InserterThread&>( thr );
499 nInsertSuccess += inserter.m_nInsertSuccess;
500 nInsertFailed += inserter.m_nInsertFailed;
505 DeleterThread& deleter = static_cast<DeleterThread&>(thr);
506 nDeleteSuccess += deleter.m_nDeleteSuccess;
507 nDeleteFailed += deleter.m_nDeleteFailed;
510 case iterator_thread:
512 IteratorThread& iter = static_cast<IteratorThread&>(thr);
513 nIteratorPassCount += iter.m_nPassCount;
514 nIteratorVisitCount += iter.m_nVisitCount;
518 assert( false ); // Forgot anything?..
523 << std::make_pair( "insert_success", nInsertSuccess )
524 << std::make_pair( "delete_success", nDeleteSuccess )
525 << std::make_pair( "insert_failed", nInsertFailed )
526 << std::make_pair( "delete_failed", nDeleteFailed )
527 << std::make_pair( "iterator_pass_count", nIteratorPassCount )
528 << std::make_pair( "iterator_visit_count", nIteratorVisitCount )
529 << std::make_pair( "final_set_size", testSet.size() );
532 EXPECT_TRUE( testSet.empty() );
534 additional_check( testSet );
535 print_stat( propout(), testSet );
536 additional_cleanup( testSet );
540 void do_test_extract( Set& testSet )
542 typedef Inserter<Set> InserterThread;
543 typedef Deleter<Set> DeleterThread;
544 typedef Extractor<typename Set::gc, Set> ExtractThread;
545 typedef Iterator<typename Set::gc, Set> IteratorThread;
547 size_t const nDelThreadCount = s_nDeleteThreadCount / 2;
548 size_t const nExtractThreadCount = s_nDeleteThreadCount - nDelThreadCount;
550 cds_test::thread_pool& pool = get_pool();
551 pool.add( new InserterThread( pool, testSet ), s_nInsertThreadCount );
552 pool.add( new DeleterThread( pool, testSet ), nDelThreadCount );
553 pool.add( new ExtractThread( pool, testSet ), nExtractThreadCount );
555 m_nModifierCount.store( pool.size(), atomics::memory_order_relaxed );
556 pool.add( new IteratorThread( pool, testSet ), 1 );
558 propout() << std::make_pair( "insert_thread_count", s_nInsertThreadCount )
559 << std::make_pair( "delete_thread_count", nDelThreadCount )
560 << std::make_pair( "extract_thread_count", nExtractThreadCount )
561 << std::make_pair( "thread_pass_count", s_nThreadPassCount )
562 << std::make_pair( "set_size", s_nSetSize );
564 std::chrono::milliseconds duration = pool.run();
566 propout() << std::make_pair( "duration", duration );
568 size_t nInsertSuccess = 0;
569 size_t nInsertFailed = 0;
570 size_t nDeleteSuccess = 0;
571 size_t nDeleteFailed = 0;
572 size_t nExtractSuccess = 0;
573 size_t nExtractFailed = 0;
574 size_t nIteratorPassCount = 0;
575 size_t nIteratorVisitCount = 0;
576 for ( size_t i = 0; i < pool.size(); ++i ) {
577 cds_test::thread& thr = pool.get( i );
578 switch ( thr.type() ) {
581 InserterThread& inserter = static_cast<InserterThread&>(thr);
582 nInsertSuccess += inserter.m_nInsertSuccess;
583 nInsertFailed += inserter.m_nInsertFailed;
588 DeleterThread& deleter = static_cast<DeleterThread&>(thr);
589 nDeleteSuccess += deleter.m_nDeleteSuccess;
590 nDeleteFailed += deleter.m_nDeleteFailed;
595 ExtractThread& extractor = static_cast<ExtractThread&>(thr);
596 nExtractSuccess += extractor.m_nDeleteSuccess;
597 nExtractFailed += extractor.m_nDeleteFailed;
600 case iterator_thread:
602 IteratorThread& iter = static_cast<IteratorThread&>(thr);
603 nIteratorPassCount += iter.m_nPassCount;
604 nIteratorVisitCount += iter.m_nVisitCount;
608 assert( false ); // Forgot anything?..
613 << std::make_pair( "insert_success", nInsertSuccess )
614 << std::make_pair( "delete_success", nDeleteSuccess )
615 << std::make_pair( "extract_success", nExtractSuccess )
616 << std::make_pair( "insert_failed", nInsertFailed )
617 << std::make_pair( "delete_failed", nDeleteFailed )
618 << std::make_pair( "extract_failed", nExtractFailed )
619 << std::make_pair( "iterator_pass_count", nIteratorPassCount )
620 << std::make_pair( "iterator_visit_count", nIteratorVisitCount )
621 << std::make_pair( "final_set_size", testSet.size() );
624 EXPECT_TRUE( testSet.empty() );
626 additional_check( testSet );
627 print_stat( propout(), testSet );
628 additional_cleanup( testSet );
634 ASSERT_TRUE( m_arrString.size() > 0 );
641 void run_test_extract()
643 ASSERT_TRUE( m_arrString.size() > 0 );
646 do_test_extract( s );
650 class Set_Iteration_LF: public Set_Iteration
651 , public ::testing::WithParamInterface<size_t>
657 s_nLoadFactor = GetParam();
658 propout() << std::make_pair( "load_factor", s_nLoadFactor );
659 Set_Iteration::run_test<Set>();
663 void run_test_extract()
665 s_nLoadFactor = GetParam();
666 propout() << std::make_pair( "load_factor", s_nLoadFactor );
667 Set_Iteration::run_test_extract<Set>();
670 static std::vector<size_t> get_load_factors();