]> git.saurik.com Git - wxWidgets.git/blob - src/mac/thread.cpp
Added (and documented :) optional stack size specification for wxThread.
[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 // ----------------------------------------------------------------------------
38 // constants
39 // ----------------------------------------------------------------------------
40
41 // the possible states of the thread ("=>" shows all possible transitions from
42 // this state)
43 enum 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
56 static ThreadID gs_idMainThread = kNoThreadID ;
57 static bool gs_waitingForThread = FALSE ;
58
59 // ============================================================================
60 // MacOS implementation of thread classes
61 // ============================================================================
62
63 class wxMacStCritical
64 {
65 public :
66 wxMacStCritical()
67 {
68 ThreadBeginCritical() ;
69 }
70 ~wxMacStCritical()
71 {
72 ThreadEndCritical() ;
73 }
74 };
75
76 // ----------------------------------------------------------------------------
77 // wxMutex implementation
78 // ----------------------------------------------------------------------------
79
80 class wxMutexInternal
81 {
82 public:
83 wxMutexInternal()
84 {
85 m_owner = kNoThreadID ;
86 }
87
88 ~wxMutexInternal()
89 {
90 }
91
92 public:
93 ThreadID m_owner ;
94 wxArrayLong m_waiters ;
95 };
96
97 wxMutex::wxMutex()
98 {
99 m_internal = new wxMutexInternal;
100
101 m_locked = 0;
102 }
103
104 wxMutex::~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
114 wxMutexError 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
135 wxMutexError 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
152 wxMutexError 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
187 class wxConditionInternal
188 {
189 public:
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
234 wxCondition::wxCondition()
235 {
236 m_internal = new wxConditionInternal;
237 }
238
239 wxCondition::~wxCondition()
240 {
241 delete m_internal;
242 }
243
244 void wxCondition::Wait()
245 {
246 (void)m_internal->Wait(0xFFFFFFFFL);
247 }
248
249 bool wxCondition::Wait(unsigned long sec,
250 unsigned long nsec)
251 {
252 return m_internal->Wait(sec*1000 + nsec/1000000);
253 }
254
255 void 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
265 void 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
289 class wxThreadInternal
290 {
291 public:
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, unsigned int stackSize);
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
332 private:
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 ;
338 };
339
340 static wxArrayPtrVoid s_threads ;
341
342 ThreadEntryUPP wxThreadInternal::s_threadEntry = NULL ;
343 pascal void* wxThreadInternal::MacThreadStart(wxThread *thread)
344 {
345 // first of all, check whether we hadn't been cancelled already
346 if ( thread->m_internal->GetState() == STATE_EXITED )
347 {
348 return (void*)-1;
349 }
350
351 void* rc = thread->Entry();
352
353 // enter m_critsect before changing the thread state
354 thread->m_critsect.Enter();
355 bool wasCancelled = thread->m_internal->GetState() == STATE_CANCELED;
356 thread->m_internal->SetState(STATE_EXITED);
357 thread->m_critsect.Leave();
358
359 thread->OnExit();
360
361 // if the thread was cancelled (from Delete()), then it the handle is still
362 // needed there
363 if ( thread->IsDetached() && !wasCancelled )
364 {
365 // auto delete
366 delete thread;
367 }
368 //else: the joinable threads handle will be closed when Wait() is done
369
370 return rc;
371 }
372 void wxThreadInternal::SetPriority(unsigned int priority)
373 {
374 // Priorities don't exist on Mac
375 }
376
377 bool wxThreadInternal::Create(wxThread *thread, unsigned int stackSize)
378 {
379 if ( s_threadEntry == NULL )
380 {
381 s_threadEntry = NewThreadEntryUPP( (ThreadEntryProcPtr) MacThreadStart ) ;
382 }
383 OSErr err = NewThread( kCooperativeThread,
384 s_threadEntry,
385 (void*) thread,
386 stackSize,
387 kNewSuspend,
388 &m_result,
389 &m_tid );
390
391 if ( err != noErr )
392 {
393 wxLogSysError(_("Can't create thread"));
394 return FALSE;
395 }
396
397 if ( m_priority != WXTHREAD_DEFAULT_PRIORITY )
398 {
399 SetPriority(m_priority);
400 }
401
402 return TRUE;
403 }
404
405 bool wxThreadInternal::Suspend()
406 {
407 OSErr err ;
408
409 ::ThreadBeginCritical();
410
411 if ( m_state != STATE_RUNNING )
412 {
413 ::ThreadEndCritical() ;
414 wxLogSysError(_("Can not suspend thread %x"), m_tid);
415 return FALSE;
416 }
417
418 m_state = STATE_PAUSED;
419
420 err = ::SetThreadStateEndCritical(m_tid, kStoppedThreadState, kNoThreadID);
421
422 return TRUE;
423 }
424
425 bool wxThreadInternal::Resume()
426 {
427 ThreadID current ;
428 OSErr err ;
429 err = MacGetCurrentThread( &current ) ;
430
431 wxASSERT( err == noErr ) ;
432 wxASSERT( current != m_tid ) ;
433
434 ::ThreadBeginCritical();
435 if ( m_state != STATE_PAUSED && m_state != STATE_NEW )
436 {
437 ::ThreadEndCritical() ;
438 wxLogSysError(_("Can not resume thread %x"), m_tid);
439 return FALSE;
440
441 }
442 err = ::SetThreadStateEndCritical(m_tid, kReadyThreadState, kNoThreadID);
443 wxASSERT( err == noErr ) ;
444
445 m_state = STATE_RUNNING;
446 ::ThreadEndCritical() ;
447 ::YieldToAnyThread() ;
448 return TRUE;
449 }
450
451 // static functions
452 // ----------------
453 wxThread *wxThread::This()
454 {
455 wxMacStCritical critical ;
456
457 ThreadID current ;
458 OSErr err ;
459
460 err = MacGetCurrentThread( &current ) ;
461
462 for ( int i = 0 ; i < s_threads.Count() ; ++i )
463 {
464 if ( ( (wxThread*) s_threads[i] )->GetId() == current )
465 return (wxThread*) s_threads[i] ;
466 }
467
468 wxLogSysError(_("Couldn't get the current thread pointer"));
469 return NULL;
470 }
471
472 bool wxThread::IsMain()
473 {
474 ThreadID current ;
475 OSErr err ;
476
477 err = MacGetCurrentThread( &current ) ;
478 return current == gs_idMainThread;
479 }
480
481 #ifdef Yield
482 #undef Yield
483 #endif
484
485 void wxThread::Yield()
486 {
487 ::YieldToAnyThread() ;
488 }
489
490 void wxThread::Sleep(unsigned long milliseconds)
491 {
492 clock_t start = clock() ;
493 do
494 {
495 YieldToAnyThread() ;
496 } while( clock() - start < milliseconds / CLOCKS_PER_SEC ) ;
497 }
498
499 int wxThread::GetCPUCount()
500 {
501 // we will use whatever MP API will be used for the new MP Macs
502 return 1;
503 }
504
505 bool wxThread::SetConcurrency(size_t level)
506 {
507 wxASSERT_MSG( IsMain(), _T("should only be called from the main thread") );
508
509 // ok only for the default one
510 if ( level == 0 )
511 return 0;
512
513 // how many CPUs have we got?
514 if ( GetCPUCount() == 1 )
515 {
516 // don't bother with all this complicated stuff - on a single
517 // processor system it doesn't make much sense anyhow
518 return level == 1;
519 }
520
521 return TRUE ;
522 }
523
524 // ctor and dtor
525 // -------------
526
527 wxThread::wxThread(wxThreadKind kind)
528 {
529 m_internal = new wxThreadInternal();
530
531 m_isDetached = kind == wxTHREAD_DETACHED;
532 s_threads.Add( (void*) this ) ;
533 }
534
535 wxThread::~wxThread()
536 {
537 s_threads.Remove( (void*) this ) ;
538 delete m_internal;
539 }
540
541 // create/start thread
542 // -------------------
543
544 wxThreadError wxThread::Create(unsigned int stackSize)
545 {
546 wxCriticalSectionLocker lock(m_critsect);
547
548 if ( !m_internal->Create(this, stackSize) )
549 return wxTHREAD_NO_RESOURCE;
550
551 return wxTHREAD_NO_ERROR;
552 }
553
554 wxThreadError wxThread::Run()
555 {
556 wxCriticalSectionLocker lock(m_critsect);
557
558 if ( m_internal->GetState() != STATE_NEW )
559 {
560 // actually, it may be almost any state at all, not only STATE_RUNNING
561 return wxTHREAD_RUNNING;
562 }
563
564 // the thread has just been created and is still suspended - let it run
565 return Resume();
566 }
567
568 // suspend/resume thread
569 // ---------------------
570
571 wxThreadError wxThread::Pause()
572 {
573 wxCriticalSectionLocker lock(m_critsect);
574
575 return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
576 }
577
578 wxThreadError wxThread::Resume()
579 {
580 wxCriticalSectionLocker lock(m_critsect);
581
582 return m_internal->Resume() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
583 }
584
585 // stopping thread
586 // ---------------
587
588 wxThread::ExitCode wxThread::Wait()
589 {
590 // although under MacOS we can wait for any thread, it's an error to
591 // wait for a detached one in wxWin API
592 wxCHECK_MSG( !IsDetached(), (ExitCode)-1,
593 _T("can't wait for detached thread") );
594
595 ExitCode rc = (ExitCode)-1;
596
597 (void)Delete(&rc);
598
599 m_internal->Free();
600
601 return rc;
602 }
603
604 wxThreadError wxThread::Delete(ExitCode *pRc)
605 {
606 ExitCode rc = 0;
607
608 // Delete() is always safe to call, so consider all possible states
609
610 // has the thread started to run?
611 bool shouldResume = FALSE;
612
613 {
614 wxCriticalSectionLocker lock(m_critsect);
615
616 if ( m_internal->GetState() == STATE_NEW )
617 {
618 // WinThreadStart() will see it and terminate immediately
619 m_internal->SetState(STATE_EXITED);
620
621 shouldResume = TRUE;
622 }
623 }
624
625 // is the thread paused?
626 if ( shouldResume || IsPaused() )
627 Resume();
628
629 // does is still run?
630 if ( IsRunning() )
631 {
632 if ( IsMain() )
633 {
634 // set flag for wxIsWaitingForThread()
635 gs_waitingForThread = TRUE;
636
637 #if wxUSE_GUI
638 wxBeginBusyCursor();
639 #endif // wxUSE_GUI
640 }
641
642 // ask the thread to terminate
643 {
644 wxCriticalSectionLocker lock(m_critsect);
645
646 m_internal->Cancel();
647 }
648
649 #if wxUSE_GUI
650 // simply wait for the thread to terminate
651 while( TestDestroy() )
652 {
653 ::YieldToAnyThread() ;
654 }
655 #else // !wxUSE_GUI
656 // simply wait for the thread to terminate
657 while( TestDestroy() )
658 {
659 ::YieldToAnyThread() ;
660 }
661 #endif // wxUSE_GUI/!wxUSE_GUI
662
663 if ( IsMain() )
664 {
665 gs_waitingForThread = FALSE;
666
667 #if wxUSE_GUI
668 wxEndBusyCursor();
669 #endif // wxUSE_GUI
670 }
671 }
672
673 // if ( !::GetExitCodeThread(hThread, (LPDWORD)&rc) )
674 {
675 wxLogLastError("GetExitCodeThread");
676
677 rc = (ExitCode)-1;
678 }
679
680 if ( IsDetached() )
681 {
682 // if the thread exits normally, this is done in WinThreadStart, but in
683 // this case it would have been too early because
684 // MsgWaitForMultipleObject() would fail if the therad handle was
685 // closed while we were waiting on it, so we must do it here
686 delete this;
687 }
688
689 // wxASSERT_MSG( (DWORD)rc != STILL_ACTIVE,
690 // wxT("thread must be already terminated.") );
691
692 if ( pRc )
693 *pRc = rc;
694
695 return rc == (ExitCode)-1 ? wxTHREAD_MISC_ERROR : wxTHREAD_NO_ERROR;
696 }
697
698 wxThreadError wxThread::Kill()
699 {
700 if ( !IsRunning() )
701 return wxTHREAD_NOT_RUNNING;
702
703 // if ( !::TerminateThread(m_internal->GetHandle(), (DWORD)-1) )
704 {
705 wxLogSysError(_("Couldn't terminate thread"));
706
707 return wxTHREAD_MISC_ERROR;
708 }
709
710 m_internal->Free();
711
712 if ( IsDetached() )
713 {
714 delete this;
715 }
716
717 return wxTHREAD_NO_ERROR;
718 }
719
720 void wxThread::Exit(ExitCode status)
721 {
722 m_internal->Free();
723
724 if ( IsDetached() )
725 {
726 delete this;
727 }
728
729 m_internal->SetResult( status ) ;
730
731 /*
732 #if defined(__VISUALC__) || (defined(__BORLANDC__) && (__BORLANDC__ >= 0x500))
733 _endthreadex((unsigned)status);
734 #else // !VC++
735 ::ExitThread((DWORD)status);
736 #endif // VC++/!VC++
737 */
738 wxFAIL_MSG(wxT("Couldn't return from ExitThread()!"));
739 }
740
741 // priority setting
742 // ----------------
743
744 // since all these calls are execute cooperatively we don't have to use the critical section
745
746 void wxThread::SetPriority(unsigned int prio)
747 {
748 m_internal->SetPriority(prio);
749 }
750
751 unsigned int wxThread::GetPriority() const
752 {
753 return m_internal->GetPriority();
754 }
755
756 unsigned long wxThread::GetId() const
757 {
758 return (unsigned long)m_internal->GetId();
759 }
760
761 bool wxThread::IsRunning() const
762 {
763 return m_internal->GetState() == STATE_RUNNING;
764 }
765
766 bool wxThread::IsAlive() const
767 {
768 return (m_internal->GetState() == STATE_RUNNING) ||
769 (m_internal->GetState() == STATE_PAUSED);
770 }
771
772 bool wxThread::IsPaused() const
773 {
774 return m_internal->GetState() == STATE_PAUSED;
775 }
776
777 bool wxThread::TestDestroy()
778 {
779 return m_internal->GetState() == STATE_CANCELED;
780 }
781
782 // ----------------------------------------------------------------------------
783 // Automatic initialization for thread module
784 // ----------------------------------------------------------------------------
785
786 class wxThreadModule : public wxModule
787 {
788 public:
789 virtual bool OnInit();
790 virtual void OnExit();
791
792 private:
793 DECLARE_DYNAMIC_CLASS(wxThreadModule)
794 };
795
796 IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
797
798 bool wxThreadModule::OnInit()
799 {
800 long response;
801 bool hasThreadManager ;
802 hasThreadManager = Gestalt( gestaltThreadMgrAttr, &response) == noErr && response & 1;
803 #if !TARGET_CARBON
804 #if GENERATINGCFM
805 // verify presence of shared library
806 hasThreadManager = hasThreadManager && ((Ptr)NewThread != (Ptr)kUnresolvedCFragSymbolAddress);
807 #endif
808 #endif
809 if ( !hasThreadManager )
810 {
811 wxMessageBox( "Error" , "Thread Support is not available on this System" , wxOK ) ;
812 return FALSE ;
813 }
814
815 // no error return for GetCurrentThreadId()
816 MacGetCurrentThread( &gs_idMainThread ) ;
817
818 return TRUE;
819 }
820
821 void wxThreadModule::OnExit()
822 {
823 }
824
825 // ----------------------------------------------------------------------------
826 // under MacOS we don't have currently preemptive threads, so any thread may access
827 // the GUI at any time
828 // ----------------------------------------------------------------------------
829
830 void WXDLLEXPORT wxMutexGuiEnter()
831 {
832 }
833
834 void WXDLLEXPORT wxMutexGuiLeave()
835 {
836 }
837
838 void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
839 {
840 }
841
842 bool WXDLLEXPORT wxGuiOwnedByMainThread()
843 {
844 return false ;
845 }
846
847 // wake up the main thread
848 void WXDLLEXPORT wxWakeUpMainThread()
849 {
850 wxMacWakeUp() ;
851 }
852
853 bool WXDLLEXPORT wxIsWaitingForThread()
854 {
855 return false ;
856 }
857
858 #endif // wxUSE_THREADS
859
860 // vi:sts=4:sw=4:et