/////////////////////////////////////////////////////////////////////////////// // Name: tests/thread/atomic.cpp // Purpose: wxAtomic??? unit test // Author: Armel Asselin // Created: 2006-12-14 // Copyright: (c) 2006 Armel Asselin /////////////////////////////////////////////////////////////////////////////// // ---------------------------------------------------------------------------- // headers // ---------------------------------------------------------------------------- #include "testprec.h" #ifndef WX_PRECOMP #endif // WX_PRECOMP #include "wx/atomic.h" #include "wx/thread.h" #include "wx/dynarray.h" #include "wx/log.h" WX_DEFINE_ARRAY_PTR(wxThread *, wxArrayThread); // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- // number of times to run the loops: the code takes too long to run if we use // the bigger value with generic atomic operations implementation #ifdef wxHAS_ATOMIC_OPS static const wxInt32 ITERATIONS_NUM = 10000000; #else static const wxInt32 ITERATIONS_NUM = 1000; #endif // ---------------------------------------------------------------------------- // test class // ---------------------------------------------------------------------------- class AtomicTestCase : public CppUnit::TestCase { public: AtomicTestCase() { } enum ETestType { IncAndDecMixed, IncOnly, DecOnly }; private: class MyThread : public wxThread { public: MyThread(wxAtomicInt &operateOn, ETestType testType) : wxThread(wxTHREAD_JOINABLE), m_operateOn(operateOn), m_testType(testType) {} // thread execution starts here virtual void *Entry() wxOVERRIDE; public: wxAtomicInt &m_operateOn; ETestType m_testType; }; CPPUNIT_TEST_SUITE( AtomicTestCase ); CPPUNIT_TEST( TestNoThread ); CPPUNIT_TEST( TestDecReturn ); CPPUNIT_TEST( TestTwoThreadsMix ); CPPUNIT_TEST( TestTenThreadsMix ); CPPUNIT_TEST( TestTwoThreadsSeparate ); CPPUNIT_TEST( TestTenThreadsSeparate ); CPPUNIT_TEST_SUITE_END(); void TestNoThread(); void TestDecReturn(); void TestTenThreadsMix() { TestWithThreads(10, IncAndDecMixed); } void TestTwoThreadsMix() { TestWithThreads(2, IncAndDecMixed); } void TestTenThreadsSeparate() { TestWithThreads(10, IncOnly); } void TestTwoThreadsSeparate() { TestWithThreads(2, IncOnly); } void TestWithThreads(int count, ETestType testtype); wxDECLARE_NO_COPY_CLASS(AtomicTestCase); }; // register in the unnamed registry so that these tests are run by default CPPUNIT_TEST_SUITE_REGISTRATION( AtomicTestCase ); // also include in its own registry so that these tests can be run alone CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( AtomicTestCase, "AtomicTestCase" ); void AtomicTestCase::TestNoThread() { wxAtomicInt int1 = 0, int2 = 0; for ( wxInt32 i = 0; i < ITERATIONS_NUM; ++i ) { wxAtomicInc(int1); wxAtomicDec(int2); } CPPUNIT_ASSERT( int1 == ITERATIONS_NUM ); CPPUNIT_ASSERT( int2 == -ITERATIONS_NUM ); } void AtomicTestCase::TestDecReturn() { wxAtomicInt i(0); wxAtomicInc(i); wxAtomicInc(i); CPPUNIT_ASSERT( i == 2 ); CPPUNIT_ASSERT( wxAtomicDec(i) > 0 ); CPPUNIT_ASSERT( wxAtomicDec(i) == 0 ); } void AtomicTestCase::TestWithThreads(int count, ETestType testType) { wxAtomicInt int1=0; wxArrayThread threads; int i; for ( i = 0; i < count; ++i ) { ETestType actualThreadType; switch(testType) { default: actualThreadType = testType; break; case IncOnly: actualThreadType = (i&1)==0 ? IncOnly : DecOnly; break; } MyThread *thread = new MyThread(int1, actualThreadType); if ( thread->Create() != wxTHREAD_NO_ERROR ) { wxLogError(wxT("Can't create thread!")); delete thread; } else threads.Add(thread); } for ( i = 0; i < count; ++i ) { threads[i]->Run(); } for ( i = 0; i < count; ++i ) { // each thread should return 0, else it detected some problem CPPUNIT_ASSERT (threads[i]->Wait() == (wxThread::ExitCode)0); delete threads[i]; } CPPUNIT_ASSERT( int1 == 0 ); } // ---------------------------------------------------------------------------- void *AtomicTestCase::MyThread::Entry() { wxInt32 negativeValuesSeen = 0; for ( wxInt32 i = 0; i < ITERATIONS_NUM; ++i ) { switch ( m_testType ) { case AtomicTestCase::IncAndDecMixed: wxAtomicInc(m_operateOn); wxAtomicDec(m_operateOn); if (m_operateOn < 0) ++negativeValuesSeen; break; case AtomicTestCase::IncOnly: wxAtomicInc(m_operateOn); break; case AtomicTestCase::DecOnly: wxAtomicDec(m_operateOn); break; } } return wxUIntToPtr(negativeValuesSeen); }