Test thread initial version
authorkhizmax <khizmax@gmail.com>
Wed, 20 Jan 2016 16:25:55 +0000 (19:25 +0300)
committerkhizmax <khizmax@gmail.com>
Wed, 20 Jan 2016 16:25:55 +0000 (19:25 +0300)
projects/Win/vc14/cds.sln
test/include/cds_test/thread.h [new file with mode: 0644]

index 9674e163e7c539bc36770156159a1de88b7dbf7d..f827d9ab5bd182bec5178256987fb2b22c394ce8 100644 (file)
@@ -192,6 +192,7 @@ EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cds_test", "cds_test", "{3A510E45-180B-4ADC-AFCD-D75774B68580}"\r
        ProjectSection(SolutionItems) = preProject\r
                ..\..\..\test\include\cds_test\fixture.h = ..\..\..\test\include\cds_test\fixture.h\r
+               ..\..\..\test\include\cds_test\thread.h = ..\..\..\test\include\cds_test\thread.h\r
        EndProjectSection\r
 EndProject\r
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest-deque", "gtest-deque.vcxproj", "{20A9F084-D01F-47E5-B775-4F4B48504FCC}"\r
diff --git a/test/include/cds_test/thread.h b/test/include/cds_test/thread.h
new file mode 100644 (file)
index 0000000..4f813bd
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+
+    Source code repo: http://github.com/khizmax/libcds/
+    Download: http://sourceforge.net/projects/libcds/files/
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef CDSTEST_THREAD_H
+#define CDSTEST_THREAD_H
+
+#include <gtest/gtest.h>
+#include <vector>
+#include <thread>
+#include <condition_variable>
+#include <mutex>
+#include <chrono>
+
+namespace cds_test {
+
+    // Forwards
+    class thread;
+    class thread_pool;
+
+    // Test thread
+    class thread 
+    {
+        void run();
+
+    protected: // thread_pool interface
+        thread( thread const& sample );
+
+        virtual ~thread()
+        {}
+
+        void join()         { m_impl.join(); }
+
+    protected:
+        virtual thread * clone() = 0;
+        virtual void test() = 0;
+
+        virtual void SetUp()
+        {}
+        virtual void TearDown()
+        {}
+
+    public:
+        explicit thread( thread_pool& master, int type = 0 );
+        
+        thread_pool& pool() { return m_pool; }
+        int type() const { return m_type; }
+        size_t id() const { return m_id;  }
+
+    private:
+        friend class thread_pool;
+
+        thread_pool&    m_pool;
+        int             m_type;
+        size_t          m_id;
+        std::thread     m_impl;
+    };
+
+    // Pool of test threads
+    class thread_pool
+    {
+    public:
+        explicit thread_pool( ::testing::Test& fixture )
+            : m_fixture( fixture )
+            , m_bRunning( false )
+            , m_bStopped( false )
+            , m_doneCount( 0 )
+        {}
+
+        ~thread_pool()
+        {
+            for ( auto t : m_threads )
+                delete t;
+        }
+
+        void add( thread& what )
+        {
+            m_threads.push_back( &what );
+            what.run();
+        }
+
+        void add( thread& what, size_t count )
+        {
+            add( what );
+            for ( size_t i = 1; i < count; ++i ) {
+                thread * p = what.clone();
+                add( *p );
+            }
+        }
+
+        std::chrono::milliseconds run()
+        {
+            m_bStopped = false;
+            m_doneCount = 0;
+
+            auto time_start = std::chrono::steady_clock::now();
+
+            m_bRunning = true;
+            m_cvStart.notify_all();
+
+            {
+                scoped_lock l( m_cvMutex );
+                while ( m_doneCount != m_threads.size() )
+                    m_cvDone.wait( l );
+                m_bStopped = true;
+            }
+            auto time_end = std::chrono::steady_clock::now();
+
+            m_cvStop.notify_all();
+
+            for ( auto t : m_threads )
+                t->join();
+
+            return m_testDuration = time_end - time_start;
+        }
+
+        size_t size() const             { return m_threads.size(); }
+        thread& get( size_t idx ) const { return *m_threads.at( idx ); }
+
+        template <typename Fixture>
+        Fixture& fixture()
+        {
+            return static_cast<Fixture&>(m_fixture);
+        }
+
+        std::chrono::milliseconds duration() const { return m_testDuration; }
+
+    protected: // thread interface
+        size_t get_next_id()
+        {
+            return m_threads.size();
+        }
+
+        void    ready_to_start( thread& /*who*/ )
+        {
+            // Called from test thread
+
+            // Wait for all thread created
+            scoped_lock l( m_cvMutex );
+            while ( !m_bRunning )
+                m_cvStart.wait( l );
+        }
+
+        void    thread_done( thread& /*who*/ )
+        {
+            // Called from test thread
+
+            {
+                scoped_lock l( m_cvMutex );
+                ++m_doneCount;
+            }
+
+            // Tell pool that the thread is done
+            m_cvDone.notify_all();
+            
+            // Wait for all thread done
+            {
+                scoped_lock l( m_cvMutex );
+                while ( !m_bStopped )
+                    m_cvStop.wait( l );
+            }
+        }
+
+    private:
+        friend class thread;
+
+        ::testing::Test&        m_fixture;
+        std::vector<thread *>   m_threads;
+
+        typedef std::unique_lock<std::mutex> scoped_lock;
+        std::mutex              m_cvMutex;
+        std::condition_variable m_cvStart;
+        std::condition_variable m_cvStop;
+        std::condition_variable m_cvDone;
+
+        volatile bool   m_bRunning;
+        volatile bool   m_bStopped;
+        volatile size_t m_doneCount;
+
+        std::chrono::milliseconds m_testDuration;
+    };
+
+    inline thread::thread( thread_pool& master, int type = 0 )
+        : m_pool( master )
+        , m_type( type )
+        , m_id( master.get_next_id())
+        , m_impl( &run, this )
+    {}
+
+    inline thread::thread( thread const& sample )
+        : m_pool( sample.m_pool )
+        , m_type( sample.m_type )
+        , m_id( m_pool.get_next_id() )
+        , m_impl( &run, this )
+    {}
+
+    inline void thread::run()
+    {
+        SetUp();
+        m_pool.ready_to_start( *this );
+        test();
+        m_pool.thread_done( *this );
+        TearDown();
+    }
+
+} // namespace cds_test
+
+#endif // CDSTEST_THREAD_H