Removed trailing spaces
[libcds.git] / test / stress / stack / push.cpp
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 "stack_type.h"
32
33 namespace {
34
35     static size_t s_nThreadCount = 8;
36     static size_t s_nStackSize = 10000000;
37     static size_t s_nEliminationSize = 4;
38
39     class stack_push : public cds_test::stress_fixture
40     {
41     protected:
42
43         struct value_type {
44             size_t      nNo;
45             size_t      nThread;
46
47             value_type()
48                 : nNo( 0 )
49                 , nThread( 0 )
50             {}
51             value_type( size_t n )
52                 : nNo( n )
53                 , nThread( 0 )
54             {}
55         };
56
57         template <class Stack>
58         class Producer: public cds_test::thread
59         {
60             typedef cds_test::thread base_class;
61
62         public:
63             Producer( cds_test::thread_pool& pool, Stack& stack )
64                 : base_class( pool )
65                 , m_stack( stack )
66                 , m_nStartItem( 0 )
67                 , m_nEndItem( 0 )
68                 , m_nPushError( 0 )
69             {}
70
71             Producer( Producer& src )
72                 : base_class( src )
73                 , m_stack( src.m_stack )
74                 , m_nStartItem( 0 )
75                 , m_nEndItem( 0 )
76                 , m_nPushError( 0 )
77             {}
78
79             virtual thread * clone()
80             {
81                 return new Producer( *this );
82             }
83
84             virtual void test()
85             {
86                 value_type v;
87                 v.nThread = id();
88                 for ( v.nNo = m_nStartItem; v.nNo < m_nEndItem; ++v.nNo ) {
89                     if ( !m_stack.push( v ) )
90                         ++m_nPushError;
91                 }
92             }
93
94         public:
95             Stack&  m_stack;
96             size_t  m_nStartItem;
97             size_t  m_nEndItem;
98             size_t  m_nPushError;
99         };
100
101     protected:
102         static void SetUpTestCase()
103         {
104             cds_test::config const& cfg = get_config("Stack_Push");
105
106             s_nThreadCount     = cfg.get_size_t( "ThreadCount",     s_nThreadCount );
107             s_nStackSize       = cfg.get_size_t( "StackSize",       s_nStackSize );
108             s_nEliminationSize = cfg.get_size_t( "EliminationSize", s_nEliminationSize );
109
110             if ( s_nThreadCount == 0 )
111                 s_nThreadCount = 1;
112         }
113
114         //static void TearDownTestCase();
115
116         template <typename Stack>
117         void test( Stack& stack )
118         {
119             cds_test::thread_pool& pool = get_pool();
120
121             pool.add( new Producer<Stack>( pool, stack ), s_nThreadCount );
122
123             size_t nStart = 0;
124             size_t nThreadItemCount = s_nStackSize / s_nThreadCount;
125             for ( size_t i = 0; i < pool.size(); ++i ) {
126                 Producer<Stack>& thread = static_cast<Producer<Stack>&>( pool.get( i ));
127                 thread.m_nStartItem = nStart;
128                 nStart += nThreadItemCount;
129                 thread.m_nEndItem = nStart;
130             }
131
132             propout() << std::make_pair( "thread_count", s_nThreadCount )
133                 << std::make_pair( "push_count", s_nStackSize );
134
135             std::chrono::milliseconds duration = pool.run();
136
137             propout() << std::make_pair( "duration", duration );
138
139             analyze( stack );
140
141             propout() << stack.statistics();
142         }
143
144         template <typename Stack>
145         void test_elimination( Stack& stack )
146         {
147             test( stack );
148             check_elimination_stat( stack.statistics() );
149         }
150
151         void check_elimination_stat( cds::container::treiber_stack::empty_stat const& )
152         {}
153
154         void check_elimination_stat( cds::container::treiber_stack::stat<> const& s )
155         {
156             EXPECT_EQ( s.m_PushCount.get(), s.m_PopCount.get() );
157         }
158
159         template <class Stack>
160         void analyze( Stack& testStack )
161         {
162             cds_test::thread_pool& pool = get_pool();
163
164             size_t nThreadItems = s_nStackSize / s_nThreadCount;
165             std::vector<size_t> aThread;
166             aThread.resize( s_nThreadCount );
167
168             for ( size_t i = 0; i < pool.size(); ++i ) {
169                 Producer<Stack>& producer = static_cast<Producer<Stack>&>( pool.get( i ));
170                 EXPECT_EQ( producer.m_nPushError, 0 ) << "Producer=" << i;
171                 aThread[producer.id()] = producer.m_nEndItem - 1;
172             }
173             EXPECT_FALSE( testStack.empty());
174
175             std::unique_ptr< uint8_t[] > uarr( new uint8_t[s_nStackSize] );
176             uint8_t * arr = uarr.get();
177             memset( arr, 0, sizeof( arr[0] ) * s_nStackSize );
178
179             auto time_start = std::chrono::steady_clock::now();
180             size_t nPopped = 0;
181             value_type val;
182             while ( testStack.pop( val )) {
183                 nPopped++;
184                 ASSERT_LT( val.nNo, s_nStackSize );
185                 ++arr[val.nNo];
186                 ASSERT_LT( val.nThread, s_nThreadCount );
187                 ASSERT_EQ( aThread[val.nThread], val.nNo );
188                 aThread[val.nThread]--;
189             }
190             propout() << std::make_pair( "pop_duration", std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - time_start));
191
192             size_t nTotalItems = nThreadItems * s_nThreadCount;
193             size_t nError = 0;
194             for ( size_t i = 0; i < nTotalItems; ++i ) {
195                 EXPECT_EQ( arr[i], 1 ) << "i=" << i;
196                 if ( ++nError > 10 )
197                     ASSERT_EQ( arr[i], 1 );
198             }
199         }
200     };
201
202     CDSSTRESS_TreiberStack( stack_push )
203     CDSSTRESS_EliminationStack( stack_push )
204     CDSSTRESS_FCStack( stack_push )
205     CDSSTRESS_FCDeque( stack_push )
206     CDSSTRESS_StdStack( stack_push )
207
208 } // namespace