Changed several occurrences of "it's" where "its" is meant, as well as a few other minor typos. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@67656 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			421 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			421 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
///////////////////////////////////////////////////////////////////////////////
 | 
						|
// 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/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()
 | 
						|
    {
 | 
						|
        //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(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)
 | 
						|
    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);
 | 
						|
}
 |