]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/threadpsx.cpp
added more stuff to wxBase: zipstrm, zstream, fs_*, mstream
[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 #ifdef __SUN__
28 extern 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
42 enum 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
51 WX_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
60 static wxArrayThread gs_allThreads;
61
62 // the id of the main thread
63 static pthread_t gs_tidMain;
64
65 // the key for the pointer to the associated wxThread object
66 static pthread_key_t gs_keySelf;
67
68 // this mutex must be acquired before any call to a GUI function
69 static wxMutex *gs_mutexGui;
70
71 //--------------------------------------------------------------------
72 // common GUI thread code
73 //--------------------------------------------------------------------
74
75 #include "threadgui.inc"
76
77 //--------------------------------------------------------------------
78 // wxMutex (Posix implementation)
79 //--------------------------------------------------------------------
80
81 class wxMutexInternal
82 {
83 public:
84 pthread_mutex_t p_mutex;
85 };
86
87 wxMutex::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
94 wxMutex::~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
103 wxMutexError 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
118 wxMutexError 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
136 wxMutexError 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
158 class wxConditionInternal
159 {
160 public:
161 pthread_cond_t p_condition;
162 };
163
164 wxCondition::wxCondition()
165 {
166 p_internal = new wxConditionInternal;
167 pthread_cond_init( &(p_internal->p_condition), (const pthread_condattr_t *) NULL );
168 }
169
170 wxCondition::~wxCondition()
171 {
172 pthread_cond_destroy( &(p_internal->p_condition) );
173
174 delete p_internal;
175 }
176
177 void wxCondition::Wait(wxMutex& mutex)
178 {
179 pthread_cond_wait( &(p_internal->p_condition), &(mutex.p_internal->p_mutex) );
180 }
181
182 bool 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
191 void wxCondition::Signal()
192 {
193 pthread_cond_signal( &(p_internal->p_condition) );
194 }
195
196 void wxCondition::Broadcast()
197 {
198 pthread_cond_broadcast( &(p_internal->p_condition) );
199 }
200
201 //--------------------------------------------------------------------
202 // wxThread (Posix implementation)
203 //--------------------------------------------------------------------
204
205 class wxThreadInternal
206 {
207 public:
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
241 private:
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
268 void *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
297 wxThreadInternal::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
311 wxThreadInternal::~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
319 wxThreadError 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
339 void 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
363 void 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
376 void 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
385 void 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
401 wxThread *wxThread::This()
402 {
403 return (wxThread *)pthread_getspecific(gs_keySelf);
404 }
405
406 bool wxThread::IsMain()
407 {
408 return (bool)pthread_equal(pthread_self(), gs_tidMain);
409 }
410
411 void 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
421 void wxThread::Sleep(unsigned long milliseconds)
422 {
423 wxUsleep(milliseconds);
424 }
425
426 // -----------------------------------------------------------------------------
427 // creating thread
428 // -----------------------------------------------------------------------------
429
430 wxThread::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
438 wxThreadError 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
486 wxThreadError wxThread::Run()
487 {
488 return p_internal->Run();
489 }
490
491 // -----------------------------------------------------------------------------
492 // misc accessors
493 // -----------------------------------------------------------------------------
494
495 void 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
532 unsigned int wxThread::GetPriority() const
533 {
534 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
535
536 return p_internal->GetPriority();
537 }
538
539 unsigned long wxThread::GetID() const
540 {
541 return (unsigned long)p_internal->thread_id;
542 }
543
544 // -----------------------------------------------------------------------------
545 // pause/resume
546 // -----------------------------------------------------------------------------
547
548 wxThreadError 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
564 wxThreadError 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
586 wxThread::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
613 wxThreadError 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
635 void 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
652 bool 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
671 wxThread::~wxThread()
672 {
673 // remove this thread from the global array
674 gs_allThreads.Remove(this);
675 }
676
677 // -----------------------------------------------------------------------------
678 // state tests
679 // -----------------------------------------------------------------------------
680
681 bool wxThread::IsRunning() const
682 {
683 wxCriticalSectionLocker lock((wxCriticalSection &)m_critsect);
684
685 return p_internal->GetState() == STATE_RUNNING;
686 }
687
688 bool 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
707 class wxThreadModule : public wxModule
708 {
709 public:
710 virtual bool OnInit();
711 virtual void OnExit();
712
713 private:
714 DECLARE_DYNAMIC_CLASS(wxThreadModule)
715 };
716
717 IMPLEMENT_DYNAMIC_CLASS(wxThreadModule, wxModule)
718
719 bool 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
737 void 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 }