From 1f5496a0f535d087de2fa0538b0b3cd2cb45e05f Mon Sep 17 00:00:00 2001 From: Francesco Montorsi Date: Mon, 10 May 2010 21:48:24 +0000 Subject: [PATCH] move code testing wxThread classes from the console sample to a new CppUnit test git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@64283 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- samples/console/console.cpp | 416 ------------------------------------ tests/Makefile.in | 4 + tests/makefile.bcc | 4 + tests/makefile.gcc | 4 + tests/makefile.vc | 4 + tests/makefile.wat | 4 + tests/test.bkl | 1 + tests/test_test.dsp | 4 + tests/test_vc7_test.vcproj | 3 + tests/test_vc8_test.vcproj | 4 + tests/test_vc9_test.vcproj | 46 ++-- tests/thread/misc.cpp | 407 +++++++++++++++++++++++++++++++++++ 12 files changed, 459 insertions(+), 442 deletions(-) create mode 100644 tests/thread/misc.cpp diff --git a/samples/console/console.cpp b/samples/console/console.cpp index d043138193..0052fd7b68 100644 --- a/samples/console/console.cpp +++ b/samples/console/console.cpp @@ -132,7 +132,6 @@ #define TEST_STDPATHS #define TEST_STREAMS #define TEST_TEXTSTREAM - #define TEST_THREADS #define TEST_TIMER // #define TEST_VOLUME --FIXME! (RN) #define TEST_WCHAR @@ -3486,403 +3485,6 @@ static void TestTextInputStream() #endif // TEST_TEXTSTREAM -// ---------------------------------------------------------------------------- -// threads -// ---------------------------------------------------------------------------- - -#ifdef TEST_THREADS - -#include "wx/thread.h" - -static size_t gs_counter = (size_t)-1; -static wxCriticalSection gs_critsect; -static wxSemaphore gs_cond; - -class MyJoinableThread : public wxThread -{ -public: - MyJoinableThread(size_t n) : wxThread(wxTHREAD_JOINABLE) - { m_n = n; Create(); } - - // thread execution starts here - virtual ExitCode Entry(); - -private: - size_t m_n; -}; - -wxThread::ExitCode MyJoinableThread::Entry() -{ - unsigned long res = 1; - for ( size_t n = 1; n < m_n; n++ ) - { - res *= n; - - // it's a loooong calculation :-) - Sleep(100); - } - - return (ExitCode)res; -} - -class MyDetachedThread : public wxThread -{ -public: - MyDetachedThread(size_t n, wxChar ch) - { - m_n = n; - m_ch = ch; - m_cancelled = false; - - Create(); - } - - // thread execution starts here - virtual ExitCode Entry(); - - // and stops here - virtual void OnExit(); - -private: - size_t m_n; // number of characters to write - wxChar m_ch; // character to write - - bool m_cancelled; // false if we exit normally -}; - -wxThread::ExitCode MyDetachedThread::Entry() -{ - { - wxCriticalSectionLocker lock(gs_critsect); - if ( gs_counter == (size_t)-1 ) - gs_counter = 1; - else - gs_counter++; - } - - for ( size_t n = 0; n < m_n; n++ ) - { - if ( TestDestroy() ) - { - m_cancelled = true; - - break; - } - - wxPutchar(m_ch); - fflush(stdout); - - wxThread::Sleep(100); - } - - return 0; -} - -void MyDetachedThread::OnExit() -{ - wxLogTrace(wxT("thread"), wxT("Thread %ld is in OnExit"), GetId()); - - wxCriticalSectionLocker lock(gs_critsect); - if ( !--gs_counter && !m_cancelled ) - gs_cond.Post(); -} - -static void TestDetachedThreads() -{ - wxPuts(wxT("\n*** Testing detached threads ***")); - - static const size_t nThreads = 3; - MyDetachedThread *threads[nThreads]; - size_t n; - for ( n = 0; n < nThreads; n++ ) - { - threads[n] = new MyDetachedThread(10, 'A' + n); - } - - threads[0]->SetPriority(WXTHREAD_MIN_PRIORITY); - threads[1]->SetPriority(WXTHREAD_MAX_PRIORITY); - - for ( n = 0; n < nThreads; n++ ) - { - threads[n]->Run(); - } - - // wait until all threads terminate - gs_cond.Wait(); - - wxPuts(wxEmptyString); -} - -static void TestJoinableThreads() -{ - wxPuts(wxT("\n*** Testing a joinable thread (a loooong calculation...) ***")); - - // calc 10! in the background - MyJoinableThread thread(10); - thread.Run(); - - wxPrintf(wxT("\nThread terminated with exit code %lu.\n"), - (unsigned long)thread.Wait()); -} - -static void TestThreadSuspend() -{ - wxPuts(wxT("\n*** Testing thread suspend/resume functions ***")); - - MyDetachedThread *thread = new MyDetachedThread(15, 'X'); - - thread->Run(); - - // this is for this demo only, in a real life program we'd use another - // condition variable which would be signaled from wxThread::Entry() to - // tell us that the thread really started running - but here just wait a - // bit and hope that it will be enough (the problem is, of course, that - // the thread might still not run when we call Pause() which will result - // in an error) - wxThread::Sleep(300); - - for ( size_t n = 0; n < 3; n++ ) - { - thread->Pause(); - - wxPuts(wxT("\nThread suspended")); - if ( n > 0 ) - { - // don't sleep but resume immediately the first time - wxThread::Sleep(300); - } - wxPuts(wxT("Going to resume the thread")); - - thread->Resume(); - } - - wxPuts(wxT("Waiting until it terminates now")); - - // wait until the thread terminates - gs_cond.Wait(); - - wxPuts(wxEmptyString); -} - -static void TestThreadDelete() -{ - // As above, using Sleep() is only for testing here - we must use some - // synchronisation object instead to ensure that the thread is still - // running when we delete it - deleting a detached thread which already - // terminated will lead to a crash! - - wxPuts(wxT("\n*** Testing thread delete function ***")); - - MyDetachedThread *thread0 = new MyDetachedThread(30, 'W'); - - thread0->Delete(); - - wxPuts(wxT("\nDeleted a thread which didn't start to run yet.")); - - MyDetachedThread *thread1 = new MyDetachedThread(30, 'Y'); - - thread1->Run(); - - wxThread::Sleep(300); - - thread1->Delete(); - - wxPuts(wxT("\nDeleted a running thread.")); - - MyDetachedThread *thread2 = new MyDetachedThread(30, 'Z'); - - thread2->Run(); - - wxThread::Sleep(300); - - thread2->Pause(); - - thread2->Delete(); - - wxPuts(wxT("\nDeleted a sleeping thread.")); - - MyJoinableThread thread3(20); - thread3.Run(); - - thread3.Delete(); - - wxPuts(wxT("\nDeleted a joinable thread.")); - - MyJoinableThread thread4(2); - thread4.Run(); - - wxThread::Sleep(300); - - thread4.Delete(); - - wxPuts(wxT("\nDeleted a joinable thread which already terminated.")); - - wxPuts(wxEmptyString); -} - -class MyWaitingThread : public wxThread -{ -public: - MyWaitingThread( wxMutex *mutex, wxCondition *condition ) - { - m_mutex = mutex; - m_condition = condition; - - Create(); - } - - virtual ExitCode Entry() - { - wxPrintf(wxT("Thread %lu has started running.\n"), GetId()); - fflush(stdout); - - gs_cond.Post(); - - wxPrintf(wxT("Thread %lu starts to wait...\n"), GetId()); - fflush(stdout); - - m_mutex->Lock(); - m_condition->Wait(); - m_mutex->Unlock(); - - wxPrintf(wxT("Thread %lu finished to wait, exiting.\n"), GetId()); - fflush(stdout); - - return 0; - } - -private: - wxMutex *m_mutex; - wxCondition *m_condition; -}; - -static void TestThreadConditions() -{ - wxMutex mutex; - wxCondition condition(mutex); - - // otherwise its difficult to understand which log messages pertain to - // which condition - //wxLogTrace(wxT("thread"), wxT("Local condition var is %08x, gs_cond = %08x"), - // condition.GetId(), gs_cond.GetId()); - - // create and launch threads - MyWaitingThread *threads[10]; - - size_t n; - for ( n = 0; n < WXSIZEOF(threads); n++ ) - { - threads[n] = new MyWaitingThread( &mutex, &condition ); - } - - for ( n = 0; n < WXSIZEOF(threads); n++ ) - { - threads[n]->Run(); - } - - // wait until all threads run - wxPuts(wxT("Main thread is waiting for the other threads to start")); - fflush(stdout); - - size_t nRunning = 0; - while ( nRunning < WXSIZEOF(threads) ) - { - gs_cond.Wait(); - - nRunning++; - - wxPrintf(wxT("Main thread: %u already running\n"), nRunning); - fflush(stdout); - } - - wxPuts(wxT("Main thread: all threads started up.")); - fflush(stdout); - - wxThread::Sleep(500); - -#if 1 - // now wake one of them up - wxPrintf(wxT("Main thread: about to signal the condition.\n")); - fflush(stdout); - condition.Signal(); -#endif - - wxThread::Sleep(200); - - // wake all the (remaining) threads up, so that they can exit - wxPrintf(wxT("Main thread: about to broadcast the condition.\n")); - fflush(stdout); - condition.Broadcast(); - - // give them time to terminate (dirty!) - wxThread::Sleep(500); -} - -// semaphore tests -#include "wx/datetime.h" - -class MySemaphoreThread : public wxThread -{ -public: - MySemaphoreThread(int i, wxSemaphore *sem) - : wxThread(wxTHREAD_JOINABLE), - m_sem(sem), - m_i(i) - { - Create(); - } - - virtual ExitCode Entry() - { - wxPrintf(wxT("%s: Thread #%d (%ld) starting to wait for semaphore...\n"), - wxDateTime::Now().FormatTime().c_str(), m_i, (long)GetId()); - - m_sem->Wait(); - - wxPrintf(wxT("%s: Thread #%d (%ld) acquired the semaphore.\n"), - wxDateTime::Now().FormatTime().c_str(), m_i, (long)GetId()); - - Sleep(1000); - - wxPrintf(wxT("%s: Thread #%d (%ld) releasing the semaphore.\n"), - wxDateTime::Now().FormatTime().c_str(), m_i, (long)GetId()); - - m_sem->Post(); - - return 0; - } - -private: - wxSemaphore *m_sem; - int m_i; -}; - -WX_DEFINE_ARRAY_PTR(wxThread *, ArrayThreads); - -static void TestSemaphore() -{ - wxPuts(wxT("*** Testing wxSemaphore class. ***")); - - static const int SEM_LIMIT = 3; - - wxSemaphore sem(SEM_LIMIT, SEM_LIMIT); - ArrayThreads threads; - - for ( int i = 0; i < 3*SEM_LIMIT; i++ ) - { - threads.Add(new MySemaphoreThread(i, &sem)); - threads.Last()->Run(); - } - - for ( size_t n = 0; n < threads.GetCount(); n++ ) - { - threads[n]->Wait(); - delete threads[n]; - } -} - -#endif // TEST_THREADS - // ---------------------------------------------------------------------------- // entry point // ---------------------------------------------------------------------------- @@ -4150,24 +3752,6 @@ int main(int argc, char **argv) TestTextInputStream(); #endif // TEST_TEXTSTREAM -#ifdef TEST_THREADS - int nCPUs = wxThread::GetCPUCount(); - wxPrintf(wxT("This system has %d CPUs\n"), nCPUs); - if ( nCPUs != -1 ) - wxThread::SetConcurrency(nCPUs); - - TestJoinableThreads(); - - #if TEST_ALL - TestJoinableThreads(); - TestDetachedThreads(); - TestThreadSuspend(); - TestThreadDelete(); - TestThreadConditions(); - TestSemaphore(); - #endif -#endif // TEST_THREADS - #ifdef TEST_TIMER TestStopWatch(); TestTimer(); diff --git a/tests/Makefile.in b/tests/Makefile.in index 4af8fdf6e8..df5ab92c41 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -113,6 +113,7 @@ TEST_OBJECTS = \ test_zlibstream.o \ test_textfiletest.o \ test_atomic.o \ + test_misc.o \ test_queue.o \ test_tls.o \ test_uris.o \ @@ -532,6 +533,9 @@ test_textfiletest.o: $(srcdir)/textfile/textfiletest.cpp $(TEST_ODEP) test_atomic.o: $(srcdir)/thread/atomic.cpp $(TEST_ODEP) $(CXXC) -c -o $@ $(TEST_CXXFLAGS) $(srcdir)/thread/atomic.cpp +test_misc.o: $(srcdir)/thread/misc.cpp $(TEST_ODEP) + $(CXXC) -c -o $@ $(TEST_CXXFLAGS) $(srcdir)/thread/misc.cpp + test_queue.o: $(srcdir)/thread/queue.cpp $(TEST_ODEP) $(CXXC) -c -o $@ $(TEST_CXXFLAGS) $(srcdir)/thread/queue.cpp diff --git a/tests/makefile.bcc b/tests/makefile.bcc index 40ba936ab3..bbad494156 100644 --- a/tests/makefile.bcc +++ b/tests/makefile.bcc @@ -97,6 +97,7 @@ TEST_OBJECTS = \ $(OBJS)\test_zlibstream.obj \ $(OBJS)\test_textfiletest.obj \ $(OBJS)\test_atomic.obj \ + $(OBJS)\test_misc.obj \ $(OBJS)\test_queue.obj \ $(OBJS)\test_tls.obj \ $(OBJS)\test_uris.obj \ @@ -574,6 +575,9 @@ $(OBJS)\test_textfiletest.obj: .\textfile\textfiletest.cpp $(OBJS)\test_atomic.obj: .\thread\atomic.cpp $(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\thread\atomic.cpp +$(OBJS)\test_misc.obj: .\thread\misc.cpp + $(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\thread\misc.cpp + $(OBJS)\test_queue.obj: .\thread\queue.cpp $(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\thread\queue.cpp diff --git a/tests/makefile.gcc b/tests/makefile.gcc index 972889d68b..b250c6377b 100644 --- a/tests/makefile.gcc +++ b/tests/makefile.gcc @@ -89,6 +89,7 @@ TEST_OBJECTS = \ $(OBJS)\test_zlibstream.o \ $(OBJS)\test_textfiletest.o \ $(OBJS)\test_atomic.o \ + $(OBJS)\test_misc.o \ $(OBJS)\test_queue.o \ $(OBJS)\test_tls.o \ $(OBJS)\test_uris.o \ @@ -555,6 +556,9 @@ $(OBJS)\test_textfiletest.o: ./textfile/textfiletest.cpp $(OBJS)\test_atomic.o: ./thread/atomic.cpp $(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $< +$(OBJS)\test_misc.o: ./thread/misc.cpp + $(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $< + $(OBJS)\test_queue.o: ./thread/queue.cpp $(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $< diff --git a/tests/makefile.vc b/tests/makefile.vc index 1f005bf184..237822026f 100644 --- a/tests/makefile.vc +++ b/tests/makefile.vc @@ -91,6 +91,7 @@ TEST_OBJECTS = \ $(OBJS)\test_zlibstream.obj \ $(OBJS)\test_textfiletest.obj \ $(OBJS)\test_atomic.obj \ + $(OBJS)\test_misc.obj \ $(OBJS)\test_queue.obj \ $(OBJS)\test_tls.obj \ $(OBJS)\test_uris.obj \ @@ -700,6 +701,9 @@ $(OBJS)\test_textfiletest.obj: .\textfile\textfiletest.cpp $(OBJS)\test_atomic.obj: .\thread\atomic.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\thread\atomic.cpp +$(OBJS)\test_misc.obj: .\thread\misc.cpp + $(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\thread\misc.cpp + $(OBJS)\test_queue.obj: .\thread\queue.cpp $(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\thread\queue.cpp diff --git a/tests/makefile.wat b/tests/makefile.wat index d7db26faec..7fca6b4ccb 100644 --- a/tests/makefile.wat +++ b/tests/makefile.wat @@ -327,6 +327,7 @@ TEST_OBJECTS = & $(OBJS)\test_zlibstream.obj & $(OBJS)\test_textfiletest.obj & $(OBJS)\test_atomic.obj & + $(OBJS)\test_misc.obj & $(OBJS)\test_queue.obj & $(OBJS)\test_tls.obj & $(OBJS)\test_uris.obj & @@ -612,6 +613,9 @@ $(OBJS)\test_textfiletest.obj : .AUTODEPEND .\textfile\textfiletest.cpp $(OBJS)\test_atomic.obj : .AUTODEPEND .\thread\atomic.cpp $(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $< +$(OBJS)\test_misc.obj : .AUTODEPEND .\thread\misc.cpp + $(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $< + $(OBJS)\test_queue.obj : .AUTODEPEND .\thread\queue.cpp $(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $< diff --git a/tests/test.bkl b/tests/test.bkl index eac3d28888..629d6049e8 100644 --- a/tests/test.bkl +++ b/tests/test.bkl @@ -88,6 +88,7 @@ streams/zlibstream.cpp textfile/textfiletest.cpp thread/atomic.cpp + thread/misc.cpp thread/queue.cpp thread/tls.cpp uris/uris.cpp diff --git a/tests/test_test.dsp b/tests/test_test.dsp index 9f353988e3..271690fa8b 100644 --- a/tests/test_test.dsp +++ b/tests/test_test.dsp @@ -389,6 +389,10 @@ SOURCE=.\streams\memstream.cpp # End Source File # Begin Source File +SOURCE=.\thread\misc.cpp +# End Source File +# Begin Source File + SOURCE=.\misc\misctests.cpp # End Source File # Begin Source File diff --git a/tests/test_vc7_test.vcproj b/tests/test_vc7_test.vcproj index 12dc231b82..874d981a35 100644 --- a/tests/test_vc7_test.vcproj +++ b/tests/test_vc7_test.vcproj @@ -727,6 +727,9 @@ + + diff --git a/tests/test_vc8_test.vcproj b/tests/test_vc8_test.vcproj index da3dad6eed..7254ddcb87 100644 --- a/tests/test_vc8_test.vcproj +++ b/tests/test_vc8_test.vcproj @@ -1043,6 +1043,10 @@ RelativePath=".\streams\memstream.cpp" > + + diff --git a/tests/test_vc9_test.vcproj b/tests/test_vc9_test.vcproj index 80f59fbfb8..071b74f23a 100644 --- a/tests/test_vc9_test.vcproj +++ b/tests/test_vc9_test.vcproj @@ -1,16 +1,10 @@ - - - + + @@ -1146,7 +1142,5 @@ - - diff --git a/tests/thread/misc.cpp b/tests/thread/misc.cpp new file mode 100644 index 0000000000..d2eb6aacb5 --- /dev/null +++ b/tests/thread/misc.cpp @@ -0,0 +1,407 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: tests/thread/misc.cpp +// Purpose: Miscellaneous wxThread test cases +// Author: Francesco Montorsi (extracted from console sample) +// Created: 2010-05-10 +// RCS-ID: $Id$ +// Copyright: (c) 2010 wxWidgets team +/////////////////////////////////////////////////////////////////////////////// + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- + +#include "testprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#endif // WX_PRECOMP + +#include "wx/thread.h" +#include "wx/tls.h" + +// ---------------------------------------------------------------------------- +// globals +// ---------------------------------------------------------------------------- + +static size_t gs_counter = (size_t)-1; +static wxCriticalSection gs_critsect; +static wxSemaphore gs_cond; + +class MyJoinableThread : public wxThread +{ +public: + MyJoinableThread(size_t n) : wxThread(wxTHREAD_JOINABLE) + { m_n = n; Create(); } + + // thread execution starts here + virtual ExitCode Entry(); + +private: + size_t m_n; +}; + +wxThread::ExitCode MyJoinableThread::Entry() +{ + unsigned long res = 1; + for ( size_t n = 1; n < m_n; n++ ) + { + res *= n; + + // it's a loooong calculation :-) + wxMilliSleep(100); + } + + return (ExitCode)res; +} + +class MyDetachedThread : public wxThread +{ +public: + MyDetachedThread(size_t n, wxChar ch) + { + m_n = n; + m_ch = ch; + m_cancelled = false; + + Create(); + } + + // thread execution starts here + virtual ExitCode Entry(); + + // and stops here + virtual void OnExit(); + +private: + size_t m_n; // number of characters to write + wxChar m_ch; // character to write + + bool m_cancelled; // false if we exit normally +}; + +wxThread::ExitCode MyDetachedThread::Entry() +{ + { + wxCriticalSectionLocker lock(gs_critsect); + if ( gs_counter == (size_t)-1 ) + gs_counter = 1; + else + gs_counter++; + } + + for ( size_t n = 0; n < m_n; n++ ) + { + if ( TestDestroy() ) + { + m_cancelled = true; + + break; + } + + //wxPutchar(m_ch); + //fflush(stdout); + + wxThread::Sleep(100); + } + + return 0; +} + +void MyDetachedThread::OnExit() +{ + //wxLogTrace(wxT("thread"), wxT("Thread %ld is in OnExit"), GetId()); + + wxCriticalSectionLocker lock(gs_critsect); + if ( !--gs_counter && !m_cancelled ) + gs_cond.Post(); +} + +class MyWaitingThread : public wxThread +{ +public: + MyWaitingThread( wxMutex *mutex, wxCondition *condition ) + { + m_mutex = mutex; + m_condition = condition; + + Create(); + } + + virtual ExitCode Entry() + { + //wxPrintf(wxT("Thread %lu has started running.\n"), GetId()); + gs_cond.Post(); + + //wxPrintf(wxT("Thread %lu starts to wait...\n"), GetId()); + + m_mutex->Lock(); + m_condition->Wait(); + m_mutex->Unlock(); + + //wxPrintf(wxT("Thread %lu finished to wait, exiting.\n"), GetId()); + + return 0; + } + +private: + wxMutex *m_mutex; + wxCondition *m_condition; +}; + +// semaphore tests +#include "wx/datetime.h" + +class MySemaphoreThread : public wxThread +{ +public: + MySemaphoreThread(int i, wxSemaphore *sem) + : wxThread(wxTHREAD_JOINABLE), + m_sem(sem), + m_i(i) + { + Create(); + } + + virtual ExitCode Entry() + { + //wxPrintf(wxT("%s: Thread #%d (%ld) starting to wait for semaphore...\n"), + // wxDateTime::Now().FormatTime().c_str(), m_i, (long)GetId()); + + m_sem->Wait(); + + //wxPrintf(wxT("%s: Thread #%d (%ld) acquired the semaphore.\n"), + // wxDateTime::Now().FormatTime().c_str(), m_i, (long)GetId()); + + Sleep(1000); + + //wxPrintf(wxT("%s: Thread #%d (%ld) releasing the semaphore.\n"), + // wxDateTime::Now().FormatTime().c_str(), m_i, (long)GetId()); + + m_sem->Post(); + + return 0; + } + +private: + wxSemaphore *m_sem; + int m_i; +}; + +WX_DEFINE_ARRAY_PTR(wxThread *, ArrayThreads); + +// ---------------------------------------------------------------------------- +// test class +// ---------------------------------------------------------------------------- + +class MiscThreadTestCase : public CppUnit::TestCase +{ +public: + MiscThreadTestCase(); + +private: + CPPUNIT_TEST_SUITE( MiscThreadTestCase ); + CPPUNIT_TEST( TestJoinable ); + CPPUNIT_TEST( TestDetached ); + CPPUNIT_TEST( TestThreadSuspend ); + CPPUNIT_TEST( TestThreadDelete ); + CPPUNIT_TEST( TestThreadConditions ); + CPPUNIT_TEST( TestSemaphore ); + CPPUNIT_TEST_SUITE_END(); + + void TestJoinable(); + void TestDetached(); + void TestSemaphore(); + + void TestThreadSuspend(); + void TestThreadDelete(); + void TestThreadConditions(); + + DECLARE_NO_COPY_CLASS(MiscThreadTestCase) +}; + +// register in the unnamed registry so that these tests are run by default +CPPUNIT_TEST_SUITE_REGISTRATION( MiscThreadTestCase ); + +// also include in it's own registry so that these tests can be run alone +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MiscThreadTestCase, "MiscThreadTestCase" ); + +MiscThreadTestCase::MiscThreadTestCase() +{ + int nCPUs = wxThread::GetCPUCount(); + if ( nCPUs != -1 ) + wxThread::SetConcurrency(nCPUs); +} + +void MiscThreadTestCase::TestJoinable() +{ + // calc 10! in the background + MyJoinableThread thread(10); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread.Run() ); + CPPUNIT_ASSERT_EQUAL( 362880, (unsigned long)thread.Wait() ); +} + +void MiscThreadTestCase::TestDetached() +{ + static const size_t nThreads = 3; + MyDetachedThread *threads[nThreads]; + + size_t n; + for ( n = 0; n < nThreads; n++ ) + { + threads[n] = new MyDetachedThread(10, 'A' + n); + } + + threads[0]->SetPriority(WXTHREAD_MIN_PRIORITY); + threads[1]->SetPriority(WXTHREAD_MAX_PRIORITY); + + for ( n = 0; n < nThreads; n++ ) + { + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, threads[n]->Run() ); + } + + // wait until all threads terminate + CPPUNIT_ASSERT_EQUAL( wxSEMA_NO_ERROR, gs_cond.Wait() ); +} + +void MiscThreadTestCase::TestSemaphore() +{ + static const int SEM_LIMIT = 3; + + wxSemaphore sem(SEM_LIMIT, SEM_LIMIT); + ArrayThreads threads; + + for ( int i = 0; i < 3*SEM_LIMIT; i++ ) + { + threads.Add(new MySemaphoreThread(i, &sem)); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, threads.Last()->Run() ); + } + + for ( size_t n = 0; n < threads.GetCount(); n++ ) + { + CPPUNIT_ASSERT_EQUAL( 0, (long)threads[n]->Wait() ); + delete threads[n]; + } +} + +void MiscThreadTestCase::TestThreadSuspend() +{ + MyDetachedThread *thread = new MyDetachedThread(15, 'X'); + + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread->Run() ); + + // this is for this demo only, in a real life program we'd use another + // condition variable which would be signaled from wxThread::Entry() to + // tell us that the thread really started running - but here just wait a + // bit and hope that it will be enough (the problem is, of course, that + // the thread might still not run when we call Pause() which will result + // in an error) + wxThread::Sleep(300); + + for ( size_t n = 0; n < 3; n++ ) + { + thread->Pause(); + + if ( n > 0 ) + { + // don't sleep but resume immediately the first time + wxThread::Sleep(300); + } + + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread->Resume() ); + } + + // wait until the thread terminates + CPPUNIT_ASSERT_EQUAL( wxSEMA_NO_ERROR, gs_cond.Wait() ); +} + +void MiscThreadTestCase::TestThreadDelete() +{ + // As above, using Sleep() is only for testing here - we must use some + // synchronisation object instead to ensure that the thread is still + // running when we delete it - deleting a detached thread which already + // terminated will lead to a crash! + + MyDetachedThread *thread0 = new MyDetachedThread(30, 'W'); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_MISC_ERROR, thread0->Delete() ); + // delete a thread which didn't start to run yet. + + MyDetachedThread *thread1 = new MyDetachedThread(30, 'Y'); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread1->Run() ); + wxThread::Sleep(300); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread1->Delete() ); + // delete a running thread + + MyDetachedThread *thread2 = new MyDetachedThread(30, 'Z'); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread2->Run() ); + wxThread::Sleep(300); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread2->Pause() ); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread2->Delete() ); + // delete a sleeping thread + + MyJoinableThread thread3(20); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread3.Run() ); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread3.Delete() ); + // delete a joinable thread + + MyJoinableThread thread4(2); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread4.Run() ); + wxThread::Sleep(300); + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread4.Delete() ); + // delete a joinable thread which already terminated +} + +void MiscThreadTestCase::TestThreadConditions() +{ + wxMutex mutex; + wxCondition condition(mutex); + + // otherwise its difficult to understand which log messages pertain to + // which condition + //wxLogTrace(wxT("thread"), wxT("Local condition var is %08x, gs_cond = %08x"), + // condition.GetId(), gs_cond.GetId()); + + // create and launch threads + MyWaitingThread *threads[10]; + + size_t n; + for ( n = 0; n < WXSIZEOF(threads); n++ ) + { + threads[n] = new MyWaitingThread( &mutex, &condition ); + } + + for ( n = 0; n < WXSIZEOF(threads); n++ ) + { + CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, threads[n]->Run() ); + } + + // wait until all threads run + // NOTE: main thread is waiting for the other threads to start + size_t nRunning = 0; + while ( nRunning < WXSIZEOF(threads) ) + { + CPPUNIT_ASSERT_EQUAL( wxSEMA_NO_ERROR, gs_cond.Wait() ); + + nRunning++; + + // note that main thread is already running + } + + wxThread::Sleep(500); + +#if 1 + // now wake one of them up + CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, condition.Signal() ); +#endif + + wxThread::Sleep(200); + + // wake all the (remaining) threads up, so that they can exit + CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, condition.Broadcast() ); + + // give them time to terminate (dirty!) + wxThread::Sleep(500); +} -- 2.47.2