]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/unix/threadpsx.cpp
Fixed fatal typo.
[wxWidgets.git] / src / unix / threadpsx.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: threadpsx.cpp
3// Purpose: wxThread (Posix) Implementation
4// Author: Original from Wolfram Gloger/Guilhem Lavaux
5// Modified by:
6// Created: 04/22/98
7// RCS-ID: $Id$
8// Copyright: (c) Wolfram Gloger (1996, 1997)
9// Guilhem Lavaux (1998)
10// Vadim Zeitlin (1999)
11// Robert Roebling (1999)
12// Licence: wxWindows licence
13/////////////////////////////////////////////////////////////////////////////
14
15// ============================================================================
16// declaration
17// ============================================================================
18
19// ----------------------------------------------------------------------------
20// headers
21// ----------------------------------------------------------------------------
22
23#ifdef __GNUG__
24 #pragma implementation "thread.h"
25#endif
26
27// With simple makefiles, we must ignore the file body if not using
28// threads.
29#include "wx/setup.h"
30
31#if wxUSE_THREADS
32
33#include "wx/thread.h"
34#include "wx/module.h"
35#include "wx/utils.h"
36#include "wx/log.h"
37#include "wx/intl.h"
38#include "wx/dynarray.h"
39
40#include <stdio.h>
41#include <unistd.h>
42#include <pthread.h>
43#include <errno.h>
44#include <time.h>
45
46#if HAVE_SCHED_H
47 #include <sched.h>
48#endif
49
50// ----------------------------------------------------------------------------
51// constants
52// ----------------------------------------------------------------------------
53
54// the possible states of the thread and transitions from them
55enum wxThreadState
56{
57 STATE_NEW, // didn't start execution yet (=> RUNNING)
58 STATE_RUNNING, // running (=> PAUSED or EXITED)
59 STATE_PAUSED, // suspended (=> RUNNING or EXITED)
60 STATE_EXITED // thread doesn't exist any more
61};
62
63// ----------------------------------------------------------------------------
64// types
65// ----------------------------------------------------------------------------
66
67WX_DEFINE_ARRAY(wxThread *, wxArrayThread);
68
69// -----------------------------------------------------------------------------
70// global data
71// -----------------------------------------------------------------------------
72
73// we keep the list of all threads created by the application to be able to
74// terminate them on exit if there are some left - otherwise the process would
75// be left in memory
76static wxArrayThread gs_allThreads;
77
78// the id of the main thread
79static pthread_t gs_tidMain;
80
81// the key for the pointer to the associated wxThread object
82static pthread_key_t gs_keySelf;
83
84// this mutex must be acquired before any call to a GUI function
85static wxMutex *gs_mutexGui;
86
87// ============================================================================
88// implementation
89// ============================================================================
90
91//--------------------------------------------------------------------
92// wxMutex (Posix implementation)
93//--------------------------------------------------------------------
94
95class wxMutexInternal
96{
97public:
98 pthread_mutex_t p_mutex;
99};
100
101wxMutex::wxMutex()
102{
103 p_internal = new wxMutexInternal;
104 pthread_mutex_init( &(p_internal->p_mutex), (const pthread_mutexattr_t*) NULL );
105 m_locked = 0;
106}
107
108wxMutex::~wxMutex()
109{
110 if (m_locked > 0)
111 wxLogDebug(_T("Freeing a locked mutex (%d locks)"), m_locked);
112
113 pthread_mutex_destroy( &(p_internal->p_mutex) );
114 delete p_internal;
115}
116
117wxMutexError wxMutex::Lock()
118{
119 int err = pthread_mutex_lock( &(p_internal->p_mutex) );
120 if (err == EDEADLK)
121 {
122 wxLogDebug(_T("Locking this mutex would lead to deadlock!"));
123
124 return wxMUTEX_DEAD_LOCK;
125 }
126
127 m_locked++;
128
129 return wxMUTEX_NO_ERROR;
130}
131
132wxMutexError wxMutex::TryLock()
133{
134 if (m_locked)
135 {
136 return wxMUTEX_BUSY;
137 }
138
139 int err = pthread_mutex_trylock( &(p_internal->p_mutex) );
140 switch (err)
141 {
142 case EBUSY: return wxMUTEX_BUSY;
143 }
144
145 m_locked++;
146
147 return wxMUTEX_NO_ERROR;
148}
149
150wxMutexError wxMutex::Unlock()
151{
152 if (m_locked > 0)
153 {
154 m_locked--;
155 }
156 else
157 {
158 wxLogDebug(_T("Unlocking not locked mutex."));
159
160 return wxMUTEX_UNLOCKED;
161 }
162
163 pthread_mutex_unlock( &(p_internal->p_mutex) );
164
165 return wxMUTEX_NO_ERROR;
166}
167
168//--------------------------------------------------------------------
169// wxCondition (Posix implementation)
170//--------------------------------------------------------------------
171
172class wxConditionInternal
173{
174public:
175 pthread_cond_t p_condition;
176};
177
178wxCondition::wxCondition()
179{
180 p_internal = new wxConditionInternal;
181 pthread_cond_init( &(p_internal->p_condition), (const pthread_condattr_t *) NULL );
182}
183
184wxCondition::~wxCondition()
185{
186 pthread_cond_destroy( &(p_internal->p_condition) );
187
188 delete p_internal;
189}
190
191void wxCondition::Wait(wxMutex& mutex)
192{
193 pthread_cond_wait( &(p_internal->p_condition), &(mutex.p_internal->p_mutex) );
194}
195
196bool wxCondition::Wait(wxMutex& mutex, unsigned long sec, unsigned long nsec)
197{
198 struct timespec tspec;
199
200 tspec.tv_sec = time(0L)+sec;
201 tspec.tv_nsec = nsec;
202 return (pthread_cond_timedwait(&(p_internal->p_condition), &(mutex.p_internal->p_mutex), &tspec) != ETIMEDOUT);
203}
204
205void wxCondition::Signal()
206{
207 pthread_cond_signal( &(p_internal->p_condition) );
208}
209
210void wxCondition::Broadcast()
211{
212 pthread_cond_broadcast( &(p_internal->p_condition) );
213}
214
215//--------------------------------------------------------------------
216// wxThread (Posix implementation)
217//--------------------------------------------------------------------
218
219class wxThreadInternal
220{
221public:
222 wxThreadInternal();
223 ~wxThreadInternal();
224
225 // thread entry function
226 static void *PthreadStart(void *ptr);
227
228#if HAVE_THREAD_CLEANUP_FUNCTIONS
229 // thread exit function
230 static void PthreadCleanup(void *ptr);
231#endif
232
233 // thread actions
234 // start the thread
235 wxThreadError Run();
236 // ask the thread to terminate
237 void Wait();
238 // wake up threads waiting for our termination
239 void SignalExit();
240 // go to sleep until Resume() is called
241 void Pause();
242 // resume the thread
243 void Resume();
244
245 // accessors
246 // priority
247 int GetPriority() const { return m_prio; }
248 void SetPriority(int prio) { m_prio = prio; }
249 // state
250 wxThreadState GetState() const { return m_state; }
251 void SetState(wxThreadState state) { m_state = state; }
252 // id
253 pthread_t GetId() const { return m_threadId; }
254 pthread_t *GetIdPtr() { return &m_threadId; }
255 // "cancelled" flag
256 void SetCancelFlag() { m_cancelled = TRUE; }
257 bool WasCancelled() const { return m_cancelled; }
258
259private:
260 pthread_t m_threadId; // id of the thread
261 wxThreadState m_state; // see wxThreadState enum
262 int m_prio; // in wxWindows units: from 0 to 100
263
264 // set when the thread should terminate
265 bool m_cancelled;
266
267 // this (mutex, cond) pair is used to synchronize the main thread and this
268 // thread in several situations:
269 // 1. The thread function blocks until condition is signaled by Run() when
270 // it's initially created - this allows thread creation in "suspended"
271 // state
272 // 2. The Delete() function blocks until the condition is signaled when the
273 // thread exits.
274 // GL: On Linux, this may fail because we can have a deadlock in either
275 // SignalExit() or Wait(): so we add m_end_mutex for the finalization.
276 wxMutex m_mutex, m_end_mutex;
277 wxCondition m_cond;
278
279 // another (mutex, cond) pair for Pause()/Resume() usage
280 //
281 // VZ: it's possible that we might reuse the mutex and condition from above
282 // for this too, but as I'm not at all sure that it won't create subtle
283 // problems with race conditions between, say, Pause() and Delete() I
284 // prefer this may be a bit less efficient but much safer solution
285 wxMutex m_mutexSuspend;
286 wxCondition m_condSuspend;
287};
288
289void *wxThreadInternal::PthreadStart(void *ptr)
290{
291 wxThread *thread = (wxThread *)ptr;
292 wxThreadInternal *pthread = thread->p_internal;
293 void *status;
294
295 int rc = pthread_setspecific(gs_keySelf, thread);
296 if ( rc != 0 )
297 {
298 wxLogSysError(rc, _("Cannot start thread: error writing TLS"));
299
300 return (void *)-1;
301 }
302#if HAVE_THREAD_CLEANUP_FUNCTIONS
303 // Install the cleanup handler.
304 pthread_cleanup_push(wxThreadInternal::PthreadCleanup, ptr);
305#endif
306
307 // wait for the condition to be signaled from Run()
308 // mutex state: currently locked by the thread which created us
309 pthread->m_cond.Wait(pthread->m_mutex);
310 // mutex state: locked again on exit of Wait()
311
312 // call the main entry
313 status = thread->Entry();
314
315#if HAVE_THREAD_CLEANUP_FUNCTIONS
316 pthread_cleanup_pop(FALSE);
317#endif
318
319 // terminate the thread
320 thread->Exit(status);
321
322 wxFAIL_MSG(_T("wxThread::Exit() can't return."));
323
324 return NULL;
325}
326
327#if HAVE_THREAD_CLEANUP_FUNCTIONS
328// Only called when the thread is explicitely killed.
329
330void wxThreadInternal::PthreadCleanup(void *ptr)
331{
332 wxThread *thread = (wxThread *) ptr;
333
334 // The thread is already considered as finished.
335 if (thread->p_internal->GetState() == STATE_EXITED)
336 return;
337
338 // first call user-level clean up code
339 thread->OnExit();
340
341 // next wake up the threads waiting for us (OTOH, this function won't retur
342 // until someone waited for us!)
343 thread->p_internal->SetState(STATE_EXITED);
344
345 thread->p_internal->SignalExit();
346}
347#endif
348
349wxThreadInternal::wxThreadInternal()
350{
351 m_state = STATE_NEW;
352 m_cancelled = FALSE;
353
354 // this mutex is locked during almost all thread lifetime - it will only be
355 // unlocked in the very end
356 m_mutex.Lock();
357
358 // this mutex is used by wxThreadInternal::Wait() and by
359 // wxThreadInternal::SignalExit(). We don't use m_mutex because of a
360 // possible deadlock in either Wait() or SignalExit().
361 m_end_mutex.Lock();
362
363 // this mutex is used in Pause()/Resume() and is also locked all the time
364 // unless the thread is paused
365 m_mutexSuspend.Lock();
366}
367
368wxThreadInternal::~wxThreadInternal()
369{
370 // GL: moved to SignalExit
371 // m_mutexSuspend.Unlock();
372
373 // note that m_mutex will be unlocked by the thread which waits for our
374 // termination
375
376 // In the case, we didn't start the thread, all these mutex are locked:
377 // we must unlock them.
378 if (m_mutex.IsLocked())
379 m_mutex.Unlock();
380
381 if (m_end_mutex.IsLocked())
382 m_end_mutex.Unlock();
383
384 if (m_mutexSuspend.IsLocked())
385 m_mutexSuspend.Unlock();
386}
387
388wxThreadError wxThreadInternal::Run()
389{
390 wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING,
391 _T("thread may only be started once after successful Create()") );
392
393 // the mutex was locked on Create(), so we will be able to lock it again
394 // only when the thread really starts executing and enters the wait -
395 // otherwise we might signal the condition before anybody is waiting for it
396 wxMutexLocker lock(m_mutex);
397 m_cond.Signal();
398
399 m_state = STATE_RUNNING;
400
401 return wxTHREAD_NO_ERROR;
402
403 // now the mutex is unlocked back - but just to allow Wait() function to
404 // terminate by relocking it, so the net result is that the worker thread
405 // starts executing and the mutex is still locked
406}
407
408void wxThreadInternal::Wait()
409{
410 wxCHECK_RET( WasCancelled(), _T("thread should have been cancelled first") );
411
412 // if the thread we're waiting for is waiting for the GUI mutex, we will
413 // deadlock so make sure we release it temporarily
414 if ( wxThread::IsMain() )
415 wxMutexGuiLeave();
416
417 // entering Wait() releases the mutex thus allowing SignalExit() to acquire
418 // it and to signal us its termination
419 m_cond.Wait(m_end_mutex);
420
421 // mutex is still in the locked state - relocked on exit from Wait(), so
422 // unlock it - we don't need it any more, the thread has already terminated
423 m_end_mutex.Unlock();
424
425 // After that, we wait for the real end of the other thread.
426 pthread_join(GetId(), NULL);
427
428 // reacquire GUI mutex
429 if ( wxThread::IsMain() )
430 wxMutexGuiEnter();
431}
432
433void wxThreadInternal::SignalExit()
434{
435 // GL: Unlock mutexSuspend here.
436 m_mutexSuspend.Unlock();
437
438 // as mutex is currently locked, this will block until some other thread
439 // (normally the same which created this one) unlocks it by entering Wait()
440 m_end_mutex.Lock();
441
442 // wake up all the threads waiting for our termination
443 m_cond.Broadcast();
444
445 // after this call mutex will be finally unlocked
446 m_end_mutex.Unlock();
447}
448
449void wxThreadInternal::Pause()
450{
451 // the state is set from the thread which pauses us first, this function
452 // is called later so the state should have been already set
453 wxCHECK_RET( m_state == STATE_PAUSED,
454 _T("thread must first be paused with wxThread::Pause().") );
455
456 // don't pause the thread which is being terminated - this would lead to
457 // deadlock if the thread is paused after Delete() had called Resume() but
458 // before it had time to call Wait()
459 if ( WasCancelled() )
460 return;
461
462 // wait until the condition is signaled from Resume()
463 m_condSuspend.Wait(m_mutexSuspend);
464}
465
466void wxThreadInternal::Resume()
467{
468 wxCHECK_RET( m_state == STATE_PAUSED,
469 _T("can't resume thread which is not suspended.") );
470
471 // we will be able to lock this mutex only when Pause() starts waiting
472 wxMutexLocker lock(m_mutexSuspend);
473 m_condSuspend.Signal();
474
475 SetState(STATE_RUNNING);
476}
477
478// -----------------------------------------------------------------------------
479// static functions
480// -----------------------------------------------------------------------------
481
482wxThread *wxThread::This()
483{
484 return (wxThread *)pthread_getspecific(gs_keySelf);
485}
486
487bool wxThread::IsMain()
488{
489 return (bool)pthread_equal(pthread_self(), gs_tidMain);
490}
491
492void wxThread::Yield()
493{
494 sched_yield();
495}
496
497void wxThread::Sleep(unsigned long milliseconds)
498{
499 wxUsleep(milliseconds);
500}
501
502// -----------------------------------------------------------------------------
503// creating thread
504// -----------------------------------------------------------------------------
505
506wxThread::wxThread()
507{
508 // add this thread to the global list of all threads
509 gs_allThreads.Add(this);
510
511 p_internal = new wxThreadInternal();
512}
513
514wxThreadError wxThread::Create()
515{
516 if (p_internal->GetState() != STATE_NEW)
517 return wxTHREAD_RUNNING;
518
519 // set up the thread attribute: right now, we only set thread priority
520 pthread_attr_t attr;
521 pthread_attr_init(&attr);
522
523#ifdef HAVE_THREAD_PRIORITY_FUNCTIONS
524 int prio;
525 if ( pthread_attr_getschedpolicy(&attr, &prio) != 0 )
526 {
527 wxLogError(_("Cannot retrieve thread scheduling policy."));
528 }
529
530 int min_prio = sched_get_priority_min(prio),
531 max_prio = sched_get_priority_max(prio);
532
533 if ( min_prio == -1 || max_prio == -1 )
534 {
535 wxLogError(_("Cannot get priority range for scheduling policy %d."),
536 prio);
537 }
538 else
539 {
540 struct sched_param sp;
541 pthread_attr_getschedparam(&attr, &sp);
542 sp.sched_priority = min_prio +
543 (p_internal->GetPriority()*(max_prio-min_prio))/100;
544 pthread_attr_setschedparam(&attr, &sp);
545 }
546#endif // HAVE_THREAD_PRIORITY_FUNCTIONS
547
548#ifdef HAVE_PTHREAD_ATTR_SETSCOPE
549 // this will make the threads created by this process really concurrent
550 pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
551#endif // HAVE_PTHREAD_ATTR_SETSCOPE
552
553 // create the new OS thread object
554 int rc = pthread_create(p_internal->GetIdPtr(), &attr,
555 wxThreadInternal::PthreadStart, (void *)this);
556 pthread_attr_destroy(&attr);
557
558 if ( rc != 0 )
559 {
560 p_internal->SetState(STATE_EXITED);
561 return wxTHREAD_NO_RESOURCE;
562 }
563
564 return wxTHREAD_NO_ERROR;
565}
566
567wxThreadError wxThread::Run()
568{
569 return p_internal->Run();
570}
571
572// -----------------------------------------------------------------------------
573// misc accessors
574// -----------------------------------------------------------------------------
575
576void wxThread::SetPriority(unsigned int prio)
577{
578 wxCHECK_RET( ((int)WXTHREAD_MIN_PRIORITY <= (int)prio) &&
579 ((int)prio <= (int)WXTHREAD_MAX_PRIORITY),
580 _T("invalid thread priority") );
581
582 wxCriticalSectionLocker lock(m_critsect);
583
584 switch ( p_internal->GetState() )
585 {
586 case STATE_NEW:
587 // thread not yet started, priority will be set when it is
588 p_internal->SetPriority(prio);
589 break;
590
591 case STATE_RUNNING:
592 case STATE_PAUSED:
593#ifdef HAVE_THREAD_PRIORITY_FUNCTIONS
594 {
595 struct sched_param sparam;
596 sparam.sched_priority = prio;
597
598 if ( pthread_setschedparam(p_internal->GetId(),
599 SCHED_OTHER, &sparam) != 0 )
600 {
601 wxLogError(_("Failed to set thread priority %d."), prio);
602 }
603 }
604#endif // HAVE_THREAD_PRIORITY_FUNCTIONS
605 break;
606
607 case STATE_EXITED:
608 default:
609 wxFAIL_MSG(_T("impossible to set thread priority in this state"));
610 }
611}
612
613unsigned int wxThread::GetPriority() const
614{
615 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
616
617 return p_internal->GetPriority();
618}
619
620unsigned long wxThread::GetID() const
621{
622 return (unsigned long)p_internal->GetId();
623}
624
625// -----------------------------------------------------------------------------
626// pause/resume
627// -----------------------------------------------------------------------------
628
629wxThreadError wxThread::Pause()
630{
631 wxCriticalSectionLocker lock(m_critsect);
632
633 if ( p_internal->GetState() != STATE_RUNNING )
634 {
635 wxLogDebug(_T("Can't pause thread which is not running."));
636
637 return wxTHREAD_NOT_RUNNING;
638 }
639
640 p_internal->SetState(STATE_PAUSED);
641
642 return wxTHREAD_NO_ERROR;
643}
644
645wxThreadError wxThread::Resume()
646{
647 wxCriticalSectionLocker lock(m_critsect);
648
649 if ( p_internal->GetState() == STATE_PAUSED )
650 {
651 m_critsect.Leave();
652 p_internal->Resume();
653 m_critsect.Enter();
654
655 return wxTHREAD_NO_ERROR;
656 }
657 else
658 {
659 wxLogDebug(_T("Attempt to resume a thread which is not paused."));
660
661 return wxTHREAD_MISC_ERROR;
662 }
663}
664
665// -----------------------------------------------------------------------------
666// exiting thread
667// -----------------------------------------------------------------------------
668
669wxThread::ExitCode wxThread::Delete()
670{
671 m_critsect.Enter();
672 wxThreadState state = p_internal->GetState();
673
674 // ask the thread to stop
675 p_internal->SetCancelFlag();
676
677 m_critsect.Leave();
678
679 switch ( state )
680 {
681 case STATE_NEW:
682 case STATE_EXITED:
683 // nothing to do
684 break;
685
686 case STATE_PAUSED:
687 // resume the thread first
688 Resume();
689
690 // fall through
691
692 default:
693 // wait until the thread stops
694 p_internal->Wait();
695 }
696
697 return NULL;
698}
699
700wxThreadError wxThread::Kill()
701{
702 switch ( p_internal->GetState() )
703 {
704 case STATE_NEW:
705 case STATE_EXITED:
706 return wxTHREAD_NOT_RUNNING;
707
708 default:
709#ifdef HAVE_PTHREAD_CANCEL
710 if ( pthread_cancel(p_internal->GetId()) != 0 )
711#endif
712 {
713 wxLogError(_("Failed to terminate a thread."));
714
715 return wxTHREAD_MISC_ERROR;
716 }
717
718 return wxTHREAD_NO_ERROR;
719 }
720}
721
722void wxThread::Exit(void *status)
723{
724 // first call user-level clean up code
725 OnExit();
726
727 // next wake up the threads waiting for us (OTOH, this function won't return
728 // until someone waited for us!)
729 p_internal->SignalExit();
730
731 p_internal->SetState(STATE_EXITED);
732
733 // delete both C++ thread object and terminate the OS thread object
734 // GL: This is very ugly and buggy ...
735// delete this;
736 pthread_exit(status);
737}
738
739// also test whether we were paused
740bool wxThread::TestDestroy()
741{
742 wxCriticalSectionLocker lock(m_critsect);
743
744 if ( p_internal->GetState() == STATE_PAUSED )
745 {
746 // leave the crit section or the other threads will stop too if they try
747 // to call any of (seemingly harmless) IsXXX() functions while we sleep
748 m_critsect.Leave();
749
750 p_internal->Pause();
751
752 // enter it back before it's finally left in lock object dtor
753 m_critsect.Enter();
754 }
755
756 return p_internal->WasCancelled();
757}
758
759wxThread::~wxThread()
760{
761 m_critsect.Enter();
762 if (p_internal->GetState() != STATE_EXITED &&
763 p_internal->GetState() != STATE_NEW)
764 wxLogDebug(_T("The thread is being destroyed althought it is still running ! The application may crash."));
765
766 m_critsect.Leave();
767
768 delete p_internal;
769 // remove this thread from the global array
770 gs_allThreads.Remove(this);
771}
772
773// -----------------------------------------------------------------------------
774// state tests
775// -----------------------------------------------------------------------------
776
777bool wxThread::IsRunning() const
778{
779 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
780
781 return p_internal->GetState() == STATE_RUNNING;
782}
783
784bool wxThread::IsAlive() const
785{
786 wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
787
788 switch ( p_internal->GetState() )
789 {
790 case STATE_RUNNING:
791 case STATE_PAUSED:
792 return TRUE;
793
794 default:
795 return FALSE;
796 }
797}
798
799bool wxThread::IsPaused() const
800{
801 wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect);
802
803 return (p_internal->GetState() == STATE_PAUSED);
804}
805
806//--------------------------------------------------------------------
807// wxThreadModule
808//--------------------------------------------------------------------
809
810class wxThreadModule : public wxModule
811{
812public:
813 virtual bool OnInit();
814 virtual void OnExit();
815
816private:
817 DECLARE_DYNAMIC_CLASS(wxThreadModule)
818};
819
820IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
821
822bool wxThreadModule::OnInit()
823{
824 int rc = pthread_key_create(&gs_keySelf, NULL /* dtor function */);
825 if ( rc != 0 )
826 {
827 wxLogSysError(rc, _("Thread module initialization failed: "
828 "failed to create thread key"));
829
830 return FALSE;
831 }
832
833 gs_mutexGui = new wxMutex();
834
835 gs_tidMain = pthread_self();
836
837 gs_mutexGui->Lock();
838
839 return TRUE;
840}
841
842void wxThreadModule::OnExit()
843{
844 wxASSERT_MSG( wxThread::IsMain(), _T("only main thread can be here") );
845
846 // terminate any threads left
847 size_t count = gs_allThreads.GetCount();
848 if ( count != 0u )
849 wxLogDebug(_T("Some threads were not terminated by the application."));
850
851 for ( size_t n = 0u; n < count; n++ )
852 {
853 gs_allThreads[n]->Delete();
854 }
855
856 // destroy GUI mutex
857 gs_mutexGui->Unlock();
858
859 delete gs_mutexGui;
860
861 // and free TLD slot
862 (void)pthread_key_delete(gs_keySelf);
863}
864
865// ----------------------------------------------------------------------------
866// global functions
867// ----------------------------------------------------------------------------
868
869void wxMutexGuiEnter()
870{
871 gs_mutexGui->Lock();
872}
873
874void wxMutexGuiLeave()
875{
876 gs_mutexGui->Unlock();
877}
878
879#endif
880 // wxUSE_THREADS