]> git.saurik.com Git - wxWidgets.git/blob - src/mac/thread.cpp
Size updates and new methods to export.
[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);
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 public :
339 };
340
341 static wxArrayPtrVoid s_threads ;
342
343 ThreadEntryUPP wxThreadInternal::s_threadEntry = NULL ;
344 pascal 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 }
373 void wxThreadInternal::SetPriority(unsigned int priority)
374 {
375 // Priorities don't exist on Mac
376 }
377
378 bool 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
406 bool 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
426 bool 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 // ----------------
454 wxThread *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
473 bool 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
486 void wxThread::Yield()
487 {
488 ::YieldToAnyThread() ;
489 }
490
491 void 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
500 int wxThread::GetCPUCount()
501 {
502 // we will use whatever MP API will be used for the new MP Macs
503 return 1;
504 }
505
506 bool 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
528 wxThread::wxThread(wxThreadKind kind)
529 {
530 m_internal = new wxThreadInternal();
531
532 m_isDetached = kind == wxTHREAD_DETACHED;
533 s_threads.Add( (void*) this ) ;
534 }
535
536 wxThread::~wxThread()
537 {
538 s_threads.Remove( (void*) this ) ;
539 delete m_internal;
540 }
541
542 // create/start thread
543 // -------------------
544
545 wxThreadError 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
555 wxThreadError 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
572 wxThreadError wxThread::Pause()
573 {
574 wxCriticalSectionLocker lock(m_critsect);
575
576 return m_internal->Suspend() ? wxTHREAD_NO_ERROR : wxTHREAD_MISC_ERROR;
577 }
578
579 wxThreadError 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
589 wxThread::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
605 wxThreadError 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
699 wxThreadError 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
721 void 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
747 void wxThread::SetPriority(unsigned int prio)
748 {
749 m_internal->SetPriority(prio);
750 }
751
752 unsigned int wxThread::GetPriority() const
753 {
754 return m_internal->GetPriority();
755 }
756
757 unsigned long wxThread::GetId() const
758 {
759 return (unsigned long)m_internal->GetId();
760 }
761
762 bool wxThread::IsRunning() const
763 {
764 return m_internal->GetState() == STATE_RUNNING;
765 }
766
767 bool wxThread::IsAlive() const
768 {
769 return (m_internal->GetState() == STATE_RUNNING) ||
770 (m_internal->GetState() == STATE_PAUSED);
771 }
772
773 bool wxThread::IsPaused() const
774 {
775 return m_internal->GetState() == STATE_PAUSED;
776 }
777
778 bool wxThread::TestDestroy()
779 {
780 return m_internal->GetState() == STATE_CANCELED;
781 }
782
783 // ----------------------------------------------------------------------------
784 // Automatic initialization for thread module
785 // ----------------------------------------------------------------------------
786
787 class wxThreadModule : public wxModule
788 {
789 public:
790 virtual bool OnInit();
791 virtual void OnExit();
792
793 private:
794 DECLARE_DYNAMIC_CLASS(wxThreadModule)
795 };
796
797 IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
798
799 bool 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
822 void 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
831 void WXDLLEXPORT wxMutexGuiEnter()
832 {
833 }
834
835 void WXDLLEXPORT wxMutexGuiLeave()
836 {
837 }
838
839 void WXDLLEXPORT wxMutexGuiLeaveOrEnter()
840 {
841 }
842
843 bool WXDLLEXPORT wxGuiOwnedByMainThread()
844 {
845 return false ;
846 }
847
848 // wake up the main thread
849 void WXDLLEXPORT wxWakeUpMainThread()
850 {
851 wxMacWakeUp() ;
852 }
853
854 bool WXDLLEXPORT wxIsWaitingForThread()
855 {
856 return false ;
857 }
858
859 #endif // wxUSE_THREADS
860