Test thread initial version
[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
41 namespace cds_test {
42
43     // Forwards
44     class thread;
45     class thread_pool;
46
47     // Test thread
48     class thread 
49     {
50         void run();
51
52     protected: // thread_pool interface
53         thread( thread const& sample );
54
55         virtual ~thread()
56         {}
57
58         void join()         { m_impl.join(); }
59
60     protected:
61         virtual thread * clone() = 0;
62         virtual void test() = 0;
63
64         virtual void SetUp()
65         {}
66         virtual void TearDown()
67         {}
68
69     public:
70         explicit thread( thread_pool& master, int type = 0 );
71         
72         thread_pool& pool() { return m_pool; }
73         int type() const { return m_type; }
74         size_t id() const { return m_id;  }
75
76     private:
77         friend class thread_pool;
78
79         thread_pool&    m_pool;
80         int             m_type;
81         size_t          m_id;
82         std::thread     m_impl;
83     };
84
85     // Pool of test threads
86     class thread_pool
87     {
88     public:
89         explicit thread_pool( ::testing::Test& fixture )
90             : m_fixture( fixture )
91             , m_bRunning( false )
92             , m_bStopped( false )
93             , m_doneCount( 0 )
94         {}
95
96         ~thread_pool()
97         {
98             for ( auto t : m_threads )
99                 delete t;
100         }
101
102         void add( thread& what )
103         {
104             m_threads.push_back( &what );
105             what.run();
106         }
107
108         void add( thread& what, size_t count )
109         {
110             add( what );
111             for ( size_t i = 1; i < count; ++i ) {
112                 thread * p = what.clone();
113                 add( *p );
114             }
115         }
116
117         std::chrono::milliseconds run()
118         {
119             m_bStopped = false;
120             m_doneCount = 0;
121
122             auto time_start = std::chrono::steady_clock::now();
123
124             m_bRunning = true;
125             m_cvStart.notify_all();
126
127             {
128                 scoped_lock l( m_cvMutex );
129                 while ( m_doneCount != m_threads.size() )
130                     m_cvDone.wait( l );
131                 m_bStopped = true;
132             }
133             auto time_end = std::chrono::steady_clock::now();
134
135             m_cvStop.notify_all();
136
137             for ( auto t : m_threads )
138                 t->join();
139
140             return m_testDuration = time_end - time_start;
141         }
142
143         size_t size() const             { return m_threads.size(); }
144         thread& get( size_t idx ) const { return *m_threads.at( idx ); }
145
146         template <typename Fixture>
147         Fixture& fixture()
148         {
149             return static_cast<Fixture&>(m_fixture);
150         }
151
152         std::chrono::milliseconds duration() const { return m_testDuration; }
153
154     protected: // thread interface
155         size_t get_next_id()
156         {
157             return m_threads.size();
158         }
159
160         void    ready_to_start( thread& /*who*/ )
161         {
162             // Called from test thread
163
164             // Wait for all thread created
165             scoped_lock l( m_cvMutex );
166             while ( !m_bRunning )
167                 m_cvStart.wait( l );
168         }
169
170         void    thread_done( thread& /*who*/ )
171         {
172             // Called from test thread
173
174             {
175                 scoped_lock l( m_cvMutex );
176                 ++m_doneCount;
177             }
178
179             // Tell pool that the thread is done
180             m_cvDone.notify_all();
181             
182             // Wait for all thread done
183             {
184                 scoped_lock l( m_cvMutex );
185                 while ( !m_bStopped )
186                     m_cvStop.wait( l );
187             }
188         }
189
190     private:
191         friend class thread;
192
193         ::testing::Test&        m_fixture;
194         std::vector<thread *>   m_threads;
195
196         typedef std::unique_lock<std::mutex> scoped_lock;
197         std::mutex              m_cvMutex;
198         std::condition_variable m_cvStart;
199         std::condition_variable m_cvStop;
200         std::condition_variable m_cvDone;
201
202         volatile bool   m_bRunning;
203         volatile bool   m_bStopped;
204         volatile size_t m_doneCount;
205
206         std::chrono::milliseconds m_testDuration;
207     };
208
209     inline thread::thread( thread_pool& master, int type = 0 )
210         : m_pool( master )
211         , m_type( type )
212         , m_id( master.get_next_id())
213         , m_impl( &run, this )
214     {}
215
216     inline thread::thread( thread const& sample )
217         : m_pool( sample.m_pool )
218         , m_type( sample.m_type )
219         , m_id( m_pool.get_next_id() )
220         , m_impl( &run, this )
221     {}
222
223     inline void thread::run()
224     {
225         SetUp();
226         m_pool.ready_to_start( *this );
227         test();
228         m_pool.thread_done( *this );
229         TearDown();
230     }
231
232 } // namespace cds_test
233
234 #endif // CDSTEST_THREAD_H