X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/015ee8444a80efa1d52815a6b3b93ea2d85d5e92..2df793f9f482688be754e8657ab733c9d4fcb010:/src/gtk/threadpsx.cpp diff --git a/src/gtk/threadpsx.cpp b/src/gtk/threadpsx.cpp index 28abd1b362..99239908ef 100644 --- a/src/gtk/threadpsx.cpp +++ b/src/gtk/threadpsx.cpp @@ -24,11 +24,17 @@ #include #endif +#ifdef __SUN__ +extern int usleep(unsigned int useconds); +#endif + + #include "wx/thread.h" #include "wx/module.h" #include "wx/utils.h" #include "wx/log.h" #include "wx/intl.h" +#include "wx/dynarray.h" #include "gdk/gdk.h" #include "gtk/gtk.h" @@ -42,12 +48,19 @@ enum thread_state STATE_EXITED }; -//-------------------------------------------------------------------- +WX_DEFINE_ARRAY(wxThread *, wxArrayThread); + +// ----------------------------------------------------------------------------- // global data -//-------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +// we keep the list of all threads created by the application to be able to +// terminate them on exit if there are some left - otherwise the process would +// be left in memory +static wxArrayThread gs_allThreads; // the id of the main thread -static pthread_t gs_pidMain; +static pthread_t gs_tidMain; // the key for the pointer to the associated wxThread object static pthread_key_t gs_keySelf; @@ -81,7 +94,7 @@ wxMutex::wxMutex() wxMutex::~wxMutex() { if (m_locked > 0) - wxLogDebug( "wxMutex warning: freeing a locked mutex (%d locks)", m_locked ); + wxLogDebug("Freeing a locked mutex (%d locks)", m_locked); pthread_mutex_destroy( &(p_internal->p_mutex) ); delete p_internal; @@ -92,6 +105,8 @@ wxMutexError wxMutex::Lock() int err = pthread_mutex_lock( &(p_internal->p_mutex) ); if (err == EDEADLK) { + wxLogDebug("Locking this mutex would lead to deadlock!"); + return wxMUTEX_DEAD_LOCK; } @@ -126,6 +141,8 @@ wxMutexError wxMutex::Unlock() } else { + wxLogDebug("Unlocking not locked mutex."); + return wxMUTEX_UNLOCKED; } @@ -188,14 +205,23 @@ void wxCondition::Broadcast() class wxThreadInternal { public: - wxThreadInternal() { m_state = STATE_NEW; } - ~wxThreadInternal() {} + wxThreadInternal(); + ~wxThreadInternal(); // thread entry function static void *PthreadStart(void *ptr); - // start the thread + // thread actions + // start the thread wxThreadError Run(); + // ask the thread to terminate + void Cancel(); + // wake up threads waiting for our termination + void SignalExit(); + // go to sleep until Resume() is called + void Pause(); + // resume the thread + void Resume(); // accessors // priority @@ -207,7 +233,6 @@ public: // id pthread_t GetId() const { return thread_id; } // "cancelled" flag - void Cancel(); bool WasCancelled() const { return m_cancelled; } //private: -- should be! @@ -220,13 +245,24 @@ private: // set when the thread should terminate bool m_cancelled; - // we start running when this condition becomes true - wxMutex m_mutexRun; - wxCondition m_condRun; - - // this condition becomes true when we get back to PthreadStart() function - wxMutex m_mutexStop; - wxCondition m_condStop; + // this (mutex, cond) pair is used to synchronize the main thread and this + // thread in several situations: + // 1. The thread function blocks until condition is signaled by Run() when + // it's initially created - this allows create thread in "suspended" + // state + // 2. The Delete() function blocks until the condition is signaled when the + // thread exits. + wxMutex m_mutex; + wxCondition m_cond; + + // another (mutex, cond) pair for Pause()/Resume() usage + // + // VZ: it's possible that we might reuse the mutex and condition from above + // for this too, but as I'm not at all sure that it won't create subtle + // problems with race conditions between, say, Pause() and Delete() I + // prefer this may be a bit less efficient but much safer solution + wxMutex m_mutexSuspend; + wxCondition m_condSuspend; }; void *wxThreadInternal::PthreadStart(void *ptr) @@ -242,17 +278,14 @@ void *wxThreadInternal::PthreadStart(void *ptr) } // wait for the condition to be signaled from Run() - pthread->m_mutexRun.Lock(); - pthread->m_condRun.Wait(pthread->m_mutexRun); + // mutex state: currently locked by the thread which created us + pthread->m_cond.Wait(pthread->m_mutex); + + // mutex state: locked again on exit of Wait() // call the main entry void* status = thread->Entry(); - pthread->m_mutexRun.Unlock(); - - // wake up the pthread(s) waiting for our termination - pthread->m_condStop.Broadcast(); - // terminate the thread thread->Exit(status); @@ -261,22 +294,104 @@ void *wxThreadInternal::PthreadStart(void *ptr) return NULL; } +wxThreadInternal::wxThreadInternal() +{ + m_state = STATE_NEW; + m_cancelled = FALSE; + + // this mutex is locked during almost all thread lifetime - it will only be + // unlocked in the very end + m_mutex.Lock(); + + // this mutex is used in Pause()/Resume() and is also locked all the time + // unless the thread is paused + m_mutexSuspend.Lock(); +} + +wxThreadInternal::~wxThreadInternal() +{ + m_mutexSuspend.Unlock(); + + // note that m_mutex will be unlocked by the thread which waits for our + // termination +} + wxThreadError wxThreadInternal::Run() { wxCHECK_MSG( GetState() == STATE_NEW, wxTHREAD_RUNNING, "thread may only be started once after successful Create()" ); - wxMutexLocker lock(&m_mutexRun); - m_condRun.Signal(); + // the mutex was locked on Create(), so we will be able to lock it again + // only when the thread really starts executing and enters the wait - + // otherwise we might signal the condition before anybody is waiting for it + wxMutexLocker lock(m_mutex); + m_cond.Signal(); + + m_state = STATE_RUNNING; return wxTHREAD_NO_ERROR; + + // now the mutex is unlocked back - but just to allow Wait() function to + // terminate by relocking it, so the net result is that the worker thread + // starts executing and the mutex is still locked } void wxThreadInternal::Cancel() { - wxMutexLocker lock(&m_mutexStop); + // if the thread we're waiting for is waiting for the GUI mutex, we will + // deadlock so make sure we release it temporarily + if ( wxThread::IsMain() ) + wxMutexGuiLeave(); + + // nobody ever writes this variable so it's safe to not use any + // synchronization here m_cancelled = TRUE; - m_condStop.Wait(m_mutexStop); + + // entering Wait() releases the mutex thus allowing SignalExit() to acquire + // it and to signal us its termination + m_cond.Wait(m_mutex); + + // mutex is still in the locked state - relocked on exit from Wait(), so + // unlock it - we don't need it any more, the thread has already terminated + m_mutex.Unlock(); + + // reacquire GUI mutex + if ( wxThread::IsMain() ) + wxMutexGuiEnter(); +} + +void wxThreadInternal::SignalExit() +{ + // as mutex is currently locked, this will block until some other thread + // (normally the same which created this one) unlocks it by entering Wait() + m_mutex.Lock(); + + // wake up all the threads waiting for our termination + m_cond.Broadcast(); + + // after this call mutex will be finally unlocked + m_mutex.Unlock(); +} + +void wxThreadInternal::Pause() +{ + wxCHECK_RET( m_state == STATE_PAUSED, + "thread must first be paused with wxThread::Pause()." ); + + // wait until the condition is signaled from Resume() + m_condSuspend.Wait(m_mutexSuspend); +} + +void wxThreadInternal::Resume() +{ + wxCHECK_RET( m_state == STATE_PAUSED, + "can't resume thread which is not suspended." ); + + // we will be able to lock this mutex only when Pause() starts waiting + wxMutexLocker lock(m_mutexSuspend); + m_condSuspend.Signal(); + + SetState(STATE_RUNNING); } // ----------------------------------------------------------------------------- @@ -290,19 +405,22 @@ wxThread *wxThread::This() bool wxThread::IsMain() { - return (bool)pthread_equal(pthread_self(), gs_pidMain); + return (bool)pthread_equal(pthread_self(), gs_tidMain); } void wxThread::Yield() { +#ifdef HAVE_SCHED_YIELD sched_yield(); +#else // !HAVE_SCHED_YIELD + // may be it will have the desired effect? + Sleep(0); +#endif // HAVE_SCHED_YIELD } void wxThread::Sleep(unsigned long milliseconds) { - // FIXME how to test for nanosleep() availability? - - usleep(milliseconds * 1000); // usleep(3) wants microseconds + wxUsleep(milliseconds); } // ----------------------------------------------------------------------------- @@ -311,6 +429,9 @@ void wxThread::Sleep(unsigned long milliseconds) wxThread::wxThread() { + // add this thread to the global list of all threads + gs_allThreads.Add(this); + p_internal = new wxThreadInternal(); } @@ -323,6 +444,7 @@ wxThreadError wxThread::Create() pthread_attr_t attr; pthread_attr_init(&attr); +#ifdef HAVE_THREAD_PRIORITY_FUNCTIONS int prio; if ( pthread_attr_getschedpolicy(&attr, &prio) != 0 ) { @@ -345,8 +467,9 @@ wxThreadError wxThread::Create() (p_internal->GetPriority()*(max_prio-min_prio))/100; pthread_attr_setschedparam(&attr, &sp); } +#endif // HAVE_THREAD_PRIORITY_FUNCTIONS - // this is the point of no return + // create the new OS thread object int rc = pthread_create(&p_internal->thread_id, &attr, wxThreadInternal::PthreadStart, (void *)this); pthread_attr_destroy(&attr); @@ -371,8 +494,9 @@ wxThreadError wxThread::Run() void wxThread::SetPriority(unsigned int prio) { - wxCHECK_RET( (WXTHREAD_MIN_PRIORITY <= prio) && - (prio <= WXTHREAD_MAX_PRIORITY), "invalid thread priority" ); + wxCHECK_RET( ((int)WXTHREAD_MIN_PRIORITY <= (int)prio) && + ((int)prio <= (int)WXTHREAD_MAX_PRIORITY), + "invalid thread priority" ); wxCriticalSectionLocker lock(m_critsect); @@ -385,6 +509,7 @@ void wxThread::SetPriority(unsigned int prio) case STATE_RUNNING: case STATE_PAUSED: +#ifdef HAVE_THREAD_PRIORITY_FUNCTIONS { struct sched_param sparam; sparam.sched_priority = prio; @@ -395,6 +520,7 @@ void wxThread::SetPriority(unsigned int prio) wxLogError(_("Failed to set thread priority %d."), prio); } } +#endif // HAVE_THREAD_PRIORITY_FUNCTIONS break; case STATE_EXITED: @@ -441,7 +567,7 @@ wxThreadError wxThread::Resume() if ( p_internal->GetState() == STATE_PAUSED ) { - p_internal->SetState(STATE_RUNNING); + p_internal->Resume(); return wxTHREAD_NO_ERROR; } @@ -493,7 +619,9 @@ wxThreadError wxThread::Kill() return wxTHREAD_NOT_RUNNING; default: +#ifdef HAVE_PTHREAD_CANCEL if ( pthread_cancel(p_internal->GetId()) != 0 ) +#endif { wxLogError(_("Failed to terminate a thread.")); @@ -506,27 +634,44 @@ wxThreadError wxThread::Kill() void wxThread::Exit(void *status) { - wxThread *ptr = this; - THREAD_SEND_EXIT_MSG(ptr); - + // first call user-level clean up code OnExit(); + // next wake up the threads waiting for us (OTOH, this function won't return + // until someone waited for us!) + p_internal->SignalExit(); + p_internal->SetState(STATE_EXITED); + // delete both C++ thread object and terminate the OS thread object delete this; - pthread_exit(status); } -bool wxThread::TestDestroy() const +// also test whether we were paused +bool wxThread::TestDestroy() { wxCriticalSectionLocker lock((wxCriticalSection&)m_critsect); + if ( p_internal->GetState() == STATE_PAUSED ) + { + // leave the crit section or the other threads will stop too if they try + // to call any of (seemingly harmless) IsXXX() functions while we sleep + m_critsect.Leave(); + + p_internal->Pause(); + + // enter it back before it's finally left in lock object dtor + m_critsect.Enter(); + } + return p_internal->WasCancelled(); } wxThread::~wxThread() { + // remove this thread from the global array + gs_allThreads.Remove(this); } // ----------------------------------------------------------------------------- @@ -583,7 +728,7 @@ bool wxThreadModule::OnInit() gs_mutexGui = new wxMutex(); wxThreadGuiInit(); - gs_pidMain = (int)getpid(); + gs_tidMain = pthread_self(); gs_mutexGui->Lock(); return TRUE; @@ -591,9 +736,23 @@ bool wxThreadModule::OnInit() void wxThreadModule::OnExit() { + wxASSERT_MSG( wxThread::IsMain(), "only main thread can be here" ); + + // terminate any threads left + size_t count = gs_allThreads.GetCount(); + if ( count != 0u ) + wxLogDebug("Some threads were not terminated by the application."); + + for ( size_t n = 0u; n < count; n++ ) + { + gs_allThreads[n]->Delete(); + } + + // destroy GUI mutex gs_mutexGui->Unlock(); wxThreadGuiExit(); delete gs_mutexGui; + // and free TLD slot (void)pthread_key_delete(gs_keySelf); }