1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxThread Implementation
4 // Author: Original from Wolfram Gloger/Guilhem Lavaux/Vadim Zeitlin
5 // Modified by: Aj Lavin, Stefan Csomor
8 // Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998),
9 // Vadim Zeitlin (1999) , Stefan Csomor (2000)
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
14 #pragma implementation "thread.h"
17 // ----------------------------------------------------------------------------
19 // ----------------------------------------------------------------------------
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
24 #if defined(__BORLANDC__)
34 #include "wx/module.h"
35 #include "wx/thread.h"
38 #include <CoreServices/CoreServices.h>
39 #include "wx/mac/uma.h"
43 // ----------------------------------------------------------------------------
45 // ----------------------------------------------------------------------------
47 // the possible states of the thread ("=>" shows all possible transitions from
51 STATE_NEW
, // didn't start execution yet (=> RUNNING)
52 STATE_RUNNING
, // thread is running (=> PAUSED, CANCELED)
53 STATE_PAUSED
, // thread is temporarily suspended (=> RUNNING)
54 STATE_CANCELED
, // thread should terminate a.s.a.p. (=> EXITED)
55 STATE_EXITED
// thread is terminating
58 // ----------------------------------------------------------------------------
59 // this module globals
60 // ----------------------------------------------------------------------------
62 static MPTaskID gs_idMainThread
= 0;
63 static bool gs_waitingForThread
= FALSE
;
64 size_t g_numberOfThreads
= 0;
68 MPCriticalRegionID gs_guiCritical
= 0;
72 // ============================================================================
73 // MacOS implementation of thread classes
74 // ============================================================================
84 MPEnterCriticalRegion( s_criticalId
, kDurationForever
);
88 MPExitCriticalRegion( s_criticalId
);
95 OSStatus err
= MPCreateCriticalRegion( & s_criticalId
);
98 wxLogSysError(_("Could not make the static mutex."));
102 static MPCriticalRegionID s_criticalId
;
105 MPCriticalRegionID
wxMacStCritical::s_criticalId
= 0;
107 // ----------------------------------------------------------------------------
108 // wxMutex implementation
109 // ----------------------------------------------------------------------------
111 class wxMutexInternal
114 // ALL MUTEXES WILL BE NON-RECURSIVE.
115 wxMutexInternal(wxMutexType
WXUNUSED(mutexType
))
117 OSStatus err
= MPCreateBinarySemaphore( & m_semaphore
);
119 wxLogSysError(_("Could not construct mutex."));
124 MPDeleteSemaphore( m_semaphore
);
127 bool IsOk() const { return true; }
129 wxMutexError
Lock() ;
130 wxMutexError
TryLock() ;
131 wxMutexError
Unlock();
134 MPSemaphoreID m_semaphore
;
137 wxMutexError
wxMutexInternal::Lock()
139 OSStatus err
= MPWaitOnSemaphore( m_semaphore
, kDurationForever
);
142 wxLogSysError(_("Could not lock mutex"));
143 return wxMUTEX_MISC_ERROR
;
146 return wxMUTEX_NO_ERROR
;
149 wxMutexError
wxMutexInternal::TryLock()
151 OSStatus err
= MPWaitOnSemaphore( m_semaphore
, kDurationImmediate
);
154 if ( err
== kMPTimeoutErr
)
158 wxLogSysError(_("Could not try lock mutex"));
159 return wxMUTEX_MISC_ERROR
;
162 return wxMUTEX_NO_ERROR
;
165 wxMutexError
wxMutexInternal::Unlock()
167 OSStatus err
= MPSignalSemaphore( m_semaphore
);
170 wxLogSysError(_("Could not unlock mutex"));
171 return wxMUTEX_MISC_ERROR
;
174 return wxMUTEX_NO_ERROR
;
177 // --------------------------------------------------------------------------
179 // --------------------------------------------------------------------------
181 class wxSemaphoreInternal
184 wxSemaphoreInternal(int initialcount
, int maxcount
);
185 ~wxSemaphoreInternal();
187 bool IsOk() const { return true ; }
189 wxSemaError
WaitTimeout(unsigned long milliseconds
);
191 wxSemaError
Wait() { return WaitTimeout( kDurationForever
); }
193 wxSemaError
TryWait() { return WaitTimeout(0); }
198 MPSemaphoreID m_semaphore
;
201 wxSemaphoreInternal::wxSemaphoreInternal(int initialcount
, int maxcount
)
205 // make it practically infinite
208 MPCreateSemaphore( maxcount
, initialcount
, & m_semaphore
);
211 wxSemaphoreInternal::~wxSemaphoreInternal()
213 MPDeleteSemaphore( m_semaphore
);
216 wxSemaError
wxSemaphoreInternal::WaitTimeout(unsigned long milliseconds
)
218 OSStatus err
= MPWaitOnSemaphore( m_semaphore
, milliseconds
);
221 if ( err
== kMPTimeoutErr
)
223 return wxSEMA_TIMEOUT
;
225 return wxSEMA_MISC_ERROR
;
227 return wxSEMA_NO_ERROR
;
230 wxSemaError
wxSemaphoreInternal::Post()
232 OSStatus err
= MPSignalSemaphore( m_semaphore
);
235 return wxSEMA_MISC_ERROR
;
237 return wxSEMA_NO_ERROR
;
240 // ----------------------------------------------------------------------------
241 // wxCondition implementation
242 // ----------------------------------------------------------------------------
244 class wxConditionInternal
248 wxConditionInternal(wxMutex
& mutex
)
258 ~wxConditionInternal()
262 bool IsOk() const { return m_mutex
.IsOk() ; }
266 return WaitTimeout( kDurationForever
);
269 wxCondError
WaitTimeout(unsigned long msectimeout
);
273 return DoSignal( false);
276 wxCondError
Broadcast()
278 return DoSignal( true);
283 wxCondError
DoSignal( bool signalAll
);
286 wxSemaphoreInternal m_semaphore
; // Signals the waiting threads.
287 wxSemaphoreInternal m_gate
;
288 wxCriticalSection m_varSection
;
289 size_t m_waiters
; // Number of threads waiting for a signal.
290 size_t m_signals
; // Number of signals to send.
291 size_t m_canceled
; // Number of canceled waiters in m_waiters.
295 wxCondError
wxConditionInternal::WaitTimeout(unsigned long msectimeout
)
298 if ( ++ m_waiters
== INT_MAX
)
300 m_varSection
.Enter();
301 m_waiters
-= m_canceled
;
302 m_signals
-= m_canceled
;
304 m_varSection
.Leave();
310 wxSemaError err
= m_semaphore
.WaitTimeout( msectimeout
);
311 wxASSERT( err
== wxSEMA_NO_ERROR
|| err
== wxSEMA_TIMEOUT
);
313 m_varSection
.Enter();
314 if ( err
!= wxSEMA_NO_ERROR
)
316 if ( m_signals
> m_canceled
)
318 // A signal is being sent after we timed out.
320 if ( m_waiters
== m_signals
)
322 // There are no excess waiters to catch the signal, so
323 // we must throw it away.
325 wxSemaError err2
= m_semaphore
.Wait();
326 if ( err2
!= wxSEMA_NO_ERROR
)
328 wxLogSysError(_("Error while waiting on semaphore"));
330 wxASSERT( err2
== wxSEMA_NO_ERROR
);
332 if ( -- m_signals
== m_canceled
)
334 // This was the last signal. open the gate.
335 wxASSERT( m_waiters
== m_canceled
);
341 // There are excess waiters to catch the signal, leave
348 // No signals is being sent.
349 // The gate may be open or closed, so we can't touch m_waiters.
356 // We caught a signal.
357 wxASSERT( m_signals
> m_canceled
);
359 if ( -- m_signals
== m_canceled
)
361 // This was the last signal. open the gate.
362 wxASSERT( m_waiters
== m_canceled
);
366 m_varSection
.Leave();
372 return err
== wxSEMA_TIMEOUT
? wxCOND_TIMEOUT
: wxCOND_MISC_ERROR
;
375 return wxCOND_NO_ERROR
;
379 wxCondError
wxConditionInternal::DoSignal( bool signalAll
)
382 m_varSection
.Enter();
384 wxASSERT( m_signals
== m_canceled
);
386 if ( m_waiters
== m_canceled
)
388 m_varSection
.Leave();
390 return wxCOND_NO_ERROR
;
395 m_waiters
-= m_canceled
;
400 m_signals
= signalAll
? m_waiters
: 1;
401 size_t n
= m_signals
;
403 m_varSection
.Leave();
405 // Let the waiters inherit the gate lock.
409 wxSemaError err
= m_semaphore
.Post();
410 wxASSERT( err
== wxSEMA_NO_ERROR
);
413 return wxCOND_NO_ERROR
;
418 // ----------------------------------------------------------------------------
419 // wxCriticalSection implementation
420 // ----------------------------------------------------------------------------
422 // XXX currently implemented as mutex in headers. Change to critical section.
424 // ----------------------------------------------------------------------------
425 // wxThread implementation
426 // ----------------------------------------------------------------------------
428 // wxThreadInternal class
429 // ----------------------
431 class wxThreadInternal
438 m_priority
= WXTHREAD_DEFAULT_PRIORITY
;
444 if ( m_notifyQueueId
)
445 MPDeleteQueue( m_notifyQueueId
);
452 // create a new (suspended) thread (for the given thread object)
453 // stacksize == 0 will give the default stack size of 4 KB.
454 bool Create(wxThread
*thread
, unsigned int stackSize
);
458 // suspend/resume/terminate
463 wxCriticalSectionLocker
lock( m_critical
);
464 m_state
= STATE_CANCELED
;
467 wxThread::ExitCode
Wait();
470 void SetState(wxThreadState state
)
472 wxCriticalSectionLocker
lock( m_critical
);
476 wxThreadState
GetState() const
478 wxCriticalSectionLocker
lock( m_critical
);
483 void SetPriority(unsigned int priority
);
484 unsigned int GetPriority() const
486 wxCriticalSectionLocker
lock( m_critical
);
490 void SetResult( void *res
)
492 wxCriticalSectionLocker
lock( m_critical
);
498 wxCriticalSectionLocker
lock( m_critical
);
502 // Get the ID of this thread's underlying MP Services task.
503 MPTaskID
GetId() const
505 wxCriticalSectionLocker
lock( m_critical
);
510 static OSStatus
MacThreadStart(void* arg
);
514 wxThreadState m_state
; // state, see wxThreadState enum
515 unsigned int m_priority
; // thread priority in "wx" units
516 MPTaskID m_tid
; // thread id
519 mutable wxCriticalSection m_critical
;
520 MPQueueID m_notifyQueueId
;
521 unsigned int m_stackSize
;
525 static wxArrayPtrVoid s_threads
;
527 OSStatus
wxThreadInternal::MacThreadStart(void *parameter
)
529 wxThread
* thread
= (wxThread
*) parameter
;
530 // first of all, check whether we hadn't been cancelled already
531 if ( thread
->m_internal
->GetState() == STATE_EXITED
)
533 return kMPDeletedErr
;
538 thread
->m_critsect
.Enter();
539 bool wasCancelled
= thread
->m_internal
->GetState() == STATE_CANCELED
;
540 thread
->m_internal
->SetState(STATE_EXITED
);
541 thread
->m_critsect
.Leave();
545 // If the thread was cancelled (from Delete()), then the handle is still
547 if ( thread
->IsDetached() && !wasCancelled
)
551 //else: the joinable threads handle will be closed when Wait() is done
556 void wxThreadInternal::SetPriority(unsigned int priority
)
558 wxASSERT_MSG( priority
>= 0, _T("Thread priority must be at least 0."));
559 wxASSERT_MSG( priority
<= 100, _T("Thread priority must be at most 100."));
561 wxCriticalSectionLocker
lock(m_critical
);
563 m_priority
= priority
;
567 // Mac priorities range from 1 to 10,000, with a default of 100.
568 // wxWindows priorities range from 0 to 100 with a default of 50.
569 // We can map wxWindows to Mac priorities easily by assuming
570 // the former uses a logarithmic scale.
571 const unsigned int macPriority
= ( int)( exp( priority
/ 25.0 * log( 10.0)) + 0.5);
573 MPSetTaskWeight( m_tid
, macPriority
);
578 void wxThreadInternal::Run()
584 err
= MPCreateTask( MacThreadStart
,
595 wxLogSysError(_("Can't create thread"));
600 if ( m_priority
!= WXTHREAD_DEFAULT_PRIORITY
)
602 SetPriority(m_priority
);
605 m_state
= STATE_RUNNING
;
609 bool wxThreadInternal::Create(wxThread
*thread
, unsigned int stackSize
)
611 m_stackSize
= stackSize
;
614 if ( ! m_notifyQueueId
)
616 OSStatus err
= MPCreateQueue( & m_notifyQueueId
);
619 wxLogSysError(_("Cant create the thread event queue"));
627 bool wxThreadInternal::Suspend()
629 wxCriticalSectionLocker
lock(m_critical
);
631 if ( m_state
!= STATE_RUNNING
)
633 wxLogSysError(_("Can not suspend thread %x"), m_tid
);
637 m_state
= STATE_PAUSED
;
639 OSStatus err
= MPThrowException( m_tid
, kMPTaskStoppedErr
);
643 wxLogSysError(_("Can not suspend thread %x"), m_tid
);
650 bool wxThreadInternal::Resume()
652 wxCriticalSectionLocker
lock(m_critical
);
654 if ( m_state
!= STATE_PAUSED
&& m_state
!= STATE_NEW
)
656 wxLogSysError(_("Can not resume thread %x"), m_tid
);
660 OSStatus err
= MPDisposeTaskException( m_tid
, kMPTaskResumeMask
);
663 wxLogSysError(_("Cannot resume thread %x"), m_tid
);
667 m_state
= STATE_RUNNING
;
673 wxThread::ExitCode
wxThreadInternal::Wait()
679 OSStatus err
= MPWaitOnQueue ( m_notifyQueueId
,
686 wxLogSysError( _( "Cannot wait on thread to exit."));
687 return ( wxThread::ExitCode
)-1;
692 return ( wxThread::ExitCode
)rc
;
698 wxThread
*wxThread::This()
700 MPTaskID current
= MPCurrentTaskID();
703 wxMacStCritical critical
;
705 for ( size_t i
= 0 ; i
< s_threads
.Count() ; ++i
)
707 if ( ( (wxThread
*) s_threads
[i
] )->GetId() == (unsigned long)current
)
708 return (wxThread
*) s_threads
[i
] ;
712 wxLogSysError(_("Couldn't get the current thread pointer"));
716 bool wxThread::IsMain()
718 return MPCurrentTaskID() == gs_idMainThread
;
725 void wxThread::Yield()
727 #if TARGET_API_MAC_OSX
728 CFRunLoopRunInMode( kCFRunLoopDefaultMode
, 0 , true ) ;
734 void wxThread::Sleep(unsigned long milliseconds
)
736 AbsoluteTime wakeup
= AddDurationToAbsolute( milliseconds
, UpTime());
737 MPDelayUntil( & wakeup
);
741 int wxThread::GetCPUCount()
743 return MPProcessors();
746 unsigned long wxThread::GetCurrentId()
748 return (unsigned long)MPCurrentTaskID();
753 bool wxThread::SetConcurrency(size_t level
)
759 wxThread::wxThread(wxThreadKind kind
)
762 m_internal
= new wxThreadInternal();
764 m_isDetached
= kind
== wxTHREAD_DETACHED
;
767 wxMacStCritical critical
;
768 s_threads
.Add( (void*) this) ;
772 wxThread::~wxThread()
774 if (g_numberOfThreads
>0)
781 wxFAIL_MSG(wxT("More threads deleted than created."));
786 wxMacStCritical critical
;
787 s_threads
.Remove( (void*) this ) ;
790 if (m_internal
!= NULL
) {
797 wxThreadError
wxThread::Create(unsigned int stackSize
)
799 wxCriticalSectionLocker
lock(m_critsect
);
801 if ( !m_internal
->Create(this, stackSize
) )
802 return wxTHREAD_NO_RESOURCE
;
804 return wxTHREAD_NO_ERROR
;
807 wxThreadError
wxThread::Run()
811 return wxTHREAD_NO_ERROR
;
814 wxThreadError
wxThread::Pause()
816 wxCriticalSectionLocker
lock(m_critsect
);
817 return m_internal
->Suspend() ? wxTHREAD_NO_ERROR
: wxTHREAD_MISC_ERROR
;
820 wxThreadError
wxThread::Resume()
822 wxCriticalSectionLocker
lock(m_critsect
);
823 return m_internal
->Resume() ? wxTHREAD_NO_ERROR
: wxTHREAD_MISC_ERROR
;
827 wxThread::ExitCode
wxThread::Wait()
829 // although under MacOS we can wait for any thread, it's an error to
830 // wait for a detached one in wxWin API
831 wxCHECK_MSG( !IsDetached(),
833 _T("can't wait for detached thread") );
845 wxThreadError
wxThread::Delete( ExitCode
*pRc
)
847 // Delete() is always safe to call, so consider all possible states
849 bool shouldResume
= FALSE
;
852 wxCriticalSectionLocker
lock(m_critsect
);
854 if ( m_internal
->GetState() == STATE_NEW
)
856 // MacThreadStart() will see it and terminate immediately
857 m_internal
->SetState(STATE_EXITED
);
863 if ( shouldResume
|| IsPaused() )
871 wxMacStCritical critical
;
872 gs_waitingForThread
= TRUE
;
880 wxCriticalSectionLocker
lock(m_critsect
);
881 m_internal
->Cancel();
884 while( TestDestroy() )
892 wxMacStCritical critical
;
893 gs_waitingForThread
= FALSE
;
902 * pRc
= (ExitCode
)m_internal
->GetResult();
906 // If the thread exits normally, this is done in
907 // MachreadStart, but in this case it would have been too
908 // early, so we must do it here.
912 if ( pRc
&& * pRc
== (ExitCode
)-1)
913 return wxTHREAD_MISC_ERROR
;
914 return wxTHREAD_NO_ERROR
;
917 wxThreadError
wxThread::Kill()
920 return wxTHREAD_NOT_RUNNING
;
922 if ( MPTerminateTask( m_internal
->GetId(), -1) )
924 wxLogSysError(_("Couldn't terminate thread"));
926 return wxTHREAD_MISC_ERROR
;
936 return wxTHREAD_NO_ERROR
;
939 void wxThread::Exit(ExitCode status
)
949 m_internal
->SetResult( status
) ;
953 void wxThread::SetPriority(unsigned int prio
)
955 m_internal
->SetPriority(prio
);
958 unsigned int wxThread::GetPriority() const
960 return m_internal
->GetPriority();
963 unsigned long wxThread::GetId() const
965 return (unsigned long)m_internal
->GetId();
968 bool wxThread::IsRunning() const
970 return m_internal
->GetState() == STATE_RUNNING
;
973 bool wxThread::IsAlive() const
975 return (m_internal
->GetState() == STATE_RUNNING
) ||
976 (m_internal
->GetState() == STATE_PAUSED
);
979 bool wxThread::IsPaused() const
981 return m_internal
->GetState() == STATE_PAUSED
;
984 bool wxThread::TestDestroy()
986 return m_internal
->GetState() == STATE_CANCELED
;
989 // ----------------------------------------------------------------------------
990 // Automatic initialization for thread module
991 // ----------------------------------------------------------------------------
993 class wxThreadModule
: public wxModule
996 virtual bool OnInit();
997 virtual void OnExit();
1000 DECLARE_DYNAMIC_CLASS(wxThreadModule
)
1003 IMPLEMENT_DYNAMIC_CLASS(wxThreadModule
, wxModule
)
1005 bool wxThreadModule::OnInit()
1007 bool hasThreadManager
;
1008 hasThreadManager
= MPLibraryIsLoaded();
1012 // verify presence of shared library
1013 hasThreadManager
= hasThreadManager
&& ((Ptr
)NewThread
!= (Ptr
)kUnresolvedCFragSymbolAddress
);
1016 if ( !hasThreadManager
)
1018 wxMessageBox( "Error" , "Thread Support is not available on this System" , wxOK
) ;
1022 gs_idMainThread
= MPCurrentTaskID();
1026 OSStatus err
= MPCreateCriticalRegion( & gs_guiCritical
);
1029 wxLogSysError(_("Could not make the GUI critical region."));
1032 // XXX wxMac never Exits the GUI Mutex.
1033 // XXX MPEnterCriticalRegion( gs_guiCritical, kDurationForever);
1039 void wxThreadModule::OnExit()
1042 MPExitCriticalRegion( gs_guiCritical
);
1044 MPDeleteCriticalRegion( gs_guiCritical
);
1049 void WXDLLEXPORT
wxMutexGuiEnter()
1052 MPEnterCriticalRegion( gs_guiCritical
, kDurationForever
);
1056 void WXDLLEXPORT
wxMutexGuiLeave()
1059 MPExitCriticalRegion( gs_guiCritical
);
1065 void WXDLLEXPORT
wxMutexGuiLeaveOrEnter()
1070 bool WXDLLEXPORT
wxGuiOwnedByMainThread()
1075 // wake up the main thread
1076 void WXDLLEXPORT
wxWakeUpMainThread()
1081 bool WXDLLEXPORT
wxIsWaitingForThread()
1083 return gs_waitingForThread
;
1086 #include "wx/thrimpl.cpp"
1088 #endif // wxUSE_THREADS