added wxSemaphore (with docs), new version of wxCondition and bug fixes to wxThread (patch 538242 by K.S. Sreeram)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@14907 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin 2002-04-02 13:15:16 +00:00
parent 8e2baee50d
commit be80986891
7 changed files with 850 additions and 353 deletions

View File

@ -505,6 +505,7 @@ capabilities of the various platforms.
\twocolitem{\helpref{wxCriticalSection}{wxcriticalsection}}{Critical section class}
\twocolitem{\helpref{wxCriticalSectionLocker}{wxcriticalsectionlocker}}{Critical section locker utility class}
\twocolitem{\helpref{wxCondition}{wxcondition}}{Condition class}
\twocolitem{\helpref{wxSemaphore}{wxsemaphore}}{Semaphore class}
\end{twocollist}
{\large {\bf HTML classes}}

View File

@ -270,6 +270,7 @@
\input scrolwin.tex
\input scrlwevt.tex
\input scrolevt.tex
\input semaphor.tex
\input hprovsmp.tex
\input sngchdlg.tex
\input snglinst.tex

View File

@ -12,28 +12,22 @@ perfect because in this particular case it would be much better to just
worker threads it already makes much more sense).
Note that a call to \helpref{Signal()}{wxconditionsignal} may happen before the
other thread calls \helpref{Wait()}{wxconditionwait} but, in marked contrast
with the pthread conditions, this will still work as the missed signals are
queued and \helpref{Wait()}{wxconditionwait} simply returns immediately if
there are ny pending signals.
However, the calls to \helpref{Broadcast()}{wxconditionbroadcast} are {\bf not}
queued and so it will only wake up the threads already waiting on the
condition. Accordingly, you will probably want to use a mutex to ensure that
the thread(s) you want to be waken up have indeed started to wait before
calling \helpref{Broadcast()}{wxconditionbroadcast}.
other thread calls \helpref{Wait()}{wxconditionwait} and, just as with the
pthread conditions, the signal is then lost and so if you want to be sure to
get it you must use a mutex together with the condition variable.
\wxheading{Example}
This example shows how a main thread may launch a worker thread and wait until
it starts running:
This example shows how a main thread may launch a worker thread which starts
running and then waits until the main thread signals it to continue:
\begin{verbatim}
class MyWaitingThread : public wxThread
{
public:
MyWaitingThread(wxCondition *condition)
MyWaitingThread(wxMutex *mutex, wxCondition *condition)
{
m_mutex = mutex;
m_condition = condition;
Create();
@ -41,8 +35,11 @@ public:
virtual ExitCode Entry()
{
// let the main thread know that we started running
// wait for the signal from the main thread: it is absolutely necessary
// to look the mutex before doing it!
m_mutex->Lock();
m_condition->Signal();
m_mutex->Unlock();
... do our job ...
@ -55,15 +52,23 @@ private:
int main()
{
wxCondition condition;
MyWaitingThread *thread - new MyWaitingThread(&condition);
wxMutex mutex;
wxCondition condition(&mutex);
thread->Run();
for ( int i = 0; i < 10; i++ )
{
MyWaitingThread *thread = new MyWaitingThread(&mutex, &condition);
// wait until the thread really starts running
condition.Wait();
thread->Run();
}
...
// wake up one of the threads
condition.Signal();
// wake up all the other ones
condition.Broadcast();
... wait until they terminate or do something else ...
return 0;
}
@ -85,37 +90,57 @@ None.
\membersection{wxCondition::wxCondition}\label{wxconditionconstr}
\func{}{wxCondition}{\void}
\func{}{wxCondition}{\param{wxMutex }{*mutex}}
Default constructor.
Default and only constructor. The {\it mutex} must be non {\tt NULL}.
\membersection{wxCondition::\destruct{wxCondition}}
\func{}{\destruct{wxCondition}}{\void}
Destroys the wxCondition object.
Destroys the wxCondition object. The destructor is not virtual so this class
should not be used polymorphically.
\membersection{wxCondition::Broadcast}\label{wxconditionbroadcast}
\func{void}{Broadcast}{\void}
Broadcasts to all waiting objects.
Broadcasts to all waiting threads, waking all of them up. Note that this method
may be called whether the mutex associated with this condition is locked or
not.
\wxheading{See also}
\helpref{wxCondition::Signal}{wxconditionsignal}
\membersection{wxCondition::Signal}\label{wxconditionsignal}
\func{void}{Signal}{\void}
Signals the object.
Signals the object waking up at most one thread. If several threads are waiting
on the same condition, the exact thread which is woken up is undefined. If no
threads are waiting, the signal is lost and the condition would have to be
signalled again to wake up any thread which may start waiting on it later.
Note that this method may be called whether the mutex associated with this
condition is locked or not.
\wxheading{See also}
\helpref{wxCondition::Broadcast}{wxconditionbroadcast}
\membersection{wxCondition::Wait}\label{wxconditionwait}
\func{void}{Wait}{\void}
Waits indefinitely.
Waits until the condition is signalled.
\func{bool}{Wait}{\param{unsigned long}{ sec}, \param{unsigned long}{ nsec}}
Waits until a signal is raised or the timeout has elapsed.
Waits until the condition is signalled or the timeout has elapsed.
Note that the mutex associated with this condition {\bf must} be acquired by
the thread before calling this method.
\wxheading{Parameters}
@ -125,5 +150,7 @@ Waits until a signal is raised or the timeout has elapsed.
\wxheading{Return value}
The second form returns if the signal was raised, or FALSE if there was a timeout.
The second form returns {\tt TRUE} if the condition has been signalled, or
{\tt FALSE} if it returned because the timeout has elapsed.

View File

@ -0,0 +1,84 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Name: semaphore.tex
%% Purpose: wxSemaphore documentation
%% Author: Vadim Zeitlin
%% Modified by:
%% Created: 02.04.02
%% RCS-ID: $Id$
%% Copyright: (c) 2002 Vadim Zeitlin
%% License: wxWindows license
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{\class{wxSemaphore}}\label{wxsemaphore}
wxSemaphore is a counter limiting the number of threads concurrently accessing
a shared resource. This counter is always between $0$ and the maximum value
specified during the semaphore creation. When the counter is strictly greater
than $0$, a call to \helpref{Wait}{wxsemaphorewait} returns immediately and
decrements the counter. As soon as it reaches $0$, any subsequent calls to
\helpref{Wait}{wxsemaphorewait} block and only return when the semaphore
counter becomes strictly positive again as the result of calling
\helpref{Post}{wxsemaphorepost} which increments the counter.
In general, the semaphores are useful to restict access to a shared resource
which can only be accessed by some fixed number of clients at once. For
example, when modeling a hotel reservation system a semaphore with the counter
equal to the total number of available rooms could be created. Each time a room
is reserved, the semaphore should be acquired by calling
\helpref{Wait}{wxsemaphorewait} and each time a room is freed it should be
released by calling \helpref{Post}{wxsemaphorepost}.
\wxheading{Derived from}
No base class
\wxheading{Include files}
<wx/thread.h>
\latexignore{\rtfignore{\wxheading{Members}}}
\membersection{wxSemaphore::wxSemaphore}\label{wxsemaphorewxsemaphore}
\func{}{wxSemaphore}{\param{int }{initialcount = 0}, \param{int }{maxcount = 0}}
Specifying a {\it maxcount} of $0$ actually makes wxSemaphore behave as if
there is no upper limit. If maxcount is $1$ the semaphore behaves exactly as a
mutex.
{\it initialcount} is the initial value of the semaphore which must be between
$0$ and {\it maxcount} (if it is not set to $0$).
\membersection{wxSemaphore::\destruct{wxSemaphore}}\label{wxsemaphoredtor}
\func{}{\destruct{wxSemaphore}}{\void}
Destructor is not virtual, don't use this class polymorphically.
\membersection{wxSemaphore::Post}\label{wxsemaphorepost}
\func{void}{Post}{\void}
Increments the semaphore count and signals one of the waiting threads in an
atomic way.
\membersection{wxSemaphore::TryWait}\label{wxsemaphoretrywait}
\func{bool}{TryWait}{\void}
Same as \helpref{Wait()}{wxsemaphorewait}, but does not block, returns
{\tt TRUE} if the semaphore was successfully acquired and {\tt FALSE} if the
count is zero and it couldn't be done.
\membersection{wxSemaphore::Wait}\label{wxsemaphorewait}
\func{void}{Wait}{\void}
Wait indefinitely until the semaphore count becomes strictly positive
and then decrement it and return.
\func{bool}{Wait}{\param{unsigned long }{timeout\_millis}}
Same as the version above, but with a timeout limit: returns {\tt TRUE| if the
semaphore was acquired and {\tt FALSE} if the timeout has ellapsed

View File

@ -80,6 +80,7 @@ enum
// you should consider wxMutexLocker whenever possible instead of directly
// working with wxMutex class - it is safer
class WXDLLEXPORT wxConditionInternal;
class WXDLLEXPORT wxMutexInternal;
class WXDLLEXPORT wxMutex
{
@ -105,6 +106,8 @@ protected:
int m_locked;
wxMutexInternal *m_internal;
friend class wxConditionInternal;
};
// a helper class which locks the mutex in the ctor and unlocks it in the dtor:
@ -211,41 +214,97 @@ private:
};
// ----------------------------------------------------------------------------
// Condition variable: allows to block the thread execution until something
// happens (== condition is signaled)
// wxCondition models a POSIX condition variable which allows one (or more)
// thread(s) to wait until some condition is fulfilled
// ----------------------------------------------------------------------------
class wxConditionInternal;
class WXDLLEXPORT wxCondition
{
public:
// constructor & destructor
wxCondition();
// constructor and destructor
// Each wxCondition object is associated with with a wxMutex object The
// mutex object MUST be locked before calling Wait()
wxCondition( wxMutex *mutex );
// dtor is not virtual, don't use this class polymorphically
~wxCondition();
// wait until the condition is signaled
// waits indefinitely.
// NB: the associated mutex MUST be locked beforehand by the calling thread
//
// it atomically releases the lock on the associated mutex
// and starts waiting to be woken up by a Signal()/Broadcast()
// once its signaled, then it will wait until it can reacquire
// the lock on the associated mutex object, before returning.
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
// exactly as Wait() except that it may also return if the specified
// timeout ellapses even if the condition hasn't been signalled: in this
// case, the return value is FALSE, otherwise (i.e. in case of a normal
// return) it is TRUE
//
// the timeeout parameter specifies a interval that needs to be waited in
// milliseconds
bool Wait( unsigned long timeout_millis );
// NB: the associated mutex may or may not be locked by the calling thread
//
// this method unblocks one thread if any are blocking on the condition.
// if no thread is blocking in Wait(), then the signal is NOT remembered
// The thread which was blocking on Wait(), will then reacquire the lock
// on the associated mutex object before returning
void Signal();
// wakes up all threads waiting on this condition
void Broadcast();
#ifdef __WXDEBUG__
// for debugging purposes only
void *GetId() const { return m_internal; }
#endif // __WXDEBUG__
// NB: the associated mutex may or may not be locked by the calling thread
//
// this method unblocks all threads if any are blocking on the condition.
// if no thread is blocking in Wait(), then the signal is NOT remembered
// The threads which were blocking on Wait(), will then reacquire the lock
// on the associated mutex object before returning.
void Broadcast();
private:
wxConditionInternal *m_internal;
};
// ----------------------------------------------------------------------------
// Thread class
// wxSemaphore: a counter limiting the number of threads concurrently accessing
// a shared resource
// ----------------------------------------------------------------------------
class WXDLLEXPORT wxSemaphoreInternal;
class WXDLLEXPORT wxSemaphore
{
public:
// specifying a maxcount of 0 actually makes wxSemaphore behave as if there
// is no upper limit, if maxcount is 1 the semaphore behaves as a mutex
wxSemaphore( int initialcount = 0, int maxcount = 0 );
// dtor is not virtual, don't use this class polymorphically
~wxSemaphore();
// wait indefinitely, until the semaphore count goes beyond 0
// and then decrement it and return (this method might have been called
// Acquire())
void Wait();
// same as Wait(), but does not block, returns TRUE if successful and
// FALSE if the count is zero
bool TryWait();
// same as Wait(), but as a timeout limit, returns TRUE if the semaphore
// was acquired and FALSE if the timeout has ellapsed
bool Wait( unsigned long timeout_millis );
// increments the semaphore count and signals one of the waiting threads
void Post();
private:
wxSemaphoreInternal *m_internal;
};
// ----------------------------------------------------------------------------
// wxThread: class encpasulating a thread of execution
// ----------------------------------------------------------------------------
// there are two different kinds of threads: joinable and detached (default)
@ -540,4 +599,3 @@ public:
#endif // __THREADH__
// vi:sts=4:sw=4:et

View File

@ -146,7 +146,7 @@ public:
}
}
~wxMutexInternal() { if ( m_mutex ) CloseHandle(m_mutex); }
~wxMutexInternal() { if ( m_mutex ) ::CloseHandle(m_mutex); }
public:
HANDLE m_mutex;
@ -223,97 +223,284 @@ wxMutexError wxMutex::Unlock()
return wxMUTEX_NO_ERROR;
}
// ----------------------------------------------------------------------------
// wxCondition implementation
// ----------------------------------------------------------------------------
// ==========================================================================
// wxSemaphore
// ==========================================================================
// --------------------------------------------------------------------------
// wxSemaphoreInternal
// --------------------------------------------------------------------------
class wxSemaphoreInternal
{
public:
wxSemaphoreInternal( int initialcount = 0, int maxcount = 0 );
~wxSemaphoreInternal();
void Wait();
bool TryWait();
bool Wait( unsigned long timeout_millis );
void Post();
private:
HANDLE m_semaphore;
};
wxSemaphoreInternal::wxSemaphoreInternal( int initialcount, int maxcount )
{
if ( maxcount == 0 )
{
// make it practically infinite
maxcount = INT_MAX;
}
m_semaphore = ::CreateSemaphore( NULL, initialcount, maxcount, NULL );
if ( !m_semaphore )
{
wxLogLastError(_T("CreateSemaphore()"));
}
}
wxSemaphoreInternal::~wxSemaphoreInternal()
{
CloseHandle( m_semaphore );
}
void wxSemaphoreInternal::Wait()
{
if ( ::WaitForSingleObject( m_semaphore, INFINITE ) != WAIT_OBJECT_0 )
{
wxLogLastError(_T("WaitForSingleObject"));
}
}
bool wxSemaphoreInternal::TryWait()
{
return Wait(0);
}
bool wxSemaphoreInternal::Wait( unsigned long timeout_millis )
{
DWORD result = ::WaitForSingleObject( m_semaphore, timeout_millis );
switch ( result )
{
case WAIT_OBJECT_0:
return TRUE;
case WAIT_TIMEOUT:
break;
default:
wxLogLastError(_T("WaitForSingleObject()"));
}
return FALSE;
}
void wxSemaphoreInternal::Post()
{
if ( !::ReleaseSemaphore( m_semaphore, 1, NULL ) )
{
wxLogLastError(_T("ReleaseSemaphore"));
}
}
// --------------------------------------------------------------------------
// wxSemaphore
// --------------------------------------------------------------------------
wxSemaphore::wxSemaphore( int initialcount, int maxcount )
{
m_internal = new wxSemaphoreInternal( initialcount, maxcount );
}
wxSemaphore::~wxSemaphore()
{
delete m_internal;
}
void wxSemaphore::Wait()
{
m_internal->Wait();
}
bool wxSemaphore::TryWait()
{
return m_internal->TryWait();
}
bool wxSemaphore::Wait( unsigned long timeout_millis )
{
return m_internal->Wait( timeout_millis );
}
void wxSemaphore::Post()
{
m_internal->Post();
}
// ==========================================================================
// wxCondition
// ==========================================================================
// --------------------------------------------------------------------------
// wxConditionInternal
// --------------------------------------------------------------------------
class wxConditionInternal
{
public:
wxConditionInternal()
{
m_hEvent = ::CreateEvent(
NULL, // default secutiry
FALSE, // not manual reset
FALSE, // nonsignaled initially
NULL // nameless event
);
if ( !m_hEvent )
{
wxLogSysError(_("Can not create event object."));
}
wxConditionInternal( wxMutex *mutex );
~wxConditionInternal();
// nobody waits for us yet
m_nWaiters = 0;
}
void Wait();
bool Wait(DWORD timeout)
{
// as m_nWaiters variable is accessed from multiple waiting threads
// (and possibly from the broadcasting thread), we need to change its
// value atomically
::InterlockedIncrement(&m_nWaiters);
bool Wait( unsigned long timeout_millis );
// FIXME this should be MsgWaitForMultipleObjects() as we want to keep
// processing Windows messages while waiting (or don't we?)
DWORD rc = ::WaitForSingleObject(m_hEvent, timeout);
void Signal();
::InterlockedDecrement(&m_nWaiters);
return rc != WAIT_TIMEOUT;
}
void Signal()
{
// set the event to signaled: if a thread is already waiting on it, it
// will be woken up, otherwise the event will remain in the signaled
// state until 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(m_hEvent) )
{
wxLogLastError(wxT("SetEvent"));
}
}
void Broadcast()
{
// we need to save the original value as m_nWaiters is goign to be
// decreased by the signalled thread resulting in the loop being
// executed less times than needed
LONG nWaiters = m_nWaiters;
// 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 ( LONG n = 0; n < nWaiters; n++ )
{
Signal();
}
}
~wxConditionInternal()
{
if ( m_hEvent )
{
if ( !::CloseHandle(m_hEvent) )
{
wxLogLastError(wxT("CloseHandle(event)"));
}
}
}
void Broadcast();
private:
// the Win32 synchronization object corresponding to this event
HANDLE m_hEvent;
int m_numWaiters;
wxMutex m_mutexNumWaiters;
// number of threads waiting for this condition
LONG m_nWaiters;
wxMutex *m_mutex;
wxSemaphore m_semaphore;
};
wxCondition::wxCondition()
wxConditionInternal::wxConditionInternal( wxMutex *mutex )
{
m_internal = new wxConditionInternal;
m_mutex = mutex;
m_numWaiters = 0;
}
wxConditionInternal::~wxConditionInternal()
{
}
void wxConditionInternal::Wait()
{
// increment the number of waiters
m_mutexNumWaiters.Lock();
m_numWaiters++;
m_mutexNumWaiters.Unlock();
m_mutex->Unlock();
// a potential race condition can occur here
//
// after a thread increments nwaiters, and unlocks the mutex and before the
// semaphore.Wait() is called, if another thread can cause a signal to be
// generated
//
// this race condition is handled by using a semaphore and incrementing the
// semaphore only if 'nwaiters' is greater that zero since the semaphore,
// can 'remember' signals the race condition will not occur
// wait ( if necessary ) and decrement semaphore
m_semaphore.Wait();
m_mutex->Lock();
}
bool wxConditionInternal::Wait( unsigned long timeout_millis )
{
m_mutexNumWaiters.Lock();
m_numWaiters++;
m_mutexNumWaiters.Unlock();
m_mutex->Unlock();
// a race condition can occur at this point in the code
//
// please see the comments in Wait(), for details
bool success = TRUE;
bool result = m_semaphore.Wait( timeout_millis );
if ( !result )
{
// another potential race condition exists here it is caused when a
// 'waiting' thread timesout, and returns from WaitForSingleObject, but
// has not yet decremented 'nwaiters'.
//
// at this point if another thread calls signal() then the semaphore
// will be incremented, but the waiting thread will miss it.
//
// to handle this particular case, the waiting thread calls
// WaitForSingleObject again with a timeout of 0, after locking
// 'nwaiters_mutex'. this call does not block because of the zero
// timeout, but will allow the waiting thread to catch the missed
// signals.
m_mutexNumWaiters.Lock();
result = m_semaphore.Wait( 0 );
if ( !result )
{
m_numWaiters--;
success = FALSE;
}
m_mutexNumWaiters.Unlock();
}
m_mutex->Lock();
return success;
}
void wxConditionInternal::Signal()
{
m_mutexNumWaiters.Lock();
if ( m_numWaiters > 0 )
{
// increment the semaphore by 1
m_semaphore.Post();
m_numWaiters--;
}
m_mutexNumWaiters.Unlock();
}
void wxConditionInternal::Broadcast()
{
m_mutexNumWaiters.Lock();
while ( m_numWaiters > 0 )
{
m_semaphore.Post();
m_numWaiters--;
}
m_mutexNumWaiters.Unlock();
}
// ----------------------------------------------------------------------------
// wxCondition implementation
// ----------------------------------------------------------------------------
wxCondition::wxCondition( wxMutex *mutex )
{
if ( !mutex )
{
wxFAIL_MSG( _T("NULL mutex in wxCondition ctor") );
m_internal = NULL;
}
else
{
m_internal = new wxConditionInternal( mutex );
}
}
wxCondition::~wxCondition()
@ -323,23 +510,25 @@ wxCondition::~wxCondition()
void wxCondition::Wait()
{
(void)m_internal->Wait(INFINITE);
if ( m_internal )
m_internal->Wait();
}
bool wxCondition::Wait(unsigned long sec,
unsigned long nsec)
bool wxCondition::Wait( unsigned long timeout_millis )
{
return m_internal->Wait(sec*1000 + nsec/1000000);
return m_internal ? m_internal->Wait(timeout_millis) : FALSE;
}
void wxCondition::Signal()
{
m_internal->Signal();
if ( m_internal )
m_internal->Signal();
}
void wxCondition::Broadcast()
{
m_internal->Broadcast();
if ( m_internal )
m_internal->Broadcast();
}
// ----------------------------------------------------------------------------
@ -350,7 +539,7 @@ wxCriticalSection::wxCriticalSection()
{
#ifdef __WXDEBUG__
// Done this way to stop warnings during compilation about statement
// always being false
// always being FALSE
int csSize = sizeof(CRITICAL_SECTION);
int bSize = sizeof(m_buffer);
wxASSERT_MSG( csSize <= bSize,

View File

@ -2,13 +2,14 @@
// Name: threadpsx.cpp
// Purpose: wxThread (Posix) Implementation
// Author: Original from Wolfram Gloger/Guilhem Lavaux
// Modified by:
// Modified by: K. S. Sreeram (2002): POSIXified wxCondition, added wxSemaphore
// Created: 04/22/98
// RCS-ID: $Id$
// Copyright: (c) Wolfram Gloger (1996, 1997)
// Guilhem Lavaux (1998)
// Vadim Zeitlin (1999-2002)
// Robert Roebling (1999)
// K. S. Sreeram (2002)
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
@ -34,6 +35,7 @@
#include "wx/log.h"
#include "wx/intl.h"
#include "wx/dynarray.h"
#include "wx/timer.h"
#include <stdio.h>
#include <unistd.h>
@ -72,15 +74,6 @@ static const wxThread::ExitCode EXITCODE_CANCELLED = (wxThread::ExitCode)-1;
// our trace mask
#define TRACE_THREADS _T("thread")
// ----------------------------------------------------------------------------
// pseudo template types
// ----------------------------------------------------------------------------
WX_DECLARE_LIST(pthread_mutex_t, wxMutexList);
#include "wx/listimpl.cpp"
WX_DEFINE_LIST(wxMutexList);
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
@ -88,35 +81,6 @@ WX_DEFINE_LIST(wxMutexList);
static void ScheduleThreadForDeletion();
static void DeleteThread(wxThread *This);
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
// same as wxMutexLocker but for "native" mutex
class MutexLock
{
public:
MutexLock(pthread_mutex_t& mutex)
{
m_mutex = &mutex;
if ( pthread_mutex_lock(m_mutex) != 0 )
{
wxLogDebug(_T("pthread_mutex_lock() failed"));
}
}
~MutexLock()
{
if ( pthread_mutex_unlock(m_mutex) != 0 )
{
wxLogDebug(_T("pthread_mutex_unlock() failed"));
}
}
private:
pthread_mutex_t *m_mutex;
};
// ----------------------------------------------------------------------------
// types
// ----------------------------------------------------------------------------
@ -143,7 +107,7 @@ static pthread_key_t gs_keySelf;
static size_t gs_nThreadsBeingDeleted = 0;
// a mutex to protect gs_nThreadsBeingDeleted
static pthread_mutex_t gs_mutexDeleteThread;
static wxMutex *gs_mutexDeleteThread = (wxMutex *)NULL;
// and a condition variable which will be signaled when all
// gs_nThreadsBeingDeleted will have been deleted
@ -174,6 +138,8 @@ public:
private:
pthread_mutex_t m_mutex;
friend class wxConditionInternal;
};
wxMutexInternal::wxMutexInternal()
@ -342,174 +308,107 @@ wxMutexError wxMutex::Unlock()
return m_internal->Unlock();
}
// ============================================================================
// ===========================================================================
// wxCondition implementation
// ============================================================================
// ===========================================================================
// ----------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// wxConditionInternal
// ----------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// The native POSIX condition variables are dumb: if the condition is signaled
// before another thread starts to wait on it, the signal is lost and so this
// other thread will be never woken up. It's much more convenient to us to
// remember that the condition was signaled and to return from Wait()
// immediately in this case (this is more like Win32 automatic event objects)
class wxConditionInternal
{
public:
wxConditionInternal();
wxConditionInternal( wxMutex *mutex );
~wxConditionInternal();
// wait with the given timeout or indefinitely if NULL
bool Wait(const timespec* ts = NULL);
void Wait();
void Signal(bool all = FALSE);
bool Wait( const timespec *ts );
void Signal();
void Broadcast();
private:
// the number of Signal() calls we "missed", i.e. which were done while
// there were no threads to wait for them
size_t m_nQueuedSignals;
// counts all pending waiters
size_t m_nWaiters;
// the condition itself
pthread_cond_t m_condition;
// the mutex used with the conditon: it also protects the counters above
pthread_mutex_t m_mutex;
wxMutex *m_mutex;
pthread_cond_t m_cond;
};
wxConditionInternal::wxConditionInternal()
wxConditionInternal::wxConditionInternal( wxMutex *mutex )
{
m_nQueuedSignals =
m_nWaiters = 0;
if ( pthread_cond_init(&m_condition, (pthread_condattr_t *)NULL) != 0 )
m_mutex = mutex;
if ( pthread_cond_init( &m_cond, NULL ) != 0 )
{
// this is supposed to never happen
wxFAIL_MSG( _T("pthread_cond_init() failed") );
}
if ( pthread_mutex_init(&m_mutex, NULL) != 0 )
{
// neither this
wxFAIL_MSG( _T("wxCondition: pthread_mutex_init() failed") );
wxLogDebug(_T("pthread_cond_init() failed"));
}
}
wxConditionInternal::~wxConditionInternal()
{
if ( pthread_cond_destroy( &m_condition ) != 0 )
if ( pthread_cond_destroy( &m_cond ) != 0 )
{
wxLogDebug(_T("Failed to destroy condition variable (some "
"threads are probably still waiting on it?)"));
}
if ( pthread_mutex_destroy( &m_mutex ) != 0 )
{
wxLogDebug(_T("Failed to destroy mutex (it is probably locked)"));
wxLogDebug(_T("pthread_cond_destroy() failed"));
}
}
bool wxConditionInternal::Wait(const timespec* ts)
void wxConditionInternal::Wait()
{
MutexLock lock(m_mutex);
if ( m_nQueuedSignals )
if ( pthread_cond_wait( &m_cond, &(m_mutex->m_internal->m_mutex) ) != 0 )
{
m_nQueuedSignals--;
wxLogTrace(TRACE_THREADS,
_T("wxCondition(%08x)::Wait(): Has been signaled before"),
this);
return TRUE;
wxLogDebug(_T("pthread_cond_wait() failed"));
}
// there are no queued signals, so start really waiting
m_nWaiters++;
// calling wait function below unlocks the mutex and Signal() or
// Broadcast() will be able to continue to run now if they were
// blocking for it in the loop locking all mutexes)
wxLogTrace(TRACE_THREADS,
_T("wxCondition(%08x)::Wait(): starting to wait"), this);
int err = ts ? pthread_cond_timedwait(&m_condition, &m_mutex, ts)
: pthread_cond_wait(&m_condition, &m_mutex);
switch ( err )
{
case 0:
// condition was signaled
wxLogTrace(TRACE_THREADS,
_T("wxCondition(%08x)::Wait(): ok"), this);
break;
default:
wxLogDebug(_T("unexpected pthread_cond_[timed]wait() return"));
// fall through
case ETIMEDOUT:
case EINTR:
// The condition has not been signaled, so we have to
// decrement the counter manually
--m_nWaiters;
// wait interrupted or timeout elapsed
wxLogTrace(TRACE_THREADS,
_T("wxCondition(%08x)::Wait(): timeout/intr"), this);
}
return err == 0;
}
void wxConditionInternal::Signal(bool all)
bool wxConditionInternal::Wait( const timespec *ts )
{
// make sure that only one Signal() or Broadcast() is in progress
MutexLock lock(m_mutex);
int result = pthread_cond_timedwait( &m_cond,
&(m_mutex->m_internal->m_mutex),
ts );
if ( result == ETIMEDOUT )
return FALSE;
// Are there any waiters?
if ( m_nWaiters == 0 )
{
// No, there are not, so don't signal but keep in mind for the next
// Wait()
m_nQueuedSignals++;
wxASSERT_MSG( result == 0, _T("pthread_cond_timedwait() failed") );
return;
return TRUE;
}
void wxConditionInternal::Signal()
{
int result = pthread_cond_signal( &m_cond );
if ( result != 0 )
{
wxFAIL_MSG( _T("pthread_cond_signal() failed") );
}
}
// now we can finally signal it
wxLogTrace(TRACE_THREADS, _T("wxCondition(%08x)::Signal(): preparing to %s"),
this, all ? _T("broadcast") : _T("signal"));
void wxConditionInternal::Broadcast()
{
int result = pthread_cond_broadcast( &m_cond );
if ( result != 0 )
{
wxFAIL_MSG( _T("pthread_cond_broadcast() failed") );
}
}
int err = all ? pthread_cond_broadcast(&m_condition)
: pthread_cond_signal(&m_condition);
if ( all )
// ---------------------------------------------------------------------------
// wxCondition
// ---------------------------------------------------------------------------
wxCondition::wxCondition( wxMutex *mutex )
{
if ( !mutex )
{
m_nWaiters = 0;
wxFAIL_MSG( _T("NULL mutex in wxCondition ctor") );
m_internal = NULL;
}
else
{
--m_nWaiters;
m_internal = new wxConditionInternal( mutex );
}
if ( err )
{
// shouldn't ever happen
wxFAIL_MSG(_T("pthread_cond_{broadcast|signal}() failed"));
}
}
// ----------------------------------------------------------------------------
// wxCondition
// ----------------------------------------------------------------------------
wxCondition::wxCondition()
{
m_internal = new wxConditionInternal;
}
wxCondition::~wxCondition()
@ -519,32 +418,269 @@ wxCondition::~wxCondition()
void wxCondition::Wait()
{
(void)m_internal->Wait();
if ( m_internal )
m_internal->Wait();
}
bool wxCondition::Wait(unsigned long sec, unsigned long nsec)
bool wxCondition::Wait( unsigned long timeout_millis )
{
wxCHECK_MSG( m_internal, FALSE, _T("can't wait on uninitalized condition") );
wxLongLong curtime = wxGetLocalTimeMillis();
curtime += timeout_millis;
wxLongLong temp = curtime / 1000;
int sec = temp.GetLo();
temp = temp * 1000;
temp = curtime - temp;
int millis = temp.GetLo();
timespec tspec;
tspec.tv_sec = time(0L) + sec; // FIXME is time(0) correct here?
tspec.tv_nsec = nsec;
tspec.tv_sec = sec;
tspec.tv_nsec = millis * 1000L * 1000L;
return m_internal->Wait(&tspec);
}
void wxCondition::Signal()
{
m_internal->Signal();
if ( m_internal )
m_internal->Signal();
}
void wxCondition::Broadcast()
{
m_internal->Signal(TRUE /* all */);
if ( m_internal )
m_internal->Broadcast();
}
// ============================================================================
// ===========================================================================
// wxSemaphore implementation
// ===========================================================================
// ---------------------------------------------------------------------------
// wxSemaphoreInternal
// ---------------------------------------------------------------------------
class wxSemaphoreInternal
{
public:
wxSemaphoreInternal( int initialcount, int maxcount );
void Wait();
bool TryWait();
bool Wait( unsigned long timeout_millis );
void Post();
private:
wxMutex m_mutex;
wxCondition m_cond;
int count,
maxcount;
};
wxSemaphoreInternal::wxSemaphoreInternal( int initialcount, int maxcount )
: m_cond(m_mutex)
{
if ( (initialcount < 0) || ((maxcount > 0) && (initialcount > maxcount)) )
{
wxFAIL_MSG( _T("wxSemaphore: invalid initial count") );
}
maxcount = maxcount;
count = initialcount;
}
void wxSemaphoreInternal::Wait()
{
wxMutexLocker locker(*m_mutex);
while ( count <= 0 )
{
m_cond->Wait();
}
count--;
}
bool wxSemaphoreInternal::TryWait()
{
wxMutexLocker locker(*m_mutex);
if ( count <= 0 )
return FALSE;
count--;
return TRUE;
}
bool wxSemaphoreInternal::Wait( unsigned long timeout_millis )
{
wxMutexLocker locker( *m_mutex );
wxLongLong startTime = wxGetLocalTimeMillis();
while ( count <= 0 )
{
wxLongLong elapsed = wxGetLocalTimeMillis() - startTime;
long remainingTime = (long)timeout_millis - (long)elapsed.GetLo();
if ( remainingTime <= 0 )
return FALSE;
bool result = m_cond->Wait( remainingTime );
if ( !result )
return FALSE;
}
count--;
return TRUE;
}
void wxSemaphoreInternal::Post()
{
wxMutexLocker locker(*m_mutex);
if ( (maxcount > 0) && (count == maxcount) )
{
wxFAIL_MSG( _T("wxSemaphore::Post() overflow") );
}
count++;
m_cond->Signal();
}
// --------------------------------------------------------------------------
// wxSemaphore
// --------------------------------------------------------------------------
wxSemaphore::wxSemaphore( int initialcount, int maxcount )
{
m_internal = new wxSemaphoreInternal( initialcount, maxcount );
}
wxSemaphore::~wxSemaphore()
{
delete m_internal;
}
void wxSemaphore::Wait()
{
m_internal->Wait();
}
bool wxSemaphore::TryWait()
{
return m_internal->TryWait();
}
bool wxSemaphore::Wait( unsigned long timeout_millis )
{
return m_internal->Wait( timeout_millis );
}
void wxSemaphore::Post()
{
m_internal->Post();
}
// This class is used by wxThreadInternal to support Delete() on
// a detached thread
class wxRefCountedCondition
{
public:
// start with a initial reference count of 1
wxRefCountedCondition()
{
m_refCount = 1;
m_signaled = FALSE;
m_mutex = new wxMutex();
m_cond = new wxCondition( m_mutex );
}
// increment the reference count
void AddRef()
{
wxMutexLocker locker( *m_mutex );
m_refCount++;
}
// decrement the reference count if reference count is zero then delete the
// object
void DeleteRef()
{
bool shouldDelete = FALSE;
m_mutex->Lock();
if ( --m_refCount == 0 )
{
shouldDelete = TRUE;
}
m_mutex->Unlock();
if ( shouldDelete )
{
delete this;
}
}
// sets the object to signaled this signal will be a persistent signal all
// further Wait()s on the object will return without blocking
void SetSignaled()
{
wxMutexLocker locker( *m_mutex );
m_signaled = TRUE;
m_cond->Broadcast();
}
// wait till the object is signaled if the object was already signaled then
// return immediately
void Wait()
{
wxMutexLocker locker( *m_mutex );
if ( !m_signaled )
{
m_cond->Wait();
}
}
private:
int m_refCount;
wxMutex *m_mutex;
wxCondition *m_cond;
bool m_signaled;
// Cannot delete this object directly, call DeleteRef() instead
~wxRefCountedCondition()
{
delete m_cond;
delete m_mutex;
}
// suppress gcc warning about the class having private dtor and not having
// friend (so what??)
friend class wxDummyFriend;
};
// ===========================================================================
// wxThread implementation
// ============================================================================
// ===========================================================================
// the thread callback functions must have the C linkage
extern "C"
@ -580,7 +716,7 @@ public:
// wake up threads waiting for our termination
void SignalExit();
// wake up threads waiting for our start
void SignalRun() { m_condRun.Signal(); }
void SignalRun() { m_semRun.Post(); }
// go to sleep until Resume() is called
void Pause();
// resume the thread
@ -630,7 +766,7 @@ private:
// this flag is set when the thread should terminate
bool m_cancelled;
// this flag is set when the thread is blocking on m_condSuspend
// this flag is set when the thread is blocking on m_semSuspend
bool m_isPaused;
// the thread exit code - only used for joinable (!detached) threads and
@ -644,22 +780,18 @@ private:
bool m_shouldBroadcast;
bool m_isDetached;
// VZ: it's possible that we might do with less than three different
// condition objects - for example, m_condRun and m_condEnd a priori
// won't be used in the same time. But for now I prefer this may be a
// bit less efficient but safer solution of having distinct condition
// variables for each purpose.
// this condition is signaled by Run() and the threads Entry() is not
// this semaphore is posted by Run() and the threads Entry() is not
// called before it is done
wxCondition m_condRun;
wxSemaphore m_semRun;
// this one is signaled when the thread should resume after having been
// Pause()d
wxCondition m_condSuspend;
wxSemaphore m_semSuspend;
// finally this one is signalled when the thread exits
wxCondition m_condEnd;
// we are using a reference counted condition to support
// Delete() for a detached thread
wxRefCountedCondition *m_condEnd;
};
// ----------------------------------------------------------------------------
@ -697,8 +829,8 @@ void *wxThreadInternal::PthreadStart(wxThread *thread)
pthread_cleanup_push(wxPthreadCleanup, thread);
#endif // HAVE_THREAD_CLEANUP_FUNCTIONS
// wait for the condition to be signaled from Run()
pthread->m_condRun.Wait();
// wait for the semaphore to be posted from Run()
pthread->m_semRun.Wait();
// test whether we should run the run at all - may be it was deleted
// before it started to Run()?
@ -792,17 +924,20 @@ wxThreadInternal::wxThreadInternal()
m_threadId = 0;
m_exitcode = 0;
// set to TRUE only when the thread starts waiting on m_condSuspend
// set to TRUE only when the thread starts waiting on m_semSuspend
m_isPaused = FALSE;
// defaults for joinable threads
m_shouldBeJoined = TRUE;
m_shouldBroadcast = TRUE;
m_isDetached = FALSE;
m_condEnd = new wxRefCountedCondition();
}
wxThreadInternal::~wxThreadInternal()
{
m_condEnd->DeleteRef();
}
wxThreadError wxThreadInternal::Run()
@ -832,7 +967,15 @@ void wxThreadInternal::Wait()
// wait until the thread terminates (we're blocking in _another_ thread,
// of course)
m_condEnd.Wait();
// a reference counting condition is used to handle the
// case where a detached thread deletes itself
// before m_condEnd->Wait() returns
// in this case the deletion of the condition object is deferred until
// all Wait()ing threads have finished calling DeleteRef()
m_condEnd->AddRef();
m_condEnd->Wait();
m_condEnd->DeleteRef();
wxLogTrace(TRACE_THREADS, _T("Finished waiting for thread %ld."), id);
@ -877,7 +1020,7 @@ void wxThreadInternal::SignalExit()
wxLogTrace(TRACE_THREADS, _T("Thread %ld signals end condition."),
GetId());
m_condEnd.Broadcast();
m_condEnd->SetSignaled();
}
}
@ -890,8 +1033,8 @@ void wxThreadInternal::Pause()
wxLogTrace(TRACE_THREADS, _T("Thread %ld goes to sleep."), GetId());
// wait until the condition is signaled from Resume()
m_condSuspend.Wait();
// wait until the semaphore is Post()ed from Resume()
m_semSuspend.Wait();
}
void wxThreadInternal::Resume()
@ -906,7 +1049,7 @@ void wxThreadInternal::Resume()
wxLogTrace(TRACE_THREADS, _T("Waking up thread %ld"), GetId());
// wake up Pause()
m_condSuspend.Signal();
m_semSuspend.Post();
// reset the flag
SetReallyPaused(FALSE);
@ -1307,7 +1450,7 @@ wxThreadError wxThread::Delete(ExitCode *rc)
{
case STATE_NEW:
// we need to wake up the thread so that PthreadStart() will
// terminate - right now it's blocking on m_condRun
// terminate - right now it's blocking on m_semRun
m_internal->SignalRun();
// fall through
@ -1490,7 +1633,8 @@ wxThread::~wxThread()
// is not called for the joinable threads, so do it here
if ( !m_isDetached )
{
MutexLock lock(gs_mutexDeleteThread);
wxMutexLocker lock( *gs_mutexDeleteThread );
gs_nThreadsBeingDeleted--;
wxLogTrace(TRACE_THREADS, _T("%u scheduled for deletion threads left."),
@ -1566,9 +1710,8 @@ bool wxThreadModule::OnInit()
gs_mutexGui->Lock();
#endif // wxUSE_GUI
// under Solaris we get a warning from CC when using
// PTHREAD_MUTEX_INITIALIZER, so do it dynamically
pthread_mutex_init(&gs_mutexDeleteThread, NULL);
gs_mutexDeleteThread = new wxMutex();
gs_condAllDeleted = new wxCondition( gs_mutexDeleteThread );
return TRUE;
}
@ -1579,18 +1722,19 @@ void wxThreadModule::OnExit()
// are there any threads left which are being deleted right now?
size_t nThreadsBeingDeleted;
{
MutexLock lock(gs_mutexDeleteThread);
wxMutexLocker lock( *gs_mutexDeleteThread );
nThreadsBeingDeleted = gs_nThreadsBeingDeleted;
}
if ( nThreadsBeingDeleted > 0 )
{
wxLogTrace(TRACE_THREADS, _T("Waiting for %u threads to disappear"),
nThreadsBeingDeleted);
if ( nThreadsBeingDeleted > 0 )
{
wxLogTrace(TRACE_THREADS, _T("Waiting for %u threads to disappear"),
nThreadsBeingDeleted);
// have to wait until all of them disappear
gs_condAllDeleted->Wait();
// have to wait until all of them disappear
gs_condAllDeleted->Wait();
}
}
// terminate any threads left
@ -1617,6 +1761,9 @@ void wxThreadModule::OnExit()
// and free TLD slot
(void)pthread_key_delete(gs_keySelf);
delete gs_condAllDeleted;
delete gs_mutexDeleteThread;
}
// ----------------------------------------------------------------------------
@ -1625,12 +1772,7 @@ void wxThreadModule::OnExit()
static void ScheduleThreadForDeletion()
{
MutexLock lock(gs_mutexDeleteThread);
if ( gs_nThreadsBeingDeleted == 0 )
{
gs_condAllDeleted = new wxCondition;
}
wxMutexLocker lock( *gs_mutexDeleteThread );
gs_nThreadsBeingDeleted++;
@ -1643,17 +1785,15 @@ static void DeleteThread(wxThread *This)
{
// gs_mutexDeleteThread should be unlocked before signalling the condition
// or wxThreadModule::OnExit() would deadlock
{
MutexLock lock(gs_mutexDeleteThread);
wxMutexLocker locker( *gs_mutexDeleteThread );
wxLogTrace(TRACE_THREADS, _T("Thread %ld auto deletes."), This->GetId());
wxLogTrace(TRACE_THREADS, _T("Thread %ld auto deletes."), This->GetId());
delete This;
delete This;
wxCHECK_RET( gs_nThreadsBeingDeleted > 0,
wxCHECK_RET( gs_nThreadsBeingDeleted > 0,
_T("no threads scheduled for deletion, yet we delete "
"one?") );
}
wxLogTrace(TRACE_THREADS, _T("%u scheduled for deletion threads left."),
gs_nThreadsBeingDeleted - 1);
@ -1662,9 +1802,6 @@ static void DeleteThread(wxThread *This)
{
// no more threads left, signal it
gs_condAllDeleted->Signal();
delete gs_condAllDeleted;
gs_condAllDeleted = (wxCondition *)NULL;
}
}