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