Migrated set-insDelFind stress test to gtest framework
[libcds.git] / test / include / cds_test / thread.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 #ifndef CDSTEST_THREAD_H
32 #define CDSTEST_THREAD_H
33
34 #include <gtest/gtest.h>
35 #include <vector>
36 #include <thread>
37 #include <condition_variable>
38 #include <mutex>
39 #include <chrono>
40 #include <cds/threading/model.h>
41
42 namespace cds_test {
43
44     // Forwards
45     class thread;
46     class thread_pool;
47
48     // Test thread
49     class thread
50     {
51         void run();
52
53     protected: // thread_pool interface
54         thread( thread const& sample );
55
56         virtual ~thread()
57         {}
58
59         void join()
60         {
61             m_impl.join();
62         }
63
64     protected:
65         virtual thread * clone() = 0;
66         virtual void test() = 0;
67
68         virtual void SetUp()
69         {
70             cds::threading::Manager::attachThread();
71         }
72
73         virtual void TearDown()
74         {
75             cds::threading::Manager::detachThread();
76         }
77
78     public:
79         explicit thread( thread_pool& master, int type = 0 );
80         
81         thread_pool& pool() { return m_pool; }
82         int type() const { return m_type; }
83         size_t id() const { return m_id;  }
84         bool time_elapsed() const;
85
86     private:
87         friend class thread_pool;
88
89         thread_pool&    m_pool;
90         int             m_type;
91         size_t          m_id;
92         std::thread     m_impl;
93     };
94
95     // Pool of test threads
96     class thread_pool
97     {
98     public:
99         explicit thread_pool( ::testing::Test& fixture )
100             : m_fixture( fixture )
101             , m_bRunning( false )
102             , m_bStopped( false )
103             , m_doneCount( 0 )
104             , m_bTimeElapsed( false )
105             , m_readyCount( 0 )
106         {}
107
108         ~thread_pool()
109         {
110             clear();
111         }
112
113         void add( thread * what )
114         {
115             m_threads.push_back( what );
116         }
117
118         void add( thread * what, size_t count )
119         {
120             add( what );
121             for ( size_t i = 1; i < count; ++i ) {
122                 thread * p = what->clone();
123                 add( p );
124             }
125         }
126
127         std::chrono::milliseconds run()
128         {
129             return run( std::chrono::seconds::zero() );
130         }
131
132         std::chrono::milliseconds run( std::chrono::seconds duration )
133         {
134             m_bStopped = false;
135             m_doneCount = 0;
136
137             while ( m_readyCount.load() != m_threads.size() )
138                 std::this_thread::yield();
139
140             m_bTimeElapsed.store( false, std::memory_order_release );
141             auto time_start = std::chrono::steady_clock::now();
142
143             {
144                 scoped_lock l( m_cvMutex );
145                 m_bRunning = true;
146                 m_cvStart.notify_all();
147             }
148
149             if ( duration != std::chrono::seconds::zero() )
150                 std::this_thread::sleep_for( duration );
151             m_bTimeElapsed.store( true, std::memory_order_release );
152
153             {
154                 scoped_lock l( m_cvMutex );
155                 while ( m_doneCount != m_threads.size() )
156                     m_cvDone.wait( l );
157                 m_bStopped = true;
158             }
159             auto time_end = std::chrono::steady_clock::now();
160
161             m_cvStop.notify_all();
162
163             for ( auto t : m_threads )
164                 t->join();
165
166             return m_testDuration = std::chrono::duration_cast<std::chrono::milliseconds>(time_end - time_start);
167         }
168
169         size_t size() const             { return m_threads.size(); }
170         thread& get( size_t idx ) const { return *m_threads.at( idx ); }
171
172         template <typename Fixture>
173         Fixture& fixture()
174         {
175             return static_cast<Fixture&>(m_fixture);
176         }
177
178         std::chrono::milliseconds duration() const { return m_testDuration; }
179
180         void clear()
181         {
182             for ( auto t : m_threads )
183                 delete t;
184             m_threads.clear();
185             m_bRunning = false;
186             m_bStopped = false;
187             m_doneCount = 0;
188         }
189
190     protected: // thread interface
191         size_t get_next_id()
192         {
193             return m_threads.size();
194         }
195
196         void    ready_to_start( thread& /*who*/ )
197         {
198             // Called from test thread
199
200             // Wait for all thread created
201             scoped_lock l( m_cvMutex );
202             m_readyCount.fetch_add( 1 );
203             while ( !m_bRunning )
204                 m_cvStart.wait( l );
205         }
206
207         void    thread_done( thread& /*who*/ )
208         {
209             // Called from test thread
210
211             {
212                 scoped_lock l( m_cvMutex );
213                 ++m_doneCount;
214
215                 // Tell pool that the thread is done
216                 m_cvDone.notify_all();
217
218                 // Wait for all thread done
219                 while ( !m_bStopped )
220                     m_cvStop.wait( l );
221             }
222         }
223
224     private:
225         friend class thread;
226
227         ::testing::Test&        m_fixture;
228         std::vector<thread *>   m_threads;
229
230         typedef std::unique_lock<std::mutex> scoped_lock;
231         std::mutex              m_cvMutex;
232         std::condition_variable m_cvStart;
233         std::condition_variable m_cvStop;
234         std::condition_variable m_cvDone;
235
236         volatile bool   m_bRunning;
237         volatile bool   m_bStopped;
238         volatile size_t m_doneCount;
239         std::atomic<bool> m_bTimeElapsed;
240         std::atomic<size_t> m_readyCount;
241
242         std::chrono::milliseconds m_testDuration;
243     };
244
245     inline thread::thread( thread_pool& master, int type /*= 0*/ )
246         : m_pool( master )
247         , m_type( type )
248         , m_id( master.get_next_id())
249         , m_impl( &thread::run, this )
250     {}
251
252     inline thread::thread( thread const& sample )
253         : m_pool( sample.m_pool )
254         , m_type( sample.m_type )
255         , m_id( m_pool.get_next_id() )
256         , m_impl( &thread::run, this )
257     {}
258
259     inline void thread::run()
260     {
261         SetUp();
262         m_pool.ready_to_start( *this );
263         test();
264         m_pool.thread_done( *this );
265         TearDown();
266     }
267
268     inline bool thread::time_elapsed() const
269     {
270         return m_pool.m_bTimeElapsed.load( std::memory_order_acquire );
271     }
272
273 } // namespace cds_test
274
275 #endif // CDSTEST_THREAD_H