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
This commit is contained in:
@@ -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();
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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) $<
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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) $<
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -727,6 +727,9 @@
|
||||
<File
|
||||
RelativePath=".\streams\memstream.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\thread\misc.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\misc\misctests.cpp">
|
||||
</File>
|
||||
|
@@ -1043,6 +1043,10 @@
|
||||
RelativePath=".\streams\memstream.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\thread\misc.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\misc\misctests.cpp"
|
||||
>
|
||||
|
@@ -1,16 +1,10 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<!--
|
||||
|
||||
This project was generated by
|
||||
Bakefile 0.2.8 (http://www.bakefile.org)
|
||||
Do not modify, all changes will be overwritten!
|
||||
|
||||
-->
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Version="9,00"
|
||||
Name="test"
|
||||
ProjectGUID="{2F45723C-ED6B-5F60-8BFF-6B3609464A7B}"
|
||||
TargetFrameworkVersion="0"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
@@ -18,7 +12,6 @@
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
@@ -51,7 +44,7 @@
|
||||
Name="VCCLCompilerTool"
|
||||
AdditionalOptions="/MP"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=".\..\lib\vc_lib\mswud;.\..\include;."
|
||||
AdditionalIncludeDirectories=".\..\lib\vc_lib\mswud;.\..\include;.;F:\cppunit\include"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;__WXMSW__;_UNICODE;_CONSOLE;wxUSE_GUI=0"
|
||||
ExceptionHandling="1"
|
||||
BasicRuntimeChecks="3"
|
||||
@@ -86,7 +79,7 @@
|
||||
OutputFile="vc_mswud\test.exe"
|
||||
LinkIncremental="2"
|
||||
SuppressStartupBanner="true"
|
||||
AdditionalLibraryDirectories=".\..\lib\vc_lib"
|
||||
AdditionalLibraryDirectories=".\..\lib\vc_lib;F:\cppunit\lib"
|
||||
GenerateManifest="true"
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="vc_mswud\test.pdb"
|
||||
@@ -104,8 +97,8 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
OutputFile="vc_mswud\test_vc9_test.bsc"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile="vc_mswud\test_vc9_test.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
@@ -185,9 +178,9 @@
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="vc_mswu\test.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
@@ -200,8 +193,8 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
OutputFile="vc_mswu\test_vc9_test.bsc"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile="vc_mswu\test_vc9_test.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
@@ -296,8 +289,8 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
OutputFile="vc_mswunivud\test_vc9_test.bsc"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile="vc_mswunivud\test_vc9_test.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
@@ -377,9 +370,9 @@
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="vc_mswunivu\test.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
@@ -392,8 +385,8 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
OutputFile="vc_mswunivu\test_vc9_test.bsc"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile="vc_mswunivu\test_vc9_test.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
@@ -488,8 +481,8 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
OutputFile="vc_mswuddll\test_vc9_test.bsc"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile="vc_mswuddll\test_vc9_test.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
@@ -569,9 +562,9 @@
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="vc_mswudll\test.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
@@ -584,8 +577,8 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
OutputFile="vc_mswudll\test_vc9_test.bsc"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile="vc_mswudll\test_vc9_test.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
@@ -680,8 +673,8 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
OutputFile="vc_mswunivuddll\test_vc9_test.bsc"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile="vc_mswunivuddll\test_vc9_test.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
@@ -761,9 +754,9 @@
|
||||
GenerateDebugInformation="true"
|
||||
ProgramDatabaseFile="vc_mswunivudll\test.pdb"
|
||||
SubSystem="1"
|
||||
TargetMachine="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
@@ -776,8 +769,8 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
OutputFile="vc_mswunivudll\test_vc9_test.bsc"
|
||||
SuppressStartupBanner="true"
|
||||
OutputFile="vc_mswunivudll\test_vc9_test.bsc"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
@@ -791,7 +784,6 @@
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
@@ -1015,6 +1007,10 @@
|
||||
RelativePath=".\streams\memstream.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\thread\misc.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\misc\misctests.cpp"
|
||||
>
|
||||
@@ -1146,7 +1142,5 @@
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
||||
|
||||
|
407
tests/thread/misc.cpp
Normal file
407
tests/thread/misc.cpp
Normal file
@@ -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);
|
||||
}
|
Reference in New Issue
Block a user