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