Added tree consistency checking to the map unit test
[libcds.git] / tests / unit / map2 / map_insdel_func.cpp
1 //$$CDS-header$$
2
3 #include <functional>
4 #include <mutex>    //unique_lock
5 #include "map2/map_types.h"
6 #include "cppunit/thread.h"
7
8 #include <cds/sync/spinlock.h>
9 #include <vector>
10 #include <algorithm>    // random_shuffle
11
12 namespace map2 {
13
14 #   define TEST_MAP(X)          void X() { test<MapTypes<key_type, value_type>::X >()    ; }
15 #   define TEST_MAP_EXTRACT(X)  TEST_MAP(X)
16 #   define TEST_MAP_NOLF(X)     void X() { test_nolf<MapTypes<key_type, value_type>::X >()    ; }
17 #   define TEST_MAP_NOLF_EXTRACT(X) TEST_MAP_NOLF(X)
18
19     namespace {
20         static size_t  c_nMapSize = 1000000    ;  // map size
21         static size_t  c_nInsertThreadCount = 4;  // count of insertion thread
22         static size_t  c_nDeleteThreadCount = 4;  // count of deletion thread
23         static size_t  c_nEnsureThreadCount = 4;  // count of ensure thread
24         static size_t  c_nThreadPassCount = 4  ;  // pass count for each thread
25         static size_t  c_nMaxLoadFactor = 8    ;  // maximum load factor
26         static bool    c_bPrintGCState = true;
27     }
28
29     class Map_InsDel_func: public CppUnitMini::TestCase
30     {
31         typedef size_t  key_type;
32         struct value_type {
33             size_t      nKey;
34             size_t      nData;
35             atomics::atomic<size_t> nEnsureCall;
36             atomics::atomic<bool>   bInitialized;
37             cds::OS::ThreadId          threadId     ;   // insert thread id
38
39             typedef cds::sync::spin_lock< cds::backoff::pause > lock_type;
40             mutable lock_type   m_access;
41
42             value_type()
43                 : nKey(0)
44                 , nData(0)
45                 , nEnsureCall(0)
46                 , bInitialized( false )
47                 , threadId( cds::OS::get_current_thread_id() )
48             {}
49
50             value_type( value_type const& s )
51                 : nKey(s.nKey)
52                 , nData(s.nData)
53                 , nEnsureCall(s.nEnsureCall.load(atomics::memory_order_relaxed))
54                 , bInitialized( s.bInitialized.load(atomics::memory_order_relaxed) )
55                 , threadId( cds::OS::get_current_thread_id() )
56             {}
57
58             // boost::container::flat_map requires operator =
59             value_type& operator=( value_type const& v )
60             {
61                 nKey = v.nKey;
62                 nData = v.nData;
63                 nEnsureCall.store( v.nEnsureCall.load(atomics::memory_order_relaxed), atomics::memory_order_relaxed );
64                 bInitialized.store(v.bInitialized.load(atomics::memory_order_relaxed), atomics::memory_order_relaxed);
65
66                 return *this;
67             }
68         };
69
70         typedef std::vector<key_type>   key_array;
71         key_array                       m_arrValues;
72
73         template <class Map>
74         class Inserter: public CppUnitMini::TestThread
75         {
76             Map&     m_Map;
77
78             virtual Inserter *    clone()
79             {
80                 return new Inserter( *this );
81             }
82
83             struct insert_functor {
84                 size_t nTestFunctorRef;
85
86                 insert_functor()
87                     : nTestFunctorRef(0)
88                 {}
89
90                 template <typename Pair>
91                 void operator()( Pair& val )
92                 {
93                     operator()( val.first, val.second );
94                 }
95
96                 template <typename Key, typename Val >
97                 void operator()( Key const& key, Val& v )
98                 {
99                     std::unique_lock< typename value_type::lock_type>    ac( v.m_access );
100
101                     v.nKey  = key;
102                     v.nData = key * 8;
103
104                     ++nTestFunctorRef;
105                     v.bInitialized.store( true, atomics::memory_order_relaxed);
106                 }
107             };
108
109         public:
110             size_t  m_nInsertSuccess;
111             size_t  m_nInsertFailed;
112
113             size_t  m_nTestFunctorRef;
114
115         public:
116             Inserter( CppUnitMini::ThreadPool& pool, Map& rMap )
117                 : CppUnitMini::TestThread( pool )
118                 , m_Map( rMap )
119             {}
120             Inserter( Inserter& src )
121                 : CppUnitMini::TestThread( src )
122                 , m_Map( src.m_Map )
123             {}
124
125             Map_InsDel_func&  getTest()
126             {
127                 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
128             }
129
130             virtual void init() { cds::threading::Manager::attachThread()   ; }
131             virtual void fini() { cds::threading::Manager::detachThread()   ; }
132
133             virtual void test()
134             {
135                 Map& rMap = m_Map;
136
137                 m_nInsertSuccess =
138                     m_nInsertFailed =
139                     m_nTestFunctorRef = 0;
140
141                 // func is passed by reference
142                 insert_functor  func;
143                 key_array const& arr = getTest().m_arrValues;
144
145                 if ( m_nThreadNo & 1 ) {
146                     for ( size_t nPass = 0; nPass < c_nThreadPassCount; ++nPass ) {
147                         for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
148                             if ( rMap.insert_with( *it, std::ref(func) ) )
149                                 ++m_nInsertSuccess;
150                             else
151                                 ++m_nInsertFailed;
152                         }
153                     }
154                 }
155                 else {
156                     for ( size_t nPass = 0; nPass < c_nThreadPassCount; ++nPass ) {
157                         for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
158                             if ( rMap.insert_with( *it, std::ref(func) ) )
159                                 ++m_nInsertSuccess;
160                             else
161                                 ++m_nInsertFailed;
162                         }
163                     }
164                 }
165
166                 m_nTestFunctorRef = func.nTestFunctorRef;
167             }
168         };
169
170         template <class Map>
171         class Ensurer: public CppUnitMini::TestThread
172         {
173             Map&     m_Map;
174
175             virtual Ensurer *    clone()
176             {
177                 return new Ensurer( *this );
178             }
179
180             struct ensure_functor {
181                 size_t  nCreated;
182                 size_t  nModified;
183
184                 ensure_functor()
185                     : nCreated(0)
186                     , nModified(0)
187                 {}
188
189                 template <typename Key, typename Val>
190                 void operator()( bool bNew, Key const& key, Val& v )
191                 {
192                     std::unique_lock<typename value_type::lock_type>    ac( v.m_access );
193                     if ( bNew ) {
194                         ++nCreated;
195                         v.nKey = key;
196                         v.nData = key * 8;
197                         v.bInitialized.store( true, atomics::memory_order_relaxed);
198                     }
199                     else {
200                         v.nEnsureCall.fetch_add( 1, atomics::memory_order_relaxed );
201                         ++nModified;
202                     }
203                 }
204
205                 template <typename Pair>
206                 void operator()( bool bNew, Pair& val )
207                 {
208                     operator()( bNew, val.first, val.second );
209                 }
210             private:
211                 ensure_functor(const ensure_functor& );
212             };
213
214         public:
215             size_t  m_nEnsureFailed;
216             size_t  m_nEnsureCreated;
217             size_t  m_nEnsureExisted;
218             size_t  m_nFunctorCreated;
219             size_t  m_nFunctorModified;
220
221         public:
222             Ensurer( CppUnitMini::ThreadPool& pool, Map& rMap )
223                 : CppUnitMini::TestThread( pool )
224                 , m_Map( rMap )
225             {}
226             Ensurer( Ensurer& src )
227                 : CppUnitMini::TestThread( src )
228                 , m_Map( src.m_Map )
229             {}
230
231             Map_InsDel_func&  getTest()
232             {
233                 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
234             }
235
236             virtual void init() { cds::threading::Manager::attachThread()   ; }
237             virtual void fini() { cds::threading::Manager::detachThread()   ; }
238
239             virtual void test()
240             {
241                 Map& rMap = m_Map;
242
243                 m_nEnsureCreated =
244                     m_nEnsureExisted =
245                     m_nEnsureFailed = 0;
246
247                 ensure_functor func;
248
249                 key_array const& arr = getTest().m_arrValues;
250
251                 if ( m_nThreadNo & 1 ) {
252                     for ( size_t nPass = 0; nPass < c_nThreadPassCount; ++nPass ) {
253                         for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
254                         //for ( size_t nItem = 0; nItem < c_nMapSize; ++nItem ) {
255                             std::pair<bool, bool> ret = rMap.ensure( *it, std::ref( func ) );
256                             if ( ret.first  ) {
257                                 if ( ret.second )
258                                     ++m_nEnsureCreated;
259                                 else
260                                     ++m_nEnsureExisted;
261                             }
262                             else
263                                 ++m_nEnsureFailed;
264                         }
265                     }
266                 }
267                 else {
268                     for ( size_t nPass = 0; nPass < c_nThreadPassCount; ++nPass ) {
269                         for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
270                             std::pair<bool, bool> ret = rMap.ensure( *it, std::ref( func ) );
271                             if ( ret.first  ) {
272                                 if ( ret.second )
273                                     ++m_nEnsureCreated;
274                                 else
275                                     ++m_nEnsureExisted;
276                             }
277                             else
278                                 ++m_nEnsureFailed;
279                         }
280                     }
281                 }
282
283                 m_nFunctorCreated = func.nCreated;
284                 m_nFunctorModified = func.nModified;
285             }
286         };
287
288         template <class Map>
289         class Deleter: public CppUnitMini::TestThread
290         {
291             Map&     m_Map;
292             typedef typename Map::mapped_type value_type;
293
294             virtual Deleter *    clone()
295             {
296                 return new Deleter( *this );
297             }
298
299             struct value_container
300             {
301                 size_t      nKeyExpected;
302
303                 size_t      nSuccessItem;
304                 size_t      nFailedItem;
305
306                 value_container()
307                     : nSuccessItem(0)
308                     , nFailedItem(0)
309                 {}
310             };
311
312             struct erase_functor {
313                 value_container     m_cnt;
314
315                 template <typename Key, typename Val>
316                 void operator()( Key const& /*key*/, Val& v )
317                 {
318                     while ( true ) {
319                         if ( v.bInitialized.load( atomics::memory_order_relaxed )) {
320                             std::unique_lock< typename value_type::lock_type>    ac( v.m_access );
321
322                             if ( m_cnt.nKeyExpected == v.nKey && m_cnt.nKeyExpected * 8 == v.nData )
323                                 ++m_cnt.nSuccessItem;
324                             else
325                                 ++m_cnt.nFailedItem;
326                             v.nData++;
327                             v.nKey = 0;
328                             break;
329                         }
330                         else
331                             cds::backoff::yield()();
332                     }
333                 }
334
335                 template <typename Pair>
336                 void operator ()( Pair& item )
337                 {
338                     operator()( item.first, item.second );
339                 }
340             };
341
342         public:
343             size_t  m_nDeleteSuccess;
344             size_t  m_nDeleteFailed;
345
346             size_t  m_nValueSuccess;
347             size_t  m_nValueFailed;
348
349         public:
350             Deleter( CppUnitMini::ThreadPool& pool, Map& rMap )
351                 : CppUnitMini::TestThread( pool )
352                 , m_Map( rMap )
353             {}
354             Deleter( Deleter& src )
355                 : CppUnitMini::TestThread( src )
356                 , m_Map( src.m_Map )
357             {}
358
359             Map_InsDel_func&  getTest()
360             {
361                 return reinterpret_cast<Map_InsDel_func&>( m_Pool.m_Test );
362             }
363
364             virtual void init() { cds::threading::Manager::attachThread()   ; }
365             virtual void fini() { cds::threading::Manager::detachThread()   ; }
366
367             virtual void test()
368             {
369                 Map& rMap = m_Map;
370
371                 m_nDeleteSuccess =
372                     m_nDeleteFailed = 0;
373
374                 erase_functor   func;
375                 key_array const& arr = getTest().m_arrValues;
376
377                 if ( m_nThreadNo & 1 ) {
378                     for ( size_t nPass = 0; nPass < c_nThreadPassCount; ++nPass ) {
379                         for ( key_array::const_iterator it = arr.begin(), itEnd = arr.end(); it != itEnd; ++it ) {
380                             func.m_cnt.nKeyExpected = *it;
381                             if ( rMap.erase( *it, std::ref(func) ))
382                                 ++m_nDeleteSuccess;
383                             else
384                                 ++m_nDeleteFailed;
385                         }
386                     }
387                 }
388                 else {
389                     for ( size_t nPass = 0; nPass < c_nThreadPassCount; ++nPass ) {
390                         for ( key_array::const_reverse_iterator it = arr.rbegin(), itEnd = arr.rend(); it != itEnd; ++it ) {
391                             func.m_cnt.nKeyExpected = *it;
392                             if ( rMap.erase( *it, std::ref(func) ))
393                                 ++m_nDeleteSuccess;
394                             else
395                                 ++m_nDeleteFailed;
396                         }
397                     }
398                 }
399
400                 m_nValueSuccess = func.m_cnt.nSuccessItem;
401                 m_nValueFailed = func.m_cnt.nFailedItem;
402             }
403         };
404
405     protected:
406
407         template <class Map>
408         void do_test( Map& testMap )
409         {
410             typedef Inserter<Map>       InserterThread;
411             typedef Deleter<Map>        DeleterThread;
412             typedef Ensurer<Map>        EnsurerThread;
413             cds::OS::Timer    timer;
414
415             m_arrValues.clear();
416             m_arrValues.reserve( c_nMapSize );
417             for ( size_t i = 0; i < c_nMapSize; ++i )
418                 m_arrValues.push_back( i );
419             std::random_shuffle( m_arrValues.begin(), m_arrValues.end() );
420
421             CppUnitMini::ThreadPool pool( *this );
422             pool.add( new InserterThread( pool, testMap ), c_nInsertThreadCount );
423             pool.add( new DeleterThread( pool, testMap ), c_nDeleteThreadCount );
424             pool.add( new EnsurerThread( pool, testMap ), c_nEnsureThreadCount );
425             pool.run();
426             CPPUNIT_MSG( "   Duration=" << pool.avgDuration() );
427
428             size_t nInsertSuccess = 0;
429             size_t nInsertFailed = 0;
430             size_t nDeleteSuccess = 0;
431             size_t nDeleteFailed = 0;
432             size_t nDelValueSuccess = 0;
433             size_t nDelValueFailed = 0;
434             size_t nEnsureFailed = 0;
435             size_t nEnsureCreated = 0;
436             size_t nEnsureModified = 0;
437             size_t nEnsFuncCreated = 0;
438             size_t nEnsFuncModified = 0;
439             size_t nTestFunctorRef = 0;
440
441             for ( CppUnitMini::ThreadPool::iterator it = pool.begin(); it != pool.end(); ++it ) {
442                 InserterThread * pThread = dynamic_cast<InserterThread *>( *it );
443                 if ( pThread ) {
444                     nInsertSuccess += pThread->m_nInsertSuccess;
445                     nInsertFailed += pThread->m_nInsertFailed;
446                     nTestFunctorRef += pThread->m_nTestFunctorRef;
447                 }
448                 else {
449                     DeleterThread * p = dynamic_cast<DeleterThread *>( *it );
450                     if ( p ) {
451                         nDeleteSuccess += p->m_nDeleteSuccess;
452                         nDeleteFailed += p->m_nDeleteFailed;
453                         nDelValueSuccess += p->m_nValueSuccess;
454                         nDelValueFailed += p->m_nValueFailed;
455                     }
456                     else {
457                         EnsurerThread * pEns = static_cast<EnsurerThread *>( *it );
458                         nEnsureCreated += pEns->m_nEnsureCreated;
459                         nEnsureModified += pEns->m_nEnsureExisted;
460                         nEnsureFailed += pEns->m_nEnsureFailed;
461                         nEnsFuncCreated += pEns->m_nFunctorCreated;
462                         nEnsFuncModified += pEns->m_nFunctorModified;
463                     }
464                 }
465             }
466
467             CPPUNIT_MSG( "    Totals: Ins succ=" << nInsertSuccess
468                 << " Del succ=" << nDeleteSuccess << "\n"
469                 << "          : Ins fail=" << nInsertFailed
470                 << " Del fail=" << nDeleteFailed << "\n"
471                 << "          : Ensure succ=" << (nEnsureCreated + nEnsureModified) << " fail=" << nEnsureFailed
472                 << " create=" << nEnsureCreated << " modify=" << nEnsureModified << "\n"
473                 << "          Map size=" << testMap.size()
474                 );
475
476             CPPUNIT_CHECK_EX( nDelValueFailed == 0, "Functor del failed=" << nDelValueFailed );
477             CPPUNIT_CHECK_EX( nDelValueSuccess == nDeleteSuccess,  "Delete success=" << nDeleteSuccess << " functor=" << nDelValueSuccess );
478
479             CPPUNIT_CHECK( nEnsureFailed == 0 );
480
481             CPPUNIT_CHECK_EX( nEnsureCreated == nEnsFuncCreated, "Ensure created=" << nEnsureCreated << " functor=" << nEnsFuncCreated );
482             CPPUNIT_CHECK_EX( nEnsureModified == nEnsFuncModified, "Ensure modified=" << nEnsureModified << " functor=" << nEnsFuncModified );
483
484             // nTestFunctorRef is call count of insert functor
485             CPPUNIT_CHECK_EX( nTestFunctorRef == nInsertSuccess, "nInsertSuccess=" << nInsertSuccess << " functor nTestFunctorRef=" << nTestFunctorRef );
486
487             check_before_cleanup( testMap );
488
489             CPPUNIT_MSG( "  Clear map (single-threaded)..." );
490             timer.reset();
491             for ( size_t nItem = 0; nItem < c_nMapSize; ++nItem ) {
492                 testMap.erase( nItem );
493             }
494             CPPUNIT_MSG( "   Duration=" << timer.duration() );
495             CPPUNIT_CHECK( testMap.empty() );
496
497             additional_check( testMap );
498             print_stat( testMap );
499             additional_cleanup( testMap );
500         }
501
502         template <class Map>
503         void test()
504         {
505             CPPUNIT_MSG( "Thread count: insert=" << c_nInsertThreadCount
506                 << " delete=" << c_nDeleteThreadCount
507                 << " ensure=" << c_nEnsureThreadCount
508                 << " pass count=" << c_nThreadPassCount
509                 << " map size=" << c_nMapSize
510                 );
511
512             for ( size_t nLoadFactor = 1; nLoadFactor <= c_nMaxLoadFactor; nLoadFactor *= 2 ) {
513                 CPPUNIT_MSG( "Load factor=" << nLoadFactor );
514                 Map  testMap( c_nMapSize, nLoadFactor );
515                 do_test( testMap );
516                 if ( c_bPrintGCState )
517                     print_gc_state();
518             }
519
520         }
521
522         template <class Map>
523         void test_nolf()
524         {
525             CPPUNIT_MSG( "Thread count: insert=" << c_nInsertThreadCount
526                 << " delete=" << c_nDeleteThreadCount
527                 << " ensure=" << c_nEnsureThreadCount
528                 << " pass count=" << c_nThreadPassCount
529                 << " map size=" << c_nMapSize
530                 );
531
532             Map testMap;
533             do_test( testMap );
534             if ( c_bPrintGCState )
535                 print_gc_state();
536         }
537
538         void setUpParams( const CppUnitMini::TestCfg& cfg ) {
539             c_nInsertThreadCount = cfg.getULong("InsertThreadCount", 4 );
540             c_nDeleteThreadCount = cfg.getULong("DeleteThreadCount", 4 );
541             c_nEnsureThreadCount = cfg.getULong("EnsureThreadCount", 4 );
542             c_nThreadPassCount = cfg.getULong("ThreadPassCount", 4 );
543             c_nMapSize = cfg.getULong("MapSize", 1000000 );
544             c_nMaxLoadFactor = cfg.getULong("MaxLoadFactor", 8 );
545             c_bPrintGCState = cfg.getBool("PrintGCStateFlag", true );
546         }
547
548 #   include "map2/map_defs.h"
549     CDSUNIT_DECLARE_MichaelMap
550     CDSUNIT_DECLARE_SplitList
551     CDSUNIT_DECLARE_SkipListMap
552     CDSUNIT_DECLARE_EllenBinTreeMap
553     CDSUNIT_DECLARE_BronsonAVLTreeMap
554     CDSUNIT_DECLARE_StripedMap
555     CDSUNIT_DECLARE_RefinableMap
556     CDSUNIT_DECLARE_CuckooMap
557
558     CPPUNIT_TEST_SUITE( Map_InsDel_func )
559         CDSUNIT_TEST_MichaelMap
560         CDSUNIT_TEST_SplitList
561         CDSUNIT_TEST_SkipListMap
562         CDSUNIT_TEST_EllenBinTreeMap
563         CDSUNIT_TEST_BronsonAVLTreeMap
564         CDSUNIT_TEST_StripedMap
565         CDSUNIT_TEST_RefinableMap
566         CDSUNIT_TEST_CuckooMap
567     CPPUNIT_TEST_SUITE_END()
568
569     };
570
571     CPPUNIT_TEST_SUITE_REGISTRATION( Map_InsDel_func );
572 } // namespace map2