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