| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- ///////////////////////////////////////////////////////////////////////////////
- // Name: tests/thread/misc.cpp
- // Purpose: Miscellaneous wxThread test cases
- // Author: Francesco Montorsi (extracted from console sample)
- // Created: 2010-05-10
- // 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/utils.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);
- wxMilliSleep(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()
- {
- wxUnusedVar(m_i);
- //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( TestThreadRun );
- CPPUNIT_TEST( TestThreadConditions );
- CPPUNIT_TEST( TestSemaphore );
- CPPUNIT_TEST_SUITE_END();
- void TestJoinable();
- void TestDetached();
- void TestSemaphore();
- void TestThreadSuspend();
- void TestThreadDelete();
- void TestThreadRun();
- 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 its 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(wxPRIORITY_MIN);
- threads[1]->SetPriority(wxPRIORITY_MAX);
- 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)
- wxMilliSleep(300);
- for ( size_t n = 0; n < 3; n++ )
- {
- thread->Pause();
- if ( n > 0 )
- {
- // don't sleep but resume immediately the first time
- wxMilliSleep(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()
- {
- // FIXME:
- // 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() );
- wxMilliSleep(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() );
- wxMilliSleep(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 running thread
- MyJoinableThread thread4(2);
- CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread4.Run() );
- wxMilliSleep(300);
- CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread4.Delete() );
- // delete a joinable thread which already terminated
- }
- void MiscThreadTestCase::TestThreadRun()
- {
- MyJoinableThread thread1(2);
- CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread1.Run() );
- thread1.Wait(); // wait until the thread ends
- // verify that running twice the same thread fails
- WX_ASSERT_FAILS_WITH_ASSERT( thread1.Run() );
- }
- 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
- }
- wxMilliSleep(500);
- #if 1
- // now wake one of them up
- CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, condition.Signal() );
- #endif
- wxMilliSleep(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!)
- wxMilliSleep(500);
- }
|