Suppressed false-positive CppCheck warnings
[libcds.git] / test / stress / set / insdel_func / set_insdel_func.h
1 /*
2     This file is a part of libcds - Concurrent Data Structures library
3
4     (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
5
6     Source code repo: http://github.com/khizmax/libcds/
7     Download: http://sourceforge.net/projects/libcds/files/
8     
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions are met:
11
12     * Redistributions of source code must retain the above copyright notice, this
13       list of conditions and the following disclaimer.
14
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.
18
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.     
29 */
30
31 #include "set_type.h"
32
33 namespace set {
34
35     class Set_InsDel_func: public cds_test::stress_fixture
36     {
37     public:
38         static size_t s_nSetSize;               // set size
39         static size_t s_nInsertThreadCount;     // count of insertion thread
40         static size_t s_nDeleteThreadCount;     // count of deletion thread
41         static size_t s_nUpdateThreadCount;     // count of updating thread
42         static size_t s_nThreadPassCount;       // pass count for each thread
43         static size_t s_nMaxLoadFactor;         // maximum load factor
44
45         static size_t s_nCuckooInitialSize;     // initial size for CuckooSet
46         static size_t s_nCuckooProbesetSize;    // CuckooSet probeset size (only for list-based probeset)
47         static size_t s_nCuckooProbesetThreshold; // CUckooSet probeset threshold (0 - use default)
48
49         static size_t s_nFeldmanSet_HeadBits;
50         static size_t s_nFeldmanSet_ArrayBits;
51
52         static size_t s_nLoadFactor;
53
54         static void SetUpTestCase();
55         //static void TearDownTestCase();
56
57     public:
58         typedef size_t  key_type;
59
60         struct value {
61             size_t      nKey;
62             size_t      nData;
63             atomics::atomic<size_t> nUpdateCall;
64             bool volatile   bInitialized;
65             cds::OS::ThreadId          threadId     ;   // insert thread id
66
67             typedef cds::sync::spin_lock< cds::backoff::pause > lock_type;
68             mutable lock_type   m_access;
69
70             value()
71                 : nKey(0)
72                 , nData(0)
73                 , nUpdateCall(0)
74                 , bInitialized( false )
75                 , threadId( cds::OS::get_current_thread_id() )
76             {}
77
78             value( value const& s )
79                 : nKey(s.nKey)
80                 , nData(s.nData)
81                 , nUpdateCall(s.nUpdateCall.load(atomics::memory_order_relaxed))
82                 , bInitialized( s.bInitialized )
83                 , threadId( cds::OS::get_current_thread_id() )
84                 , m_access()
85             {}
86
87             // boost::container::flat_map requires operator =
88             // cppcheck-suppress operatorEqVarError
89             value& operator=( value const& v )
90             {
91                 nKey = v.nKey;
92                 nData = v.nData;
93                 threadId = v.threadId;
94                 nUpdateCall.store( v.nUpdateCall.load(atomics::memory_order_relaxed), atomics::memory_order_relaxed );
95                 bInitialized = v.bInitialized;
96
97                 return *this;
98             }
99         };
100
101         size_t *    m_pKeyFirst;
102         size_t *    m_pKeyLast;
103         size_t *    m_pKeyArr;
104
105         enum {
106             insert_thread,
107             update_thread,
108             delete_thread
109         };
110
111         template <class Set>
112         class Inserter: public cds_test::thread
113         {
114             typedef cds_test::thread base_class;
115
116             Set&     m_Set;
117             typedef typename Set::value_type keyval_type;
118
119             struct insert_functor {
120                 size_t nTestFunctorRef;
121
122                 insert_functor()
123                     : nTestFunctorRef(0)
124                 {}
125                 insert_functor( insert_functor const& ) = delete;
126
127                 void operator()( keyval_type& val )
128                 {
129                     std::unique_lock< typename value::lock_type> ac( val.val.m_access );
130
131                     val.val.nKey  = val.key;
132                     val.val.nData = val.key * 8;
133
134                     ++nTestFunctorRef;
135                     val.val.bInitialized = true;
136                 }
137             };
138
139         public:
140             size_t  m_nInsertSuccess = 0;
141             size_t  m_nInsertFailed = 0;
142             size_t  m_nTestFunctorRef = 0;
143
144         public:
145             Inserter( cds_test::thread_pool& pool, Set& set )
146                 : base_class( pool, insert_thread )
147                 , m_Set( set )
148             {}
149
150             Inserter( Inserter& src )
151                 : base_class( src )
152                 , m_Set( src.m_Set )
153             {}
154
155             virtual thread * clone()
156             {
157                 return new Inserter( *this );
158             }
159
160             virtual void test()
161             {
162                 Set& rSet = m_Set;
163                 Set_InsDel_func& fixture = pool().template fixture<Set_InsDel_func>();
164
165                 size_t * pKeyFirst      = fixture.m_pKeyFirst;
166                 size_t * pKeyLast       = fixture.m_pKeyLast;
167                 size_t const nPassCount = fixture.s_nThreadPassCount;
168
169                 // func is passed by reference
170                 insert_functor  func;
171
172                 if ( id() & 1 ) {
173                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
174                         for ( size_t * p = pKeyFirst; p < pKeyLast; ++p ) {
175                             if ( rSet.insert( *p, std::ref( func )))
176                                 ++m_nInsertSuccess;
177                             else
178                                 ++m_nInsertFailed;
179                         }
180                     }
181                 }
182                 else {
183                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
184                         for ( size_t * p = pKeyLast - 1; p >= pKeyFirst; --p ) {
185                             if ( rSet.insert( *p, std::ref( func )))
186                                 ++m_nInsertSuccess;
187                             else
188                                 ++m_nInsertFailed;
189                         }
190                     }
191                 }
192
193                 m_nTestFunctorRef = func.nTestFunctorRef;
194             }
195         };
196
197         template <class Set>
198         class Updater: public cds_test::thread
199         {
200             typedef cds_test::thread base_class;
201
202             Set&     m_Set;
203             typedef typename Set::value_type keyval_type;
204
205             struct update_functor {
206                 size_t  nCreated = 0;
207                 size_t  nModified = 0;
208
209                 update_functor() {}
210                 update_functor( const update_functor& ) = delete;
211
212                 void operator()( bool bNew, keyval_type& val, size_t /*nKey*/ )
213                 {
214                     std::unique_lock<typename value::lock_type> ac( val.val.m_access );
215                     if ( !val.val.bInitialized )
216                     {
217                         val.val.nKey = val.key;
218                         val.val.nData = val.key * 8;
219                         val.val.bInitialized = true;
220                     }
221
222                     if ( bNew ) {
223                         ++nCreated;
224                     }
225                     else {
226                         val.val.nUpdateCall.fetch_add( 1, atomics::memory_order_relaxed );
227                         ++nModified;
228                     }
229                 }
230
231                 void operator()( keyval_type& cur, keyval_type * old )
232                 {
233                     operator()( old == nullptr, cur, 0 );
234                 }
235             };
236
237         public:
238             size_t  m_nUpdateFailed = 0;
239             size_t  m_nUpdateCreated = 0;
240             size_t  m_nUpdateExisted = 0;
241             size_t  m_nFunctorCreated = 0;
242             size_t  m_nFunctorModified = 0;
243
244         public:
245             Updater( cds_test::thread_pool& pool, Set& set )
246                 : base_class( pool, update_thread )
247                 , m_Set( set )
248             {}
249
250             Updater( Updater& src )
251                 : base_class( src )
252                 , m_Set( src.m_Set )
253             {}
254
255             virtual thread * clone()
256             {
257                 return new Updater( *this );
258             }
259
260             virtual void test()
261             {
262                 Set& rSet = m_Set;
263
264                 Set_InsDel_func& fixture = pool().template fixture<Set_InsDel_func>();
265                 size_t * pKeyFirst = fixture.m_pKeyFirst;
266                 size_t * pKeyLast = fixture.m_pKeyLast;
267                 size_t const nPassCount = fixture.s_nThreadPassCount;
268
269                 update_functor func;
270
271                 if ( id() & 1 ) {
272                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
273                         for ( size_t * p = pKeyFirst; p < pKeyLast; ++p ) {
274                             std::pair<bool, bool> ret = rSet.update( *p, std::ref( func ), true );
275                             if ( ret.first  ) {
276                                 if ( ret.second )
277                                     ++m_nUpdateCreated;
278                                 else
279                                     ++m_nUpdateExisted;
280                             }
281                             else
282                                 ++m_nUpdateFailed;
283                         }
284                     }
285                 }
286                 else {
287                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
288                         for ( size_t * p = pKeyLast - 1 ; p >= pKeyFirst; --p ) {
289                             std::pair<bool, bool> ret = rSet.update( *p, std::ref( func ), true );
290                             if ( ret.first  ) {
291                                 if ( ret.second )
292                                     ++m_nUpdateCreated;
293                                 else
294                                     ++m_nUpdateExisted;
295                             }
296                             else
297                                 ++m_nUpdateFailed;
298                         }
299                     }
300                 }
301
302                 m_nFunctorCreated = func.nCreated;
303                 m_nFunctorModified = func.nModified;
304             }
305         };
306
307         template <class Set>
308         class Deleter: public cds_test::thread
309         {
310             typedef cds_test::thread base_class;
311
312             Set&     m_Set;
313             typedef typename Set::value_type keyval_type;
314
315             struct value_container
316             {
317                 size_t      nKeyExpected;
318
319                 size_t      nSuccessItem = 0;
320                 size_t      nFailedItem = 0;
321             };
322
323             struct erase_functor {
324                 value_container     m_cnt;
325
326                 void operator ()( keyval_type const& itm )
327                 {
328                     keyval_type& item = const_cast<keyval_type&>(itm);
329                     while ( true ) {
330                         bool bBkoff = false;
331                         {
332                             std::unique_lock< typename value::lock_type> ac( item.val.m_access );
333                             if ( item.val.bInitialized ) {
334                                 if ( m_cnt.nKeyExpected == item.val.nKey && m_cnt.nKeyExpected * 8 == item.val.nData )
335                                     ++m_cnt.nSuccessItem;
336                                 else
337                                     ++m_cnt.nFailedItem;
338                                 item.val.nData++;
339                                 item.val.nKey = 0;
340                                 break;
341                             }
342                             else
343                                 bBkoff = true;
344                         }
345                         if ( bBkoff )
346                             cds::backoff::yield()();
347                     }
348                 }
349             };
350
351         public:
352             size_t  m_nDeleteSuccess = 0;
353             size_t  m_nDeleteFailed = 0;
354             size_t  m_nValueSuccess = 0;
355             size_t  m_nValueFailed = 0;
356
357         public:
358             Deleter( cds_test::thread_pool& pool, Set& set )
359                 : base_class( pool, delete_thread )
360                 , m_Set( set )
361             {}
362
363             Deleter( Deleter& src )
364                 : base_class( src )
365                 , m_Set( src.m_Set )
366             {}
367
368             virtual thread * clone()
369             {
370                 return new Deleter( *this );
371             }
372
373             virtual void test()
374             {
375                 Set& rSet = m_Set;
376
377                 Set_InsDel_func& fixture = pool().template fixture<Set_InsDel_func>();
378                 size_t * pKeyFirst      = fixture.m_pKeyFirst;
379                 size_t * pKeyLast       = fixture.m_pKeyLast;
380                 size_t const nPassCount = fixture.s_nThreadPassCount;
381
382                 erase_functor   func;
383
384                 if ( id() & 1 ) {
385                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
386                         for ( size_t * p = pKeyFirst; p < pKeyLast; ++p ) {
387                             func.m_cnt.nKeyExpected = *p;
388                             if ( rSet.erase( *p, std::ref( func )))
389                                 ++m_nDeleteSuccess;
390                             else
391                                 ++m_nDeleteFailed;
392                         }
393                     }
394                 }
395                 else {
396                     for ( size_t nPass = 0; nPass < nPassCount; ++nPass ) {
397                         for ( size_t * p = pKeyLast - 1; p >= pKeyFirst; --p ) {
398                             func.m_cnt.nKeyExpected = *p;
399                             if ( rSet.erase( *p, std::ref( func )))
400                                 ++m_nDeleteSuccess;
401                             else
402                                 ++m_nDeleteFailed;
403                         }
404                     }
405                 }
406
407                 m_nValueSuccess = func.m_cnt.nSuccessItem;
408                 m_nValueFailed = func.m_cnt.nFailedItem;
409             }
410         };
411
412     protected:
413
414         template <class Set>
415         void run_test( Set& testSet )
416         {
417             typedef Inserter<Set>       InserterThread;
418             typedef Deleter<Set>        DeleterThread;
419             typedef Updater<Set>        UpdaterThread;
420
421             m_pKeyArr = new size_t[ s_nSetSize ];
422             m_pKeyFirst = m_pKeyArr;
423             m_pKeyLast = m_pKeyFirst + s_nSetSize;
424             for ( size_t i = 0; i < s_nSetSize; ++i )
425                 m_pKeyArr[i] = i;
426             shuffle( m_pKeyFirst, m_pKeyLast );
427
428             cds_test::thread_pool& pool = get_pool();
429             pool.add( new InserterThread( pool, testSet ), s_nInsertThreadCount );
430             pool.add( new DeleterThread( pool, testSet ),  s_nDeleteThreadCount );
431             pool.add( new UpdaterThread( pool, testSet ),  s_nUpdateThreadCount );
432
433             propout() << std::make_pair( "insert_thread_count", s_nInsertThreadCount )
434                 << std::make_pair( "update_thread_count", s_nUpdateThreadCount )
435                 << std::make_pair( "delete_thread_count", s_nDeleteThreadCount )
436                 << std::make_pair( "thread_pass_count", s_nThreadPassCount )
437                 << std::make_pair( "set_size", s_nSetSize );
438
439             std::chrono::milliseconds duration = pool.run();
440
441             propout() << std::make_pair( "duration", duration );
442
443             delete [] m_pKeyArr;
444
445             size_t nInsertSuccess = 0;
446             size_t nInsertFailed = 0;
447             size_t nDeleteSuccess = 0;
448             size_t nDeleteFailed = 0;
449             size_t nDelValueSuccess = 0;
450             size_t nDelValueFailed = 0;
451             size_t nUpdateFailed = 0;
452             size_t nUpdateCreated = 0;
453             size_t nUpdateModified = 0;
454             size_t nEnsFuncCreated = 0;
455             size_t nEnsFuncModified = 0;
456             size_t nTestFunctorRef = 0;
457
458             for ( size_t i = 0; i < pool.size(); ++i ) {
459                 cds_test::thread& thr = pool.get( i );
460                 switch ( thr.type() ) {
461                 case insert_thread:
462                     {
463                         InserterThread& inserter = static_cast<InserterThread&>( thr );
464                         nInsertSuccess  += inserter.m_nInsertSuccess;
465                         nInsertFailed   += inserter.m_nInsertFailed;
466                         nTestFunctorRef += inserter.m_nTestFunctorRef;
467                     }
468                     break;
469                 case update_thread:
470                     {
471                         UpdaterThread& updater = static_cast<UpdaterThread&>(thr);
472                         nUpdateCreated   += updater.m_nUpdateCreated;
473                         nUpdateModified  += updater.m_nUpdateExisted;
474                         nUpdateFailed    += updater.m_nUpdateFailed;
475                         nEnsFuncCreated  += updater.m_nFunctorCreated;
476                         nEnsFuncModified += updater.m_nFunctorModified;
477                     }
478                     break;
479                 case delete_thread:
480                     {
481                         DeleterThread& deleter = static_cast<DeleterThread&>(thr);
482                         nDeleteSuccess   += deleter.m_nDeleteSuccess;
483                         nDeleteFailed    += deleter.m_nDeleteFailed;
484                         nDelValueSuccess += deleter.m_nValueSuccess;
485                         nDelValueFailed  += deleter.m_nValueFailed;
486                     }
487                     break;
488                 }
489             }
490
491             propout()
492                 << std::make_pair( "insert_success", nInsertSuccess )
493                 << std::make_pair( "delete_success", nDeleteSuccess )
494                 << std::make_pair( "insert_failed", nInsertFailed )
495                 << std::make_pair( "delete_failed", nDeleteFailed )
496                 << std::make_pair( "update_created", nUpdateCreated )
497                 << std::make_pair( "update_modified", nUpdateModified )
498                 << std::make_pair( "update_failed", nUpdateFailed )
499                 << std::make_pair( "final_set_size", testSet.size() );
500
501
502             EXPECT_EQ( nDelValueFailed, 0 );
503             EXPECT_EQ( nDelValueSuccess, nDeleteSuccess );
504
505             EXPECT_EQ( nUpdateFailed, 0 );
506             EXPECT_EQ( nUpdateCreated, nEnsFuncCreated );
507             EXPECT_EQ( nUpdateModified, nEnsFuncModified );
508
509             // nTestFunctorRef is call count of insert functor
510             EXPECT_EQ( nTestFunctorRef, nInsertSuccess );
511
512             testSet.clear();
513             EXPECT_TRUE( testSet.empty() );
514
515             additional_check( testSet );
516             print_stat( propout(), testSet  );
517
518             additional_cleanup( testSet );
519         }
520
521         template <class Set>
522         void run_test()
523         {
524             Set s( *this );
525             run_test( s );
526         }
527     };
528
529     class Set_InsDel_func_LF: public Set_InsDel_func
530         , public ::testing::WithParamInterface<size_t>
531     {
532     public:
533         template <class Set>
534         void run_test()
535         {
536             s_nLoadFactor = GetParam();
537             propout() << std::make_pair( "load_factor", s_nLoadFactor );
538             Set_InsDel_func::run_test<Set>();
539         }
540
541         static std::vector<size_t> get_load_factors();
542     };
543
544 } // namespace set