Migrated priority queue 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
85     private:
86         friend class thread_pool;
87
88         thread_pool&    m_pool;
89         int             m_type;
90         size_t          m_id;
91         std::thread     m_impl;
92     };
93
94     // Pool of test threads
95     class thread_pool
96     {
97     public:
98         explicit thread_pool( ::testing::Test& fixture )
99             : m_fixture( fixture )
100             , m_bRunning( false )
101             , m_bStopped( false )
102             , m_doneCount( 0 )
103         {}
104
105         ~thread_pool()
106         {
107             clear();
108         }
109
110         void add( thread * what )
111         {
112             m_threads.push_back( what );
113         }
114
115         void add( thread * what, size_t count )
116         {
117             add( what );
118             for ( size_t i = 1; i < count; ++i ) {
119                 thread * p = what->clone();
120                 add( p );
121             }
122         }
123
124         std::chrono::milliseconds run()
125         {
126             m_bStopped = false;
127             m_doneCount = 0;
128
129             auto time_start = std::chrono::steady_clock::now();
130
131             m_bRunning = true;
132             m_cvStart.notify_all();
133
134             {
135                 scoped_lock l( m_cvMutex );
136                 while ( m_doneCount != m_threads.size() )
137                     m_cvDone.wait( l );
138                 m_bStopped = true;
139             }
140             auto time_end = std::chrono::steady_clock::now();
141
142             m_cvStop.notify_all();
143
144             for ( auto t : m_threads )
145                 t->join();
146
147             return m_testDuration = std::chrono::duration_cast<std::chrono::milliseconds>( time_end - time_start );
148         }
149
150         size_t size() const             { return m_threads.size(); }
151         thread& get( size_t idx ) const { return *m_threads.at( idx ); }
152
153         template <typename Fixture>
154         Fixture& fixture()
155         {
156             return static_cast<Fixture&>(m_fixture);
157         }
158
159         std::chrono::milliseconds duration() const { return m_testDuration; }
160
161         void clear()
162         {
163             for ( auto t : m_threads )
164                 delete t;
165             m_threads.clear();
166             m_bRunning = false;
167             m_bStopped = false;
168             m_doneCount = 0;
169         }
170
171     protected: // thread interface
172         size_t get_next_id()
173         {
174             return m_threads.size();
175         }
176
177         void    ready_to_start( thread& /*who*/ )
178         {
179             // Called from test thread
180
181             // Wait for all thread created
182             scoped_lock l( m_cvMutex );
183             while ( !m_bRunning )
184                 m_cvStart.wait( l );
185         }
186
187         void    thread_done( thread& /*who*/ )
188         {
189             // Called from test thread
190
191             {
192                 scoped_lock l( m_cvMutex );
193                 ++m_doneCount;
194             }
195
196             // Tell pool that the thread is done
197             m_cvDone.notify_all();
198             
199             // Wait for all thread done
200             {
201                 scoped_lock l( m_cvMutex );
202                 while ( !m_bStopped )
203                     m_cvStop.wait( l );
204             }
205         }
206
207     private:
208         friend class thread;
209
210         ::testing::Test&        m_fixture;
211         std::vector<thread *>   m_threads;
212
213         typedef std::unique_lock<std::mutex> scoped_lock;
214         std::mutex              m_cvMutex;
215         std::condition_variable m_cvStart;
216         std::condition_variable m_cvStop;
217         std::condition_variable m_cvDone;
218
219         volatile bool   m_bRunning;
220         volatile bool   m_bStopped;
221         volatile size_t m_doneCount;
222
223         std::chrono::milliseconds m_testDuration;
224     };
225
226     inline thread::thread( thread_pool& master, int type /*= 0*/ )
227         : m_pool( master )
228         , m_type( type )
229         , m_id( master.get_next_id())
230         , m_impl( &thread::run, this )
231     {}
232
233     inline thread::thread( thread const& sample )
234         : m_pool( sample.m_pool )
235         , m_type( sample.m_type )
236         , m_id( m_pool.get_next_id() )
237         , m_impl( &thread::run, this )
238     {}
239
240     inline void thread::run()
241     {
242         SetUp();
243         m_pool.ready_to_start( *this );
244         test();
245         m_pool.thread_done( *this );
246         TearDown();
247     }
248
249 } // namespace cds_test
250
251 #endif // CDSTEST_THREAD_H