]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/mac/carbon/thread.cpp
- better behaviour of wxMGL's wxTimer w.r.t. memory allocations
[wxWidgets.git] / src / mac / carbon / thread.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: thread.cpp
3// Purpose: wxThread Implementation
4// Author: Original from Wolfram Gloger/Guilhem Lavaux/Vadim Zeitlin
5// Modified by: Stefan Csomor
6// Created: 04/22/98
7// RCS-ID: $Id$
8// Copyright: (c) Wolfram Gloger (1996, 1997); Guilhem Lavaux (1998),
9// Vadim Zeitlin (1999) , Stefan Csomor (2000)
10// Licence: wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13#ifdef __GNUG__
14 #pragma implementation "thread.h"
15#endif
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
20
21// For compilers that support precompilation, includes "wx.h".
22#include "wx/wxprec.h"
23
24#if defined(__BORLANDC__)
25 #pragma hdrstop
26#endif
27
28#ifndef WX_PRECOMP
29 #include "wx/wx.h"
30#endif
31
32#if wxUSE_THREADS
33
34#include "wx/module.h"
35#include "wx/thread.h"
36
37// ----------------------------------------------------------------------------
38// constants
39// ----------------------------------------------------------------------------
40
41// the possible states of the thread ("=>" shows all possible transitions from
42// this state)
43enum wxThreadState
44{
45 STATE_NEW, // didn't start execution yet (=> RUNNING)
46 STATE_RUNNING, // thread is running (=> PAUSED, CANCELED)
47 STATE_PAUSED, // thread is temporarily suspended (=> RUNNING)
48 STATE_CANCELED, // thread should terminate a.s.a.p. (=> EXITED)
49 STATE_EXITED // thread is terminating
50};
51
52// ----------------------------------------------------------------------------
53// this module globals
54// ----------------------------------------------------------------------------
55
56static ThreadID gs_idMainThread = kNoThreadID ;
57static bool gs_waitingForThread = FALSE ;
58
59// ============================================================================
60// MacOS implementation of thread classes
61// ============================================================================
62
63class wxMacStCritical
64{
65public :
66 wxMacStCritical()
67 {
68 ThreadBeginCritical() ;
69 }
70 ~wxMacStCritical()
71 {
72 ThreadEndCritical() ;
73 }
74} ;
75
76// ----------------------------------------------------------------------------
77// wxMutex implementation
78// ----------------------------------------------------------------------------
79
80class wxMutexInternal
81{
82public:
83 wxMutexInternal()
84 {
85 m_owner = kNoThreadID ;
86 }
87
88 ~wxMutexInternal()
89 {
90 }
91
92public:
93 ThreadID m_owner ;
94 wxArrayLong m_waiters ;
95};
96
97wxMutex::wxMutex()
98{
99 m_internal = new wxMutexInternal;
100
101 m_locked = 0;
102}
103
104wxMutex::~wxMutex()
105{
106 if ( m_locked > 0 )
107 {
108 wxLogDebug(_T("Warning: freeing a locked mutex (%d locks)."), m_locked);
109 }
110
111 delete m_internal;
112}
113
114wxMutexError wxMutex::Lock()
115{
116 wxMacStCritical critical ;
117
118 OSErr err ;
119 ThreadID current = kNoThreadID;
120 err = ::MacGetCurrentThread(&current);
121 // if we are not the owner, add this thread to the list of waiting threads, stop this thread
122 // and invoke the scheduler to continue executing the owner's thread
123 while ( m_internal->m_owner != kNoThreadID && m_internal->m_owner != current)
124 {
125 m_internal->m_waiters.Add(current);
126 err = ::SetThreadStateEndCritical(kCurrentThreadID, kStoppedThreadState, m_internal->m_owner);
127 err = ::ThreadBeginCritical();
128 }
129 m_internal->m_owner = current;
130 m_locked++;
131
132 return wxMUTEX_NO_ERROR;
133}
134
135wxMutexError wxMutex::TryLock()
136{
137 wxMacStCritical critical ;
138
139 OSErr err ;
140 ThreadID current = kNoThreadID;
141 ::MacGetCurrentThread(&current);
142 // if we are not the owner, give an error back
143 if ( m_internal->m_owner != kNoThreadID && m_internal->m_owner != current )
144 return wxMUTEX_BUSY;
145
146 m_internal->m_owner = current;
147 m_locked++;
148
149 return wxMUTEX_NO_ERROR;
150}
151
152wxMutexError wxMutex::Unlock()
153{
154 OSErr err;
155 err = ::ThreadBeginCritical();
156
157 if (m_locked > 0)
158 m_locked--;
159
160 // this mutex is not owned by anybody anmore
161 m_internal->m_owner = kNoThreadID;
162
163 // now pass on to the first waiting thread
164 ThreadID firstWaiting = kNoThreadID;
165 bool found = false;
166 while (!m_internal->m_waiters.IsEmpty() && !found)
167 {
168 firstWaiting = m_internal->m_waiters[0];
169 err = ::SetThreadState(firstWaiting, kReadyThreadState, kNoThreadID);
170 // in case this was not successful (dead thread), we just loop on and reset the id
171 found = (err != threadNotFoundErr);
172 if ( !found )
173 firstWaiting = kNoThreadID ;
174 m_internal->m_waiters.RemoveAt(0) ;
175 }
176 // now we have a valid firstWaiting thread, which has been scheduled to run next, just end the
177 // critical section and invoke the scheduler
178 err = ::SetThreadStateEndCritical(kCurrentThreadID, kReadyThreadState, firstWaiting);
179
180 return wxMUTEX_NO_ERROR;
181}
182
183// ----------------------------------------------------------------------------
184// wxCondition implementation
185// ----------------------------------------------------------------------------
186
187class wxConditionInternal
188{
189public:
190 wxConditionInternal()
191 {
192 m_excessSignals = 0 ;
193 }
194 ~wxConditionInternal()
195 {
196 }
197
198 bool Wait(unsigned long msectimeout)
199 {
200 wxMacStCritical critical ;
201 if ( m_excessSignals > 0 )
202 {
203 --m_excessSignals ;
204 return TRUE ;
205 }
206 else if ( msectimeout == 0 )
207 {
208 return FALSE ;
209 }
210 else
211 {
212 }
213 /*
214 waiters++;
215
216 // FIXME this should be MsgWaitForMultipleObjects() as well probably
217 DWORD rc = ::WaitForSingleObject(event, timeout);
218
219 waiters--;
220
221 return rc != WAIT_TIMEOUT;
222 */
223 return TRUE ;
224 }
225 void Signal()
226 {
227 wxMacStCritical critical ;
228 }
229
230 wxArrayLong m_waiters ;
231 wxInt32 m_excessSignals ;
232};
233
234wxCondition::wxCondition()
235{
236 m_internal = new wxConditionInternal;
237}
238
239wxCondition::~wxCondition()
240{
241 delete m_internal;
242}
243
244void wxCondition::Wait()
245{
246 (void)m_internal->Wait(0xFFFFFFFFL );
247}
248
249bool wxCondition::Wait(unsigned long sec,
250 unsigned long nsec)
251{
252 return m_internal->Wait(sec*1000 + nsec/1000000);
253}
254
255void wxCondition::Signal()
256{
257 // set the event to signaled: if a thread is already waiting on it, it will
258 // be woken up, otherwise the event will remain in the signaled state until
259 // someone waits on it. In any case, the system will return it to a non
260 // signalled state afterwards. If multiple threads are waiting, only one
261 // will be woken up.
262 m_internal->Signal() ;
263}
264
265void wxCondition::Broadcast()
266{
267 // this works because all these threads are already waiting and so each
268 // SetEvent() inside Signal() is really a PulseEvent() because the event
269 // state is immediately returned to non-signaled
270 for ( int i = 0; i < m_internal->m_waiters.Count(); i++ )
271 {
272 Signal();
273 }
274}
275
276// ----------------------------------------------------------------------------
277// wxCriticalSection implementation
278// ----------------------------------------------------------------------------
279
280// it's implemented as a mutex on mac os, so it is defined in the headers
281
282// ----------------------------------------------------------------------------
283// wxThread implementation
284// ----------------------------------------------------------------------------
285
286// wxThreadInternal class
287// ----------------------
288
289class wxThreadInternal
290{
291public:
292 wxThreadInternal()
293 {
294 m_tid = kNoThreadID ;
295 m_state = STATE_NEW;
296 m_priority = WXTHREAD_DEFAULT_PRIORITY;
297 }
298
299 ~wxThreadInternal()
300 {
301 }
302
303 void Free()
304 {
305 }
306
307 // create a new (suspended) thread (for the given thread object)
308 bool Create(wxThread *thread);
309
310 // suspend/resume/terminate
311 bool Suspend();
312 bool Resume();
313 void Cancel() { m_state = STATE_CANCELED; }
314
315 // thread state
316 void SetState(wxThreadState state) { m_state = state; }
317 wxThreadState GetState() const { return m_state; }
318
319 // thread priority
320 void SetPriority(unsigned int priority);
321 unsigned int GetPriority() const { return m_priority; }
322
323 void SetResult( void *res ) { m_result = res ; }
324 void *GetResult() { return m_result ; }
325
326 // thread handle and id
327 ThreadID GetId() const { return m_tid; }
328
329 // thread function
330 static pascal void* MacThreadStart(wxThread* arg);
331
332private:
333 wxThreadState m_state; // state, see wxThreadState enum
334 unsigned int m_priority; // thread priority in "wx" units
335 ThreadID m_tid; // thread id
336 void * m_result ;
337 static ThreadEntryUPP s_threadEntry ;
338public :
339};
340
341static wxArrayPtrVoid s_threads ;
342
343ThreadEntryUPP wxThreadInternal::s_threadEntry = NULL ;
344pascal void* wxThreadInternal::MacThreadStart(wxThread *thread)
345{
346 // first of all, check whether we hadn't been cancelled already
347 if ( thread->m_internal->GetState() == STATE_EXITED )
348 {
349 return (void*)-1;
350 }
351
352 void* rc = thread->Entry();
353
354 // enter m_critsect before changing the thread state
355 thread->m_critsect.Enter();
356 bool wasCancelled = thread->m_internal->GetState() == STATE_CANCELED;
357 thread->m_internal->SetState(STATE_EXITED);
358 thread->m_critsect.Leave();
359
360 thread->OnExit();
361
362 // if the thread was cancelled (from Delete()), then it the handle is still
363 // needed there
364 if ( thread->IsDetached() && !wasCancelled )
365 {
366 // auto delete
367 delete thread;
368 }
369 //else: the joinable threads handle will be closed when Wait() is done
370
371 return rc;
372}
373void wxThreadInternal::SetPriority(unsigned int priority)
374{
375 // Priorities don't exist on Mac
376}
377
378bool wxThreadInternal::Create(wxThread *thread)
379{
380 if ( s_threadEntry == NULL )
381 {
382 s_threadEntry = NewThreadEntryUPP( (ThreadEntryProcPtr) MacThreadStart ) ;
383 }
384 OSErr err = NewThread(kCooperativeThread,
385 s_threadEntry,
386 (void*) thread ,
387 0 ,
388 kNewSuspend ,
389 &m_result ,
390 &m_tid ) ;
391
392 if ( err != noErr )
393 {
394 wxLogSysError(_("Can't create thread"));
395 return FALSE;
396 }
397
398 if ( m_priority != WXTHREAD_DEFAULT_PRIORITY )
399 {
400 SetPriority(m_priority);
401 }
402
403 return TRUE;
404}
405
406bool wxThreadInternal::Suspend()
407{
408 OSErr err ;
409
410 ::ThreadBeginCritical();
411
412 if ( m_state != STATE_RUNNING )
413 {
414 ::ThreadEndCritical() ;
415 wxLogSysError(_("Can not suspend thread %x"), m_tid);
416 return FALSE;
417 }
418
419 m_state = STATE_PAUSED;
420
421 err = ::SetThreadStateEndCritical(m_tid, kStoppedThreadState, kNoThreadID);
422
423 return TRUE;
424}
425
426bool wxThreadInternal::Resume()
427{
428 ThreadID current ;
429 OSErr err ;
430 err = MacGetCurrentThread( &current ) ;
431
432 wxASSERT( err == noErr ) ;
433 wxASSERT( current != m_tid ) ;
434
435 ::ThreadBeginCritical();
436 if ( m_state != STATE_PAUSED && m_state != STATE_NEW )
437 {
438 ::ThreadEndCritical() ;
439 wxLogSysError(_("Can not resume thread %x"), m_tid);
440 return FALSE;
441
442 }
443 err = ::SetThreadStateEndCritical(m_tid, kReadyThreadState, kNoThreadID);
444 wxASSERT( err == noErr ) ;
445
446 m_state = STATE_RUNNING;
447 ::ThreadEndCritical() ;
448 ::YieldToAnyThread() ;
449 return TRUE;
450}
451
452// static functions
453// ----------------
454wxThread *wxThread::This()
455{
456 wxMacStCritical critical ;
457
458 ThreadID current ;
459 OSErr err ;
460
461 err = MacGetCurrentThread( &current ) ;
462
463 for ( int i = 0 ; i < s_threads.Count() ; ++i )
464 {
465 if ( ( (wxThread*) s_threads[i] )->GetId() == current )
466 return (wxThread*) s_threads[i] ;
467 }
468
469 wxLogSysError(_("Couldn't get the current thread pointer"));
470 return NULL;
471}
472
473bool wxThread::IsMain()
474{
475 ThreadID current ;
476 OSErr err ;
477
478 err = MacGetCurrentThread( &current ) ;
479 return current == gs_idMainThread;
480}
481
482#ifdef Yield
483#undef Yield
484#endif
485
486void wxThread::Yield()
487{
488 ::YieldToAnyThread() ;
489}
490
491void wxThread::Sleep(unsigned long milliseconds)
492{
493 clock_t start = clock() ;
494 do
495 {
496 YieldToAnyThread() ;
497 } while( clock() - start < milliseconds / CLOCKS_PER_SEC ) ;
498}
499
500int wxThread::GetCPUCount()
501{
502 // we will use whatever MP API will be used for the new MP Macs
503 return 1;
504}
505
506bool wxThread::SetConcurrency(size_t level)
507{
508 wxASSERT_MSG( IsMain(), _T("should only be called from the main thread") );
509
510 // ok only for the default one
511 if ( level == 0 )
512 return 0;
513
514 // how many CPUs have we got?
515 if ( GetCPUCount() == 1 )
516 {
517 // don't bother with all this complicated stuff - on a single
518 // processor system it doesn't make much sense anyhow
519 return level == 1;
520 }
521
522 return TRUE ;
523}
524
525// ctor and dtor
526// -------------
527
528wxThread::wxThread(wxThreadKind kind)
529{
530 m_internal = new wxThreadInternal();
531
532 m_isDetached = kind == wxTHREAD_DETACHED;
533 s_threads.Add( (void*) this ) ;
534}
535
536wxThread::~wxThread()
537{
538 s_threads.Remove( (void*) this ) ;
539 delete m_internal;
540}
541
542// create/start thread
543// -------------------
544
545wxThreadError wxThread::Create()
546{
547 wxCriticalSectionLocker lock(m_critsect);
548
549 if ( !m_internal->Create(this) )
550 return wxTHREAD_NO_RESOURCE;
551
552 return wxTHREAD_NO_ERROR;
553}
554
555wxThreadError wxThread::Run()
556{
557 wxCriticalSectionLocker lock(m_critsect);
558
559 if ( m_internal->GetState() != STATE_NEW )
560 {
561 // actually, it may be almost any state at all, not only STATE_RUNNING
562 return wxTHREAD_RUNNING;
563 }
564
565 // the thread has just been created and is still suspended - let it run
566 return Resume();
567}
568
569// suspend/resume thread
570// ---------------------
571
572wxThreadError wxThread::Pause()
573{
574 wxCriticalSectionLocker lock(m_critsect);
575
576 return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
577}
578
579wxThreadError wxThread::Resume()
580{
581 wxCriticalSectionLocker lock(m_critsect);
582
583 return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
584}
585
586// stopping thread
587// ---------------
588
589wxThread::ExitCode wxThread::Wait()
590{
591 // although under MacOS we can wait for any thread, it's an error to
592 // wait for a detached one in wxWin API
593 wxCHECK_MSG( !IsDetached(), (ExitCode)-1,
594 _T("can't wait for detached thread") );
595
596 ExitCode rc = (ExitCode)-1;
597
598 (void)Delete(&rc);
599
600 m_internal->Free();
601
602 return rc;
603}
604
605wxThreadError wxThread::Delete(ExitCode *pRc)
606{
607 ExitCode rc = 0;
608
609 // Delete() is always safe to call, so consider all possible states
610
611 // has the thread started to run?
612 bool shouldResume = FALSE;
613
614 {
615 wxCriticalSectionLocker lock(m_critsect);
616
617 if ( m_internal->GetState() == STATE_NEW )
618 {
619 // WinThreadStart() will see it and terminate immediately
620 m_internal->SetState(STATE_EXITED);
621
622 shouldResume = TRUE;
623 }
624 }
625
626 // is the thread paused?
627 if ( shouldResume || IsPaused() )
628 Resume();
629
630 // does is still run?
631 if ( IsRunning() )
632 {
633 if ( IsMain() )
634 {
635 // set flag for wxIsWaitingForThread()
636 gs_waitingForThread = TRUE;
637
638#if wxUSE_GUI
639 wxBeginBusyCursor();
640#endif // wxUSE_GUI
641 }
642
643 // ask the thread to terminate
644 {
645 wxCriticalSectionLocker lock(m_critsect);
646
647 m_internal->Cancel();
648 }
649
650#if wxUSE_GUI
651 // simply wait for the thread to terminate
652 while( TestDestroy() )
653 {
654 ::YieldToAnyThread() ;
655 }
656#else // !wxUSE_GUI
657 // simply wait for the thread to terminate
658 while( TestDestroy() )
659 {
660 ::YieldToAnyThread() ;
661 }
662#endif // wxUSE_GUI/!wxUSE_GUI
663
664 if ( IsMain() )
665 {
666 gs_waitingForThread = FALSE;
667
668#if wxUSE_GUI
669 wxEndBusyCursor();
670#endif // wxUSE_GUI
671 }
672 }
673
674 // if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) )
675 {
676 wxLogLastError("GetExitCodeThread");
677
678 rc = (ExitCode)-1;
679 }
680
681 if ( IsDetached() )
682 {
683 // if the thread exits normally, this is done in WinThreadStart, but in
684 // this case it would have been too early because
685 // MsgWaitForMultipleObject() would fail if the therad handle was
686 // closed while we were waiting on it, so we must do it here
687 delete this;
688 }
689
690 // wxASSERT_MSG( (DWORD)rc != STILL_ACTIVE,
691 // wxT("thread must be already terminated.") );
692
693 if ( pRc )
694 *pRc = rc;
695
696 return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR;
697}
698
699wxThreadError wxThread::Kill()
700{
701 if ( !IsRunning() )
702 return wxTHREAD_NOT_RUNNING;
703
704// if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) )
705 {
706 wxLogSysError(_("Couldn't terminate thread"));
707
708 return wxTHREAD_MISC_ERROR;
709 }
710
711 m_internal->Free();
712
713 if ( IsDetached() )
714 {
715 delete this;
716 }
717
718 return wxTHREAD_NO_ERROR;
719}
720
721void wxThread::Exit(ExitCode status)
722{
723 m_internal->Free();
724
725 if ( IsDetached() )
726 {
727 delete this;
728 }
729
730 m_internal->SetResult( status ) ;
731
732/*
733#if defined(__VISUALC__) || (defined(__BORLANDC__) && (__BORLANDC__ >= 0x500))
734 _endthreadex((unsigned)status);
735#else // !VC++
736 ::ExitThread((DWORD)status);
737#endif // VC++/!VC++
738*/
739 wxFAIL_MSG(wxT("Couldn't return from ExitThread()!"));
740}
741
742// priority setting
743// ----------------
744
745// since all these calls are execute cooperatively we don't have to use the critical section
746
747void wxThread::SetPriority(unsigned int prio)
748{
749 m_internal->SetPriority(prio);
750}
751
752unsigned int wxThread::GetPriority() const
753{
754 return m_internal->GetPriority();
755}
756
757unsigned long wxThread::GetId() const
758{
759 return (unsigned long)m_internal->GetId();
760}
761
762bool wxThread::IsRunning() const
763{
764 return m_internal->GetState() == STATE_RUNNING;
765}
766
767bool wxThread::IsAlive() const
768{
769 return (m_internal->GetState() == STATE_RUNNING) ||
770 (m_internal->GetState() == STATE_PAUSED);
771}
772
773bool wxThread::IsPaused() const
774{
775 return m_internal->GetState() == STATE_PAUSED;
776}
777
778bool wxThread::TestDestroy()
779{
780 return m_internal->GetState() == STATE_CANCELED;
781}
782
783// ----------------------------------------------------------------------------
784// Automatic initialization for thread module
785// ----------------------------------------------------------------------------
786
787class wxThreadModule : public wxModule
788{
789public:
790 virtual bool OnInit();
791 virtual void OnExit();
792
793private:
794 DECLARE_DYNAMIC_CLASS(wxThreadModule)
795};
796
797IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
798
799bool wxThreadModule::OnInit()
800{
801 long response;
802 bool hasThreadManager ;
803 hasThreadManager = Gestalt( gestaltThreadMgrAttr, &response) == noErr && response & 1;
804#if !TARGET_CARBON
805#if GENERATINGCFM
806 // verify presence of shared library
807 hasThreadManager = hasThreadManager && ((Ptr)NewThread != (Ptr)kUnresolvedCFragSymbolAddress);
808#endif
809#endif
810 if ( !hasThreadManager )
811 {
812 wxMessageBox( "Error" , "Thread Support is not available on this System" , wxOK ) ;
813 return FALSE ;
814 }
815
816 // no error return for GetCurrentThreadId()
817 MacGetCurrentThread( &gs_idMainThread ) ;
818
819 return TRUE;
820}
821
822void wxThreadModule::OnExit()
823{
824}
825
826// ----------------------------------------------------------------------------
827// under MacOS we don't have currently preemptive threads, so any thread may access
828// the GUI at any time
829// ----------------------------------------------------------------------------
830
831void WXDLLEXPORT wxMutexGuiEnter()
832{
833}
834
835void WXDLLEXPORT wxMutexGuiLeave()
836{
837}
838
839void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
840{
841}
842
843bool WXDLLEXPORT wxGuiOwnedByMainThread()
844{
845 return false ;
846}
847
848// wake up the main thread
849void WXDLLEXPORT wxWakeUpMainThread()
850{
851 wxMacWakeUp() ;
852}
853
854bool WXDLLEXPORT wxIsWaitingForThread()
855{
856 return false ;
857}
858
859#endif // wxUSE_THREADS
860