From e6a88393bf7da46c6acf18ab078957c38294b232 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 30 May 2020 18:52:50 +1000 Subject: [PATCH] Fix rare case on non-Windows where Waitable_Wait would miss a signal pthread_cond_signal is documented to have no effect if no threads are blocked/waiting. As such, if another thread called pthread_cond_wait afterwards, it would not realise it was supposed to be signalled already, and then incorrectly proceed to block while waiting for another signal. This issue manifested in the http worker thread, which would rarely cause the launcher to get stuck 'checking for updates' until some other http request got queued, which would wake up the stuck worker thread. Fix this by adding a field that keeps track of whether the waitable is currently in the 'signalled' state. --- src/Platform.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/Platform.c b/src/Platform.c index 3e291e4cb..fbe035bc1 100644 --- a/src/Platform.c +++ b/src/Platform.c @@ -709,6 +709,7 @@ void Mutex_Unlock(void* handle) { struct WaitData { pthread_cond_t cond; pthread_mutex_t mutex; + int signalled; }; void* Waitable_Create(void) { @@ -719,6 +720,8 @@ void* Waitable_Create(void) { if (res) Logger_Abort2(res, "Creating waitable"); res = pthread_mutex_init(&ptr->mutex, NULL); if (res) Logger_Abort2(res, "Creating waitable mutex"); + + ptr->signalled = false; return ptr; } @@ -735,7 +738,13 @@ void Waitable_Free(void* handle) { void Waitable_Signal(void* handle) { struct WaitData* ptr = (struct WaitData*)handle; - int res = pthread_cond_signal(&ptr->cond); + int res; + + Mutex_Lock(&ptr->mutex); + ptr->signalled = true; + Mutex_Unlock(&ptr->mutex); + + res = pthread_cond_signal(&ptr->cond); if (res) Logger_Abort2(res, "Signalling event"); } @@ -744,8 +753,11 @@ void Waitable_Wait(void* handle) { int res; Mutex_Lock(&ptr->mutex); - res = pthread_cond_wait(&ptr->cond, &ptr->mutex); - if (res) Logger_Abort2(res, "Waitable wait"); + if (!ptr->signalled) { + res = pthread_cond_wait(&ptr->cond, &ptr->mutex); + if (res) Logger_Abort2(res, "Waitable wait"); + } + ptr->signalled = false; Mutex_Unlock(&ptr->mutex); } @@ -763,8 +775,11 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { ts.tv_nsec %= NS_PER_SEC; Mutex_Lock(&ptr->mutex); - res = pthread_cond_timedwait(&ptr->cond, &ptr->mutex, &ts); - if (res && res != ETIMEDOUT) Logger_Abort2(res, "Waitable wait for"); + if (!ptr->signalled) { + res = pthread_cond_timedwait(&ptr->cond, &ptr->mutex, &ts); + if (res && res != ETIMEDOUT) Logger_Abort2(res, "Waitable wait for"); + } + ptr->signalled = false; Mutex_Unlock(&ptr->mutex); } #endif