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