diff --git a/checkin_notes b/checkin_notes index a3170c09e1..45c0309eeb 100644 --- a/checkin_notes +++ b/checkin_notes @@ -5187,3 +5187,12 @@ David 8 June 2009 clientgui/ DltItemProperties.cpp + +Charlie 8 June 2009 + - MGR: In Async RPC logic, use posix mutexes and conditions instead of + wxWidgets implementations, as this appears to fix an intermittent + hang in wxCondition::Wait(). + + clientgui/ + AsyncRPC.cpp, .h + MainDocument.cpp. .h diff --git a/clientgui/AsyncRPC.cpp b/clientgui/AsyncRPC.cpp index dcaeba5fb5..2d974a737b 100755 --- a/clientgui/AsyncRPC.cpp +++ b/clientgui/AsyncRPC.cpp @@ -33,6 +33,216 @@ #include "error_numbers.h" #include "util.h" +#ifdef __WXMAC__ + +#ifdef HAVE_PTHREAD_MUTEXATTR_T +// on some systems pthread_mutexattr_settype() is not in the headers (but it is +// in the library, otherwise we wouldn't compile this code at all) +extern "C" int pthread_mutexattr_settype( pthread_mutexattr_t *, int ); +#endif + +BOINC_Mutex::BOINC_Mutex( wxMutexType mutexType ) +{ + int err; + switch ( mutexType ) + { + case wxMUTEX_RECURSIVE: + // support recursive locks like Win32, i.e. a thread can lock a + // mutex which it had itself already locked + // + // unfortunately initialization of recursive mutexes is non + // portable, so try several methods +#ifdef HAVE_PTHREAD_MUTEXATTR_T + { + pthread_mutexattr_t attr; + pthread_mutexattr_init( &attr ); + pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); + + err = pthread_mutex_init( &m_mutex, &attr ); + } +#elif defined(HAVE_PTHREAD_RECURSIVE_MUTEX_INITIALIZER) + // we can use this only as initializer so we have to assign it + // first to a temp var - assigning directly to m_mutex wouldn't + // even compile + { + pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; + m_mutex = mutex; + } +#else // no recursive mutexes + err = EINVAL; +#endif // HAVE_PTHREAD_MUTEXATTR_T/... + break; + + default: + wxFAIL_MSG( wxT("unknown mutex type") ); + // fall through + + case wxMUTEX_DEFAULT: + err = pthread_mutex_init( &m_mutex, NULL ); + break; + } + + m_isOk = err == 0; + if ( !m_isOk ) + { + wxLogApiError( wxT("pthread_mutex_init()"), err ); + } +} + +BOINC_Mutex::~BOINC_Mutex() +{ + if ( m_isOk ) + { + int err = pthread_mutex_destroy( &m_mutex ); + if ( err != 0 ) + { + wxLogApiError( wxT("pthread_mutex_destroy()"), err ); + } + } +} + +wxMutexError BOINC_Mutex::Lock() +{ + int err = pthread_mutex_lock( &m_mutex ); + switch ( err ) + { + case EDEADLK: + // only error checking mutexes return this value and so it's an + // unexpected situation -- hence use assert, not wxLogDebug + wxFAIL_MSG( wxT("mutex deadlock prevented") ); + return wxMUTEX_DEAD_LOCK; + + case EINVAL: + wxLogDebug( wxT("pthread_mutex_lock(): mutex not initialized.") ); + break; + + case 0: + return wxMUTEX_NO_ERROR; + + default: + wxLogApiError( wxT("pthread_mutex_lock()"), err ); + } + + return wxMUTEX_MISC_ERROR; +} + +wxMutexError BOINC_Mutex::TryLock() +{ + int err = pthread_mutex_trylock( &m_mutex ); + switch ( err ) + { + case EBUSY: + // not an error: mutex is already locked, but we're prepared for this case + return wxMUTEX_BUSY; + + case EINVAL: + wxLogDebug( wxT("pthread_mutex_trylock(): mutex not initialized.") ); + break; + + case 0: + return wxMUTEX_NO_ERROR; + + default: + wxLogApiError( wxT("pthread_mutex_trylock()"), err ); + } + + return wxMUTEX_MISC_ERROR; +} + +wxMutexError BOINC_Mutex::Unlock() +{ + int err = pthread_mutex_unlock( &m_mutex ); + switch ( err ) + { + case EPERM: + // we don't own the mutex + return wxMUTEX_UNLOCKED; + + case EINVAL: + wxLogDebug( wxT("pthread_mutex_unlock(): mutex not initialized.") ); + break; + + case 0: + return wxMUTEX_NO_ERROR; + + default: + wxLogApiError( wxT("pthread_mutex_unlock()"), err ); + } + + return wxMUTEX_MISC_ERROR; +} + + +// wxMac wxCondition has bugs, so use native UNIX implementation + +BOINC_Condition::BOINC_Condition(BOINC_Mutex& mutex) + : m_BOINC_Mutex(mutex) { + int err; + + err = pthread_cond_init(&m_cond, NULL); + mb_initOK = (err == 0); +} + +BOINC_Condition::~BOINC_Condition() { + pthread_cond_destroy(&m_cond); + mb_initOK = false; +} + +wxCondError BOINC_Condition::Wait(){ + int err; + + err = pthread_cond_wait(&m_cond, &m_BOINC_Mutex.m_mutex); + switch (err) { + case 0: + return wxCOND_NO_ERROR; + case EINVAL: + return wxCOND_INVALID; + case ETIMEDOUT: + return wxCOND_TIMEOUT; + default: + return wxCOND_MISC_ERROR; + } + return wxCOND_NO_ERROR; +} + +wxCondError BOINC_Condition::WaitTimeout(unsigned long milliseconds) { + int err; + wxLongLong curtime = wxGetLocalTimeMillis(); + curtime += milliseconds; + wxLongLong temp = curtime / 1000; + int sec = temp.GetLo(); + temp *= 1000; + temp = curtime - temp; + int millis = temp.GetLo(); + + timespec tspec; + + tspec.tv_sec = sec; + tspec.tv_nsec = millis * 1000L * 1000L; + + err = pthread_cond_timedwait(&m_cond, &m_BOINC_Mutex.m_mutex, &tspec); + switch (err) { + case 0: + return wxCOND_NO_ERROR; + case EINVAL: + return wxCOND_INVALID; + case ETIMEDOUT: + return wxCOND_TIMEOUT; + default: + return wxCOND_MISC_ERROR; + } + return wxCOND_NO_ERROR; +} + +void BOINC_Condition::Signal() { + pthread_cond_signal(&m_cond); +} + +void BOINC_Condition::Broadcast() { + pthread_cond_broadcast(&m_cond); +} + +#endif // Delay in milliseconds before showing AsyncRPCDlg #define RPC_WAIT_DLG_DELAY 1500 @@ -111,10 +321,10 @@ int AsyncRPC::RPC_Wait(RPC_SELECTOR which_rpc, void *arg1, void *arg2, RPCThread::RPCThread(CMainDocument *pDoc, - wxMutex* pRPC_Thread_Mutex, - wxCondition* pRPC_Thread_Condition, - wxMutex* pRPC_Request_Mutex, - wxCondition* pRPC_Request_Condition) + BOINC_Mutex* pRPC_Thread_Mutex, + BOINC_Condition* pRPC_Thread_Condition, + BOINC_Mutex* pRPC_Request_Mutex, + BOINC_Condition* pRPC_Request_Condition) : wxThread() { m_pDoc = pDoc; m_pRPC_Thread_Mutex = pRPC_Thread_Mutex; diff --git a/clientgui/AsyncRPC.h b/clientgui/AsyncRPC.h index 12aa3c723c..145a62b736 100644 --- a/clientgui/AsyncRPC.h +++ b/clientgui/AsyncRPC.h @@ -22,6 +22,61 @@ #pragma interface "AsyncRPC.cpp" #endif +#ifndef __WXMAC__ + +#define BOINC_Condition wxCondition +#define BOINC_Mutex wxMutex + +#else + +// Adapted from wxMac-2.8.10 +#include + + +class BOINC_Mutex +{ +public: + BOINC_Mutex( wxMutexType mutexType = wxMUTEX_DEFAULT ); + ~BOINC_Mutex(); + + wxMutexError Lock(); + wxMutexError TryLock(); + wxMutexError Unlock(); + + bool IsOk() const + { return m_isOk; } + +private: + pthread_mutex_t m_mutex; + bool m_isOk; + + // BOINC_Condition uses our m_mutex + friend class BOINC_Condition; +}; + + +// Adapted from wxMac-2.8.10 but using native pthread_cond_*() +class BOINC_Condition +{ +public: + BOINC_Condition(BOINC_Mutex& mutex); + ~BOINC_Condition(); + bool IsOk() const { return (m_BOINC_Mutex.IsOk() && mb_initOK); } + wxCondError Wait(); + wxCondError WaitTimeout(unsigned long milliseconds); + void Signal(); + void Broadcast(); + +private: + BOINC_Mutex& m_BOINC_Mutex; + pthread_cond_t m_cond; + bool mb_initOK; + + DECLARE_NO_COPY_CLASS(BOINC_Condition) +}; + +#endif + class CMainDocument; // Forward declaration @@ -288,20 +343,20 @@ class RPCThread : public wxThread { public: RPCThread(CMainDocument *pDoc, - wxMutex* pRPC_Thread_Mutex, - wxCondition* pRPC_Thread_Condition, - wxMutex* pRPC_Request_Mutex, - wxCondition* RPC_Request_Condition + BOINC_Mutex* pRPC_Thread_Mutex, + BOINC_Condition* pRPC_Thread_Condition, + BOINC_Mutex* pRPC_Request_Mutex, + BOINC_Condition* RPC_Request_Condition ); virtual void *Entry(); private: int ProcessRPCRequest(); CMainDocument* m_pDoc; - wxMutex* m_pRPC_Thread_Mutex; - wxCondition* m_pRPC_Thread_Condition; - wxMutex* m_pRPC_Request_Mutex; - wxCondition* m_pRPC_Request_Condition; + BOINC_Mutex* m_pRPC_Thread_Mutex; + BOINC_Condition* m_pRPC_Thread_Condition; + BOINC_Mutex* m_pRPC_Request_Mutex; + BOINC_Condition* m_pRPC_Request_Condition; }; diff --git a/clientgui/MainDocument.cpp b/clientgui/MainDocument.cpp index bb536b28a4..7224893977 100644 --- a/clientgui/MainDocument.cpp +++ b/clientgui/MainDocument.cpp @@ -440,16 +440,16 @@ int CMainDocument::OnInit() { current_rpc_request.clear(); - m_pRPC_Thread_Mutex = new wxMutex(); + m_pRPC_Thread_Mutex = new BOINC_Mutex(); wxASSERT(m_pRPC_Thread_Mutex); - m_pRPC_Thread_Condition = new wxCondition(*m_pRPC_Thread_Mutex); + m_pRPC_Thread_Condition = new BOINC_Condition(*m_pRPC_Thread_Mutex); wxASSERT(m_pRPC_Thread_Condition); - m_pRPC_Request_Mutex = new wxMutex(); + m_pRPC_Request_Mutex = new BOINC_Mutex(); wxASSERT(m_pRPC_Request_Mutex); - m_pRPC_Request_Condition = new wxCondition(*m_pRPC_Request_Mutex); + m_pRPC_Request_Condition = new BOINC_Condition(*m_pRPC_Request_Mutex); wxASSERT(m_pRPC_Request_Condition); m_RPCThread = new RPCThread(this, diff --git a/clientgui/MainDocument.h b/clientgui/MainDocument.h index b59f3e88fc..fe876b9bb1 100644 --- a/clientgui/MainDocument.h +++ b/clientgui/MainDocument.h @@ -194,10 +194,10 @@ private: bool m_bWaitingForRPC; bool m_bNeedRefresh; bool m_bNeedTaskBarRefresh; - wxMutex* m_pRPC_Thread_Mutex; - wxCondition* m_pRPC_Thread_Condition; - wxMutex* m_pRPC_Request_Mutex; - wxCondition* m_pRPC_Request_Condition; + BOINC_Mutex* m_pRPC_Thread_Mutex; + BOINC_Condition* m_pRPC_Thread_Condition; + BOINC_Mutex* m_pRPC_Request_Mutex; + BOINC_Condition* m_pRPC_Request_Condition; // // Project Tab