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