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