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