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 for ( auto it = rSet.begin(); it != rSet.end(); ++it ) {
409 it->val.hash = CityHash64( it->key.c_str(), it->key.length());
416 template <typename RCU, class Set>
417 class Iterator<cds::urcu::gc<RCU>, Set>: public cds_test::thread
419 typedef cds_test::thread base_class;
422 typedef typename Set::value_type keyval_type;
425 size_t m_nPassCount = 0;
426 size_t m_nVisitCount = 0; // how many items the iterator visited
429 Iterator( cds_test::thread_pool& pool, Set& set )
430 : base_class( pool, iterator_thread )
434 Iterator( Iterator& src )
439 virtual thread * clone()
441 return new Iterator( *this );
448 Set_Iteration& fixture = pool().template fixture<Set_Iteration>();
449 while ( !fixture.all_modifiers_done() ) {
451 typename Set::rcu_lock l;
452 for ( auto it = rSet.begin(); it != rSet.end(); ++it ) {
453 it->val.hash = CityHash64( it->key.c_str(), it->key.length() );
462 void do_test( Set& testSet )
464 typedef Inserter<Set> InserterThread;
465 typedef Deleter<Set> DeleterThread;
466 typedef Iterator<typename Set::gc, Set> IteratorThread;
468 cds_test::thread_pool& pool = get_pool();
469 pool.add( new InserterThread( pool, testSet ), s_nInsertThreadCount );
470 pool.add( new DeleterThread( pool, testSet ), s_nDeleteThreadCount );
472 m_nModifierCount.store( pool.size(), atomics::memory_order_relaxed );
473 pool.add( new IteratorThread( pool, testSet ), 1 );
475 propout() << std::make_pair( "insert_thread_count", s_nInsertThreadCount )
476 << std::make_pair( "delete_thread_count", s_nDeleteThreadCount )
477 << std::make_pair( "thread_pass_count", s_nThreadPassCount )
478 << std::make_pair( "set_size", s_nSetSize );
480 std::chrono::milliseconds duration = pool.run();
482 propout() << std::make_pair( "duration", duration );
484 size_t nInsertSuccess = 0;
485 size_t nInsertFailed = 0;
486 size_t nDeleteSuccess = 0;
487 size_t nDeleteFailed = 0;
488 size_t nIteratorPassCount = 0;
489 size_t nIteratorVisitCount = 0;
490 for ( size_t i = 0; i < pool.size(); ++i ) {
491 cds_test::thread& thr = pool.get( i );
492 switch ( thr.type() ) {
495 InserterThread& inserter = static_cast<InserterThread&>( thr );
496 nInsertSuccess += inserter.m_nInsertSuccess;
497 nInsertFailed += inserter.m_nInsertFailed;
502 DeleterThread& deleter = static_cast<DeleterThread&>(thr);
503 nDeleteSuccess += deleter.m_nDeleteSuccess;
504 nDeleteFailed += deleter.m_nDeleteFailed;
507 case iterator_thread:
509 IteratorThread& iter = static_cast<IteratorThread&>(thr);
510 nIteratorPassCount += iter.m_nPassCount;
511 nIteratorVisitCount += iter.m_nVisitCount;
515 assert( false ); // Forgot anything?..
520 << std::make_pair( "insert_success", nInsertSuccess )
521 << std::make_pair( "delete_success", nDeleteSuccess )
522 << std::make_pair( "insert_failed", nInsertFailed )
523 << std::make_pair( "delete_failed", nDeleteFailed )
524 << std::make_pair( "iterator_pass_count", nIteratorPassCount )
525 << std::make_pair( "iterator_visit_count", nIteratorVisitCount )
526 << std::make_pair( "final_set_size", testSet.size() );
529 EXPECT_TRUE( testSet.empty() );
531 additional_check( testSet );
532 print_stat( propout(), testSet );
533 additional_cleanup( testSet );
537 void do_test_extract( Set& testSet )
539 typedef Inserter<Set> InserterThread;
540 typedef Deleter<Set> DeleterThread;
541 typedef Extractor<typename Set::gc, Set> ExtractThread;
542 typedef Iterator<typename Set::gc, Set> IteratorThread;
544 size_t const nDelThreadCount = s_nDeleteThreadCount / 2;
545 size_t const nExtractThreadCount = s_nDeleteThreadCount - nDelThreadCount;
547 cds_test::thread_pool& pool = get_pool();
548 pool.add( new InserterThread( pool, testSet ), s_nInsertThreadCount );
549 pool.add( new DeleterThread( pool, testSet ), nDelThreadCount );
550 pool.add( new ExtractThread( pool, testSet ), nExtractThreadCount );
552 m_nModifierCount.store( pool.size(), atomics::memory_order_relaxed );
553 pool.add( new IteratorThread( pool, testSet ), 1 );
555 propout() << std::make_pair( "insert_thread_count", s_nInsertThreadCount )
556 << std::make_pair( "delete_thread_count", nDelThreadCount )
557 << std::make_pair( "extract_thread_count", nExtractThreadCount )
558 << std::make_pair( "thread_pass_count", s_nThreadPassCount )
559 << std::make_pair( "set_size", s_nSetSize );
561 std::chrono::milliseconds duration = pool.run();
563 propout() << std::make_pair( "duration", duration );
565 size_t nInsertSuccess = 0;
566 size_t nInsertFailed = 0;
567 size_t nDeleteSuccess = 0;
568 size_t nDeleteFailed = 0;
569 size_t nExtractSuccess = 0;
570 size_t nExtractFailed = 0;
571 size_t nIteratorPassCount = 0;
572 size_t nIteratorVisitCount = 0;
573 for ( size_t i = 0; i < pool.size(); ++i ) {
574 cds_test::thread& thr = pool.get( i );
575 switch ( thr.type() ) {
578 InserterThread& inserter = static_cast<InserterThread&>(thr);
579 nInsertSuccess += inserter.m_nInsertSuccess;
580 nInsertFailed += inserter.m_nInsertFailed;
585 DeleterThread& deleter = static_cast<DeleterThread&>(thr);
586 nDeleteSuccess += deleter.m_nDeleteSuccess;
587 nDeleteFailed += deleter.m_nDeleteFailed;
592 ExtractThread& extractor = static_cast<ExtractThread&>(thr);
593 nExtractSuccess += extractor.m_nDeleteSuccess;
594 nExtractFailed += extractor.m_nDeleteFailed;
597 case iterator_thread:
599 IteratorThread& iter = static_cast<IteratorThread&>(thr);
600 nIteratorPassCount += iter.m_nPassCount;
601 nIteratorVisitCount += iter.m_nVisitCount;
605 assert( false ); // Forgot anything?..
610 << std::make_pair( "insert_success", nInsertSuccess )
611 << std::make_pair( "delete_success", nDeleteSuccess )
612 << std::make_pair( "extract_success", nExtractSuccess )
613 << std::make_pair( "insert_failed", nInsertFailed )
614 << std::make_pair( "delete_failed", nDeleteFailed )
615 << std::make_pair( "extract_failed", nExtractFailed )
616 << std::make_pair( "iterator_pass_count", nIteratorPassCount )
617 << std::make_pair( "iterator_visit_count", nIteratorVisitCount )
618 << std::make_pair( "final_set_size", testSet.size() );
621 EXPECT_TRUE( testSet.empty() );
623 additional_check( testSet );
624 print_stat( propout(), testSet );
625 additional_cleanup( testSet );
631 ASSERT_TRUE( m_arrString.size() > 0 );
638 void run_test_extract()
640 ASSERT_TRUE( m_arrString.size() > 0 );
643 do_test_extract( s );
647 class Set_Iteration_LF: public Set_Iteration
648 , public ::testing::WithParamInterface<size_t>
654 s_nLoadFactor = GetParam();
655 propout() << std::make_pair( "load_factor", s_nLoadFactor );
656 Set_Iteration::run_test<Set>();
660 void run_test_extract()
662 s_nLoadFactor = GetParam();
663 propout() << std::make_pair( "load_factor", s_nLoadFactor );
664 Set_Iteration::run_test_extract<Set>();
667 static std::vector<size_t> get_load_factors();