joinable and detached POSIX threads (not fully tested yet)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@4769 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 1999-11-29 23:05:23 +00:00
parent 7d3a036dc4
commit 9fc3ad34c5
7 changed files with 836 additions and 328 deletions

View File

@ -8,6 +8,25 @@ much easier to share common data between several threads, it also makes much
easier to shoot oneself in the foot, so careful use of synchronization objects
such as \helpref{mutexes}{wxmutex} and/or \helpref{critical sections}{wxcriticalsection} is recommended.
There are two types of threads in wxWindows: {\it detached} and {\it joinable}
ones, just as in POSIX thread API (but unlike Win32 threads where all threads
are joinable). The difference between the two is that only joinbale threads
can return a return code - it is returned by Wait() function. The detached
threads (default) can not be waited for.
You shouldn't hurry to create all the threads joinable, however, because this
has a disadvantage as well: you {\bf must} Wait() for a joinable thread of the
system resources used by it will never be freed and you also must delete the
corresponding wxThread object yourself, while detached threads are of the
"fire-and-forget" kind: you only have to start a detached thread and it will
terminate and destroy itself.
This means, of course, that all detached threads {\bf must} be created on the
heap because the thread will call {\tt delete this;} upon termination. The
joinable threads may be created on stack (don't create global thread objects
because they allocate memory in their constructor which is a badthing to do),
although usually they will be created on the heap as well.
\wxheading{Derived from}
None.
@ -84,6 +103,10 @@ created. Moreover, it must be called if \helpref{Create}{wxthreadcreate} or
occupied by the thread object (it will be done in the destructor for joinable
threads).
Delete() may be called for thread in any state: running, paused or even not yet created. Moreover,
it must be called if \helpref{Create}{wxthreadcreate} or \helpref{Run}{wxthreadrun} fail to free
the memory occupied by the thread object.
For detached threads Delete() will also delete the C++ thread object, but it
will not do this for joinable ones.

View File

@ -8,7 +8,10 @@ wxWindows provides a complete set of classes encapsulating objects necessary in
multithreaded (MT) programs: the \helpref{thread}{wxthread} class itself and different
synchronization objects: \helpref{mutexes}{wxmutex} and
\helpref{critical sections}{wxcriticalsection} with
\helpref{conditions}{wxcondition}.
\helpref{conditions}{wxcondition}. The thread API in wxWindows resembles to
POSIX1.c threads API (a.k.a. pthreads), although several functions have
different names and some features inspired by Win32 thread API are there as
well.
These classes will hopefully make writing MT programs easier and they also
provide some extra error checking (compared to the native (be it Win32 or Posix)
@ -21,7 +24,7 @@ new thread for each new client), but in others it might be a very poor choice
(example: launching a separate thread when doing a long computation to show a
progress dialog). Other implementation choices are available: for the progress
dialog example it is far better to do the calculations in the
\helpref{idle handler}{wxidleevent} or call \helpref{wxYield()}{wxyield}
\helpref{idle handler}{wxidleevent} or call \helpref{wxYield()}{wxyield}
periodically to update the screen.
If you do decide to use threads in your application, it is strongly recommended

View File

@ -16,13 +16,6 @@
#pragma interface "textctrl.h"
#endif
// can we use RICHEDIT class for wxTextCtrl implementation?
#if defined(__WIN95__) && !defined(__TWIN32__) && !defined(__WXWINE__)
#define wxUSE_RICHEDIT 1
#else
#define wxUSE_RICHEDIT 0
#endif
class WXDLLEXPORT wxTextCtrl : public wxTextCtrlBase
{
public:

View File

@ -40,21 +40,21 @@
enum wxMutexError
{
wxMUTEX_NO_ERROR = 0,
wxMUTEX_DEAD_LOCK, // Mutex has been already locked by THE CALLING thread
wxMUTEX_BUSY, // Mutex has been already locked by ONE thread
wxMUTEX_UNLOCKED,
wxMUTEX_MISC_ERROR
wxMUTEX_NO_ERROR = 0,
wxMUTEX_DEAD_LOCK, // Mutex has been already locked by THE CALLING thread
wxMUTEX_BUSY, // Mutex has been already locked by ONE thread
wxMUTEX_UNLOCKED,
wxMUTEX_MISC_ERROR
};
enum wxThreadError
{
wxTHREAD_NO_ERROR = 0, // No error
wxTHREAD_NO_RESOURCE, // No resource left to create a new thread
wxTHREAD_RUNNING, // The thread is already running
wxTHREAD_NOT_RUNNING, // The thread isn't running
wxTHREAD_KILLED, // Thread we waited for had to be killed
wxTHREAD_MISC_ERROR // Some other error
wxTHREAD_NO_ERROR = 0, // No error
wxTHREAD_NO_RESOURCE, // No resource left to create a new thread
wxTHREAD_RUNNING, // The thread is already running
wxTHREAD_NOT_RUNNING, // The thread isn't running
wxTHREAD_KILLED, // Thread we waited for had to be killed
wxTHREAD_MISC_ERROR // Some other error
};
enum wxThreadKind
@ -106,7 +106,7 @@ protected:
wxMutex& operator=(const wxMutex&);
int m_locked;
wxMutexInternal *p_internal;
wxMutexInternal *m_internal;
};
// a helper class which locks the mutex in the ctor and unlocks it in the dtor:
@ -213,28 +213,32 @@ private:
};
// ----------------------------------------------------------------------------
// Condition handler.
// Condition variable: allows to block the thread execution until something
// happens (== condition is signaled)
// ----------------------------------------------------------------------------
class wxConditionInternal;
class WXDLLEXPORT wxCondition
{
public:
// constructor & destructor
wxCondition();
~wxCondition();
// constructor & destructor
wxCondition();
~wxCondition();
// Waits indefinitely.
void Wait(wxMutex& mutex);
// Waits until a signal is raised or the timeout is elapsed.
bool Wait(wxMutex& mutex, unsigned long sec, unsigned long nsec);
// Raises a signal: only one "Waiter" is released.
void Signal();
// Broadcasts to all "Waiters".
void Broadcast();
// wait until the condition is signaled
// waits indefinitely.
void Wait();
// waits until a signal is raised or the timeout elapses
bool Wait(unsigned long sec, unsigned long nsec);
// signal the condition
// wakes up one (and only one) of the waiting threads
void Signal();
// wakes up all threads waiting onthis condition
void Broadcast();
private:
wxConditionInternal *p_internal;
wxConditionInternal *m_internal;
};
// ----------------------------------------------------------------------------
@ -381,7 +385,7 @@ private:
friend class wxThreadInternal;
// the (platform-dependent) thread class implementation
wxThreadInternal *p_internal;
wxThreadInternal *m_internal;
// protects access to any methods of wxThreadInternal object
wxCriticalSection m_critsect;

View File

@ -31,9 +31,10 @@
//#define TEST_ARRAYS
//#define TEST_LOG
//#define TEST_THREADS
//#define TEST_STRINGS
#define TEST_THREADS
//#define TEST_TIME
#define TEST_LONGLONG
//#define TEST_LONGLONG
// ============================================================================
// implementation
@ -52,7 +53,7 @@ static void TestSpeed()
{
static const long max = 100000000;
long n;
{
wxStopWatch sw;
@ -151,7 +152,7 @@ wxThread::ExitCode MyJoinableThread::Entry()
class MyDetachedThread : public wxThread
{
public:
MyDetachedThread(char ch) { m_ch = ch; Create(); }
MyDetachedThread(size_t n, char ch) { m_n = n; m_ch = ch; Create(); }
// thread execution starts here
virtual ExitCode Entry();
@ -160,7 +161,8 @@ public:
virtual void OnExit();
private:
char m_ch;
size_t m_n; // number of characters to write
char m_ch; // character to write
};
wxThread::ExitCode MyDetachedThread::Entry()
@ -173,8 +175,7 @@ wxThread::ExitCode MyDetachedThread::Entry()
gs_counter++;
}
static const size_t nIter = 10;
for ( size_t n = 0; n < nIter; n++ )
for ( size_t n = 0; n < m_n; n++ )
{
if ( TestDestroy() )
break;
@ -190,11 +191,86 @@ wxThread::ExitCode MyDetachedThread::Entry()
void MyDetachedThread::OnExit()
{
wxLogTrace("thread", "Thread %ld is in OnExit", GetId());
wxCriticalSectionLocker lock(gs_critsect);
if ( !--gs_counter )
gs_cond.Signal();
}
void TestDetachedThreads()
{
puts("*** 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();
puts("");
}
void TestJoinableThreads()
{
puts("*** Testing a joinable thread (a loooong calculation...) ***");
// calc 10! in the background
MyJoinableThread thread(10);
thread.Run();
printf("\nThread terminated with exit code %lu.\n",
(unsigned long)thread.Wait());
}
void TestThreadSuspend()
{
MyDetachedThread *thread = new MyDetachedThread(30, '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();
puts("\nThread suspended");
if ( n > 0 )
{
// don't sleep but resume immediately the first time
wxThread::Sleep(300);
}
puts("Going to resume the thread");
thread->Resume();
}
// wait until the thread terminates
gs_cond.Wait();
puts("");
}
#endif // TEST_THREADS
// ----------------------------------------------------------------------------
@ -216,6 +292,64 @@ void PrintArray(const char* name, const wxArrayString& array)
#endif // TEST_ARRAYS
// ----------------------------------------------------------------------------
// strings
// ----------------------------------------------------------------------------
#ifdef TEST_STRINGS
#include "wx/timer.h"
void TestString()
{
wxStopWatch sw;
wxString a, b, c;
a.reserve (128);
b.reserve (128);
c.reserve (128);
for (int i = 0; i < 1000000; ++i)
{
a = "Hello";
b = " world";
c = "! How'ya doin'?";
a += b;
a += c;
c = "Hello world! What's up?";
if (c != a)
c = "Doh!";
}
printf ("TestString elapsed time: %ld\n", sw.Time());
}
void TestPChar()
{
wxStopWatch sw;
char a [128];
char b [128];
char c [128];
for (int i = 0; i < 1000000; ++i)
{
strcpy (a, "Hello");
strcpy (b, " world");
strcpy (c, "! How'ya doin'?");
strcat (a, b);
strcat (a, c);
strcpy (c, "Hello world! What's up?");
if (strcmp (c, a) == 0)
strcpy (c, "Doh!");
}
printf ("TestPChar elapsed time: %ld\n", sw.Time());
}
#endif // TEST_STRINGS
// ----------------------------------------------------------------------------
// entry point
// ----------------------------------------------------------------------------
@ -227,6 +361,11 @@ int main(int argc, char **argv)
fprintf(stderr, "Failed to initialize the wxWindows library, aborting.");
}
#ifdef TEST_STRINGS
TestPChar();
TestString();
#endif // TEST_STRINGS
#ifdef TEST_ARRAYS
wxArrayString a1;
a1.Add("tiger");
@ -279,38 +418,15 @@ int main(int argc, char **argv)
#endif // TEST_LOG
#ifdef TEST_THREADS
puts("Testing detached threads...");
if ( argc > 1 && argv[1][0] == 't' )
wxLog::AddTraceMask("thread");
static const size_t nThreads = 3;
MyDetachedThread *threads[nThreads];
size_t n;
for ( n = 0; n < nThreads; n++ )
TestThreadSuspend();
if ( 0 )
{
threads[n] = new MyDetachedThread('A' + n);
TestDetachedThreads();
TestJoinableThreads();
}
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
wxMutex mutex;
mutex.Lock();
gs_cond.Wait(mutex);
mutex.Unlock();
puts("\n\nTesting a joinable thread used for a loooong calculation...");
// calc 10! in the background
MyJoinableThread thread(10);
thread.Run();
printf("\nThread terminated with exit code %lu.\n",
(unsigned long)thread.Wait());
#endif // TEST_THREADS
#ifdef TEST_LONGLONG

View File

@ -104,9 +104,9 @@ public:
wxMutex::wxMutex()
{
p_internal = new wxMutexInternal;
p_internal->p_mutex = CreateMutex(NULL, FALSE, NULL);
if ( !p_internal->p_mutex )
m_internal = new wxMutexInternal;
m_internal->p_mutex = CreateMutex(NULL, FALSE, NULL);
if ( !m_internal->p_mutex )
{
wxLogSysError(_("Can not create mutex."));
}
@ -118,14 +118,14 @@ wxMutex::~wxMutex()
{
if (m_locked > 0)
wxLogDebug(wxT("Warning: freeing a locked mutex (%d locks)."), m_locked);
CloseHandle(p_internal->p_mutex);
CloseHandle(m_internal->p_mutex);
}
wxMutexError wxMutex::Lock()
{
DWORD ret;
ret = WaitForSingleObject(p_internal->p_mutex, INFINITE);
ret = WaitForSingleObject(m_internal->p_mutex, INFINITE);
switch ( ret )
{
case WAIT_ABANDONED:
@ -152,7 +152,7 @@ wxMutexError wxMutex::TryLock()
{
DWORD ret;
ret = WaitForSingleObject(p_internal->p_mutex, 0);
ret = WaitForSingleObject(m_internal->p_mutex, 0);
if (ret == WAIT_TIMEOUT || ret == WAIT_ABANDONED)
return wxMUTEX_BUSY;
@ -165,7 +165,7 @@ wxMutexError wxMutex::Unlock()
if (m_locked > 0)
m_locked--;
BOOL ret = ReleaseMutex(p_internal->p_mutex);
BOOL ret = ReleaseMutex(m_internal->p_mutex);
if ( ret == 0 )
{
wxLogSysError(_("Couldn't release a mutex"));
@ -197,16 +197,14 @@ public:
waiters = 0;
}
bool Wait(wxMutex& mutex, DWORD timeout)
bool Wait(DWORD timeout)
{
mutex.Unlock();
waiters++;
// FIXME this should be MsgWaitForMultipleObjects() as well probably
DWORD rc = ::WaitForSingleObject(event, timeout);
waiters--;
mutex.Lock();
return rc != WAIT_TIMEOUT;
}
@ -228,24 +226,23 @@ public:
wxCondition::wxCondition()
{
p_internal = new wxConditionInternal;
m_internal = new wxConditionInternal;
}
wxCondition::~wxCondition()
{
delete p_internal;
delete m_internal;
}
void wxCondition::Wait(wxMutex& mutex)
void wxCondition::Wait()
{
(void)p_internal->Wait(mutex, INFINITE);
(void)m_internal->Wait(INFINITE);
}
bool wxCondition::Wait(wxMutex& mutex,
unsigned long sec,
bool wxCondition::Wait(unsigned long sec,
unsigned long nsec)
{
return p_internal->Wait(mutex, sec*1000 + nsec/1000000);
return m_internal->Wait(sec*1000 + nsec/1000000);
}
void wxCondition::Signal()
@ -255,7 +252,7 @@ void wxCondition::Signal()
// someone waits on it. In any case, the system will return it to a non
// signalled state afterwards. If multiple threads are waiting, only one
// will be woken up.
if ( !::SetEvent(p_internal->event) )
if ( !::SetEvent(m_internal->event) )
{
wxLogLastError("SetEvent");
}
@ -266,7 +263,7 @@ void wxCondition::Broadcast()
// this works because all these threads are already waiting and so each
// SetEvent() inside Signal() is really a PulseEvent() because the event
// state is immediately returned to non-signaled
for ( int i = 0; i < p_internal->waiters; i++ )
for ( int i = 0; i < m_internal->waiters; i++ )
{
Signal();
}
@ -378,8 +375,8 @@ DWORD wxThreadInternal::WinThreadStart(wxThread *thread)
// enter m_critsect before changing the thread state
thread->m_critsect.Enter();
bool wasCancelled = thread->p_internal->GetState() == STATE_CANCELED;
thread->p_internal->SetState(STATE_EXITED);
bool wasCancelled = thread->m_internal->GetState() == STATE_CANCELED;
thread->m_internal->SetState(STATE_EXITED);
thread->m_critsect.Leave();
thread->OnExit();
@ -535,14 +532,14 @@ void wxThread::Sleep(unsigned long milliseconds)
wxThread::wxThread(wxThreadKind kind)
{
p_internal = new wxThreadInternal();
m_internal = new wxThreadInternal();
m_isDetached = kind == wxTHREAD_DETACHED;
}
wxThread::~wxThread()
{
delete p_internal;
delete m_internal;
}
// create/start thread
@ -552,7 +549,7 @@ wxThreadError wxThread::Create()
{
wxCriticalSectionLocker lock(m_critsect);
if ( !p_internal->Create(this) )
if ( !m_internal->Create(this) )
return wxTHREAD_NO_RESOURCE;
return wxTHREAD_NO_ERROR;
@ -562,7 +559,7 @@ wxThreadError wxThread::Run()
{
wxCriticalSectionLocker lock(m_critsect);
if ( p_internal->GetState() != STATE_NEW )
if ( m_internal->GetState() != STATE_NEW )
{
// actually, it may be almost any state at all, not only STATE_RUNNING
return wxTHREAD_RUNNING;
@ -579,14 +576,14 @@ wxThreadError wxThread::Pause()
{
wxCriticalSectionLocker lock(m_critsect);
return p_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
}
wxThreadError wxThread::Resume()
{
wxCriticalSectionLocker lock(m_critsect);
return p_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
}
// stopping thread
@ -603,7 +600,7 @@ wxThread::ExitCode wxThread::Wait()
(void)Delete(&rc);
p_internal->Free();
m_internal->Free();
return rc;
}
@ -616,7 +613,7 @@ wxThreadError wxThread::Delete(ExitCode *pRc)
if ( IsPaused() )
Resume();
HANDLE hThread = p_internal->GetHandle();
HANDLE hThread = m_internal->GetHandle();
if ( IsRunning() )
{
@ -634,7 +631,7 @@ wxThreadError wxThread::Delete(ExitCode *pRc)
{
wxCriticalSectionLocker lock(m_critsect);
p_internal->Cancel();
m_internal->Cancel();
}
#if wxUSE_GUI
@ -742,14 +739,14 @@ wxThreadError wxThread::Kill()
if ( !IsRunning() )
return wxTHREAD_NOT_RUNNING;
if ( !::TerminateThread(p_internal->GetHandle(), (DWORD)-1) )
if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) )
{
wxLogSysError(_("Couldn't terminate thread"));
return wxTHREAD_MISC_ERROR;
}
p_internal->Free();
m_internal->Free();
if ( IsDetached() )
{
@ -761,7 +758,7 @@ wxThreadError wxThread::Kill()
void wxThread::Exit(ExitCode status)
{
p_internal->Free();
m_internal->Free();
if ( IsDetached() )
{
@ -784,50 +781,50 @@ void wxThread::SetPriority(unsigned int prio)
{
wxCriticalSectionLocker lock(m_critsect);
p_internal->SetPriority(prio);
m_internal->SetPriority(prio);
}
unsigned int wxThread::GetPriority() const
{
wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
return p_internal->GetPriority();
return m_internal->GetPriority();
}
unsigned long wxThread::GetId() const
{
wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
return (unsigned long)p_internal->GetId();
return (unsigned long)m_internal->GetId();
}
bool wxThread::IsRunning() const
{
wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
return p_internal->GetState() == STATE_RUNNING;
return m_internal->GetState() == STATE_RUNNING;
}
bool wxThread::IsAlive() const
{
wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
return (p_internal->GetState() == STATE_RUNNING) ||
(p_internal->GetState() == STATE_PAUSED);
return (m_internal->GetState() == STATE_RUNNING) ||
(m_internal->GetState() == STATE_PAUSED);
}
bool wxThread::IsPaused() const
{
wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
return p_internal->GetState() == STATE_PAUSED;
return m_internal->GetState() == STATE_PAUSED;
}
bool wxThread::TestDestroy()
{
wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect); // const_cast
return p_internal->GetState() == STATE_CANCELED;
return m_internal->GetState() == STATE_CANCELED;
}
// ----------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff