#define TEST_STDPATHS
#define TEST_STREAMS
#define TEST_TEXTSTREAM
- #define TEST_THREADS
#define TEST_TIMER
// #define TEST_VOLUME --FIXME! (RN)
#define TEST_WCHAR
#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
// ----------------------------------------------------------------------------
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();
test_zlibstream.o \
test_textfiletest.o \
test_atomic.o \
+ test_misc.o \
test_queue.o \
test_tls.o \
test_uris.o \
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
$(OBJS)\test_zlibstream.obj \\r
$(OBJS)\test_textfiletest.obj \\r
$(OBJS)\test_atomic.obj \\r
+ $(OBJS)\test_misc.obj \\r
$(OBJS)\test_queue.obj \\r
$(OBJS)\test_tls.obj \\r
$(OBJS)\test_uris.obj \\r
$(OBJS)\test_atomic.obj: .\thread\atomic.cpp\r
$(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\thread\atomic.cpp\r
\r
+$(OBJS)\test_misc.obj: .\thread\misc.cpp\r
+ $(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\thread\misc.cpp\r
+\r
$(OBJS)\test_queue.obj: .\thread\queue.cpp\r
$(CXX) -q -c -P -o$@ $(TEST_CXXFLAGS) .\thread\queue.cpp\r
\r
$(OBJS)\test_zlibstream.o \\r
$(OBJS)\test_textfiletest.o \\r
$(OBJS)\test_atomic.o \\r
+ $(OBJS)\test_misc.o \\r
$(OBJS)\test_queue.o \\r
$(OBJS)\test_tls.o \\r
$(OBJS)\test_uris.o \\r
$(OBJS)\test_atomic.o: ./thread/atomic.cpp\r
$(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $<\r
\r
+$(OBJS)\test_misc.o: ./thread/misc.cpp\r
+ $(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $<\r
+\r
$(OBJS)\test_queue.o: ./thread/queue.cpp\r
$(CXX) -c -o $@ $(TEST_CXXFLAGS) $(CPPDEPS) $<\r
\r
$(OBJS)\test_zlibstream.obj \\r
$(OBJS)\test_textfiletest.obj \\r
$(OBJS)\test_atomic.obj \\r
+ $(OBJS)\test_misc.obj \\r
$(OBJS)\test_queue.obj \\r
$(OBJS)\test_tls.obj \\r
$(OBJS)\test_uris.obj \\r
$(OBJS)\test_atomic.obj: .\thread\atomic.cpp\r
$(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\thread\atomic.cpp\r
\r
+$(OBJS)\test_misc.obj: .\thread\misc.cpp\r
+ $(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\thread\misc.cpp\r
+\r
$(OBJS)\test_queue.obj: .\thread\queue.cpp\r
$(CXX) /c /nologo /TP /Fo$@ $(TEST_CXXFLAGS) .\thread\queue.cpp\r
\r
$(OBJS)\test_zlibstream.obj &\r
$(OBJS)\test_textfiletest.obj &\r
$(OBJS)\test_atomic.obj &\r
+ $(OBJS)\test_misc.obj &\r
$(OBJS)\test_queue.obj &\r
$(OBJS)\test_tls.obj &\r
$(OBJS)\test_uris.obj &\r
$(OBJS)\test_atomic.obj : .AUTODEPEND .\thread\atomic.cpp\r
$(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $<\r
\r
+$(OBJS)\test_misc.obj : .AUTODEPEND .\thread\misc.cpp\r
+ $(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $<\r
+\r
$(OBJS)\test_queue.obj : .AUTODEPEND .\thread\queue.cpp\r
$(CXX) -bt=nt -zq -fo=$^@ $(TEST_CXXFLAGS) $<\r
\r
streams/zlibstream.cpp
textfile/textfiletest.cpp
thread/atomic.cpp
+ thread/misc.cpp
thread/queue.cpp
thread/tls.cpp
uris/uris.cpp
# End Source File\r
# Begin Source File\r
\r
+SOURCE=.\thread\misc.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
SOURCE=.\misc\misctests.cpp\r
# End Source File\r
# Begin Source File\r
<File\r
RelativePath=".\streams\memstream.cpp">\r
</File>\r
+ <File\r
+ RelativePath=".\thread\misc.cpp">\r
+ </File>\r
<File\r
RelativePath=".\misc\misctests.cpp">\r
</File>\r
RelativePath=".\streams\memstream.cpp"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\thread\misc.cpp"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\misc\misctests.cpp"\r
>\r
<?xml version="1.0" encoding="Windows-1252"?>\r
-<!--\r
-\r
- This project was generated by\r
- Bakefile 0.2.8 (http://www.bakefile.org)\r
- Do not modify, all changes will be overwritten!\r
-\r
--->\r
<VisualStudioProject\r
ProjectType="Visual C++"\r
- Version="9.00"\r
+ Version="9,00"\r
Name="test"\r
ProjectGUID="{2F45723C-ED6B-5F60-8BFF-6B3609464A7B}"\r
+ TargetFrameworkVersion="0"\r
>\r
<Platforms>\r
<Platform\r
/>\r
</Platforms>\r
<ToolFiles>\r
- \r
</ToolFiles>\r
<Configurations>\r
<Configuration\r
Name="VCCLCompilerTool"\r
AdditionalOptions="/MP"\r
Optimization="0"\r
- AdditionalIncludeDirectories=".\..\lib\vc_lib\mswud;.\..\include;."\r
+ AdditionalIncludeDirectories=".\..\lib\vc_lib\mswud;.\..\include;.;F:\cppunit\include"\r
PreprocessorDefinitions="WIN32;_DEBUG;__WXMSW__;_UNICODE;_CONSOLE;wxUSE_GUI=0"\r
ExceptionHandling="1"\r
BasicRuntimeChecks="3"\r
OutputFile="vc_mswud\test.exe"\r
LinkIncremental="2"\r
SuppressStartupBanner="true"\r
- AdditionalLibraryDirectories=".\..\lib\vc_lib"\r
+ AdditionalLibraryDirectories=".\..\lib\vc_lib;F:\cppunit\lib"\r
GenerateManifest="true"\r
GenerateDebugInformation="true"\r
ProgramDatabaseFile="vc_mswud\test.pdb"\r
/>\r
<Tool\r
Name="VCBscMakeTool"\r
- OutputFile="vc_mswud\test_vc9_test.bsc"\r
SuppressStartupBanner="true"\r
+ OutputFile="vc_mswud\test_vc9_test.bsc"\r
/>\r
<Tool\r
Name="VCFxCopTool"\r
GenerateDebugInformation="true"\r
ProgramDatabaseFile="vc_mswu\test.pdb"\r
SubSystem="1"\r
- TargetMachine="1"\r
OptimizeReferences="2"\r
EnableCOMDATFolding="2"\r
+ TargetMachine="1"\r
/>\r
<Tool\r
Name="VCALinkTool"\r
/>\r
<Tool\r
Name="VCBscMakeTool"\r
- OutputFile="vc_mswu\test_vc9_test.bsc"\r
SuppressStartupBanner="true"\r
+ OutputFile="vc_mswu\test_vc9_test.bsc"\r
/>\r
<Tool\r
Name="VCFxCopTool"\r
/>\r
<Tool\r
Name="VCBscMakeTool"\r
- OutputFile="vc_mswunivud\test_vc9_test.bsc"\r
SuppressStartupBanner="true"\r
+ OutputFile="vc_mswunivud\test_vc9_test.bsc"\r
/>\r
<Tool\r
Name="VCFxCopTool"\r
GenerateDebugInformation="true"\r
ProgramDatabaseFile="vc_mswunivu\test.pdb"\r
SubSystem="1"\r
- TargetMachine="1"\r
OptimizeReferences="2"\r
EnableCOMDATFolding="2"\r
+ TargetMachine="1"\r
/>\r
<Tool\r
Name="VCALinkTool"\r
/>\r
<Tool\r
Name="VCBscMakeTool"\r
- OutputFile="vc_mswunivu\test_vc9_test.bsc"\r
SuppressStartupBanner="true"\r
+ OutputFile="vc_mswunivu\test_vc9_test.bsc"\r
/>\r
<Tool\r
Name="VCFxCopTool"\r
/>\r
<Tool\r
Name="VCBscMakeTool"\r
- OutputFile="vc_mswuddll\test_vc9_test.bsc"\r
SuppressStartupBanner="true"\r
+ OutputFile="vc_mswuddll\test_vc9_test.bsc"\r
/>\r
<Tool\r
Name="VCFxCopTool"\r
GenerateDebugInformation="true"\r
ProgramDatabaseFile="vc_mswudll\test.pdb"\r
SubSystem="1"\r
- TargetMachine="1"\r
OptimizeReferences="2"\r
EnableCOMDATFolding="2"\r
+ TargetMachine="1"\r
/>\r
<Tool\r
Name="VCALinkTool"\r
/>\r
<Tool\r
Name="VCBscMakeTool"\r
- OutputFile="vc_mswudll\test_vc9_test.bsc"\r
SuppressStartupBanner="true"\r
+ OutputFile="vc_mswudll\test_vc9_test.bsc"\r
/>\r
<Tool\r
Name="VCFxCopTool"\r
/>\r
<Tool\r
Name="VCBscMakeTool"\r
- OutputFile="vc_mswunivuddll\test_vc9_test.bsc"\r
SuppressStartupBanner="true"\r
+ OutputFile="vc_mswunivuddll\test_vc9_test.bsc"\r
/>\r
<Tool\r
Name="VCFxCopTool"\r
GenerateDebugInformation="true"\r
ProgramDatabaseFile="vc_mswunivudll\test.pdb"\r
SubSystem="1"\r
- TargetMachine="1"\r
OptimizeReferences="2"\r
EnableCOMDATFolding="2"\r
+ TargetMachine="1"\r
/>\r
<Tool\r
Name="VCALinkTool"\r
/>\r
<Tool\r
Name="VCBscMakeTool"\r
- OutputFile="vc_mswunivudll\test_vc9_test.bsc"\r
SuppressStartupBanner="true"\r
+ OutputFile="vc_mswunivudll\test_vc9_test.bsc"\r
/>\r
<Tool\r
Name="VCFxCopTool"\r
</Configuration>\r
</Configurations>\r
<References>\r
- \r
</References>\r
<Files>\r
<Filter\r
RelativePath=".\streams\memstream.cpp"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\thread\misc.cpp"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\misc\misctests.cpp"\r
>\r
</Filter>\r
</Files>\r
<Globals>\r
- \r
</Globals>\r
</VisualStudioProject>\r
-\r
--- /dev/null
+///////////////////////////////////////////////////////////////////////////////
+// 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);
+}