From d900657559422c3b65b044608de2c8ba6290794b Mon Sep 17 00:00:00 2001 From: freyers Date: Fri, 15 May 2026 21:12:09 +0000 Subject: [PATCH] fix(semaphore): use absolute deadline for POSIX TimedWait sem_timedwait takes an absolute CLOCK_REALTIME deadline, but the code passed a relative duration, placing the deadline near the epoch so the wait always timed out instantly without blocking on Linux/Android. Compute the deadline from clock_gettime(CLOCK_REALTIME). Add tests covering timeout, already-signaled and signaled-before-timeout cases. --- CcpSemaphore.cpp | 16 ++++++++++++++-- tests/CcpSemaphore.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/CcpSemaphore.cpp b/CcpSemaphore.cpp index bf1d6f0..ad58df8 100644 --- a/CcpSemaphore.cpp +++ b/CcpSemaphore.cpp @@ -97,6 +97,7 @@ CcpSemaphore::~CcpSemaphore() } #include +#include bool CcpSemaphore::Wait() { @@ -110,8 +111,19 @@ bool CcpSemaphore::Wait() bool CcpSemaphore::TimedWait( uint32_t timeoutInMs ) { timespec ts; - ts.tv_sec = timeoutInMs / 1000; - ts.tv_nsec = (timeoutInMs % 1000) * 1000000; + if( clock_gettime( CLOCK_REALTIME, &ts ) != 0 ) + { + return false; + } + + ts.tv_sec += timeoutInMs / 1000; + ts.tv_nsec += static_cast( timeoutInMs % 1000 ) * 1000000L; + if( ts.tv_nsec >= 1000000000L ) + { + ts.tv_sec += ts.tv_nsec / 1000000000L; + ts.tv_nsec %= 1000000000L; + } + if( sem_timedwait( &m_semaphore, &ts ) == 0 ) { return true; diff --git a/tests/CcpSemaphore.cpp b/tests/CcpSemaphore.cpp index 6d4ed21..595f7e4 100644 --- a/tests/CcpSemaphore.cpp +++ b/tests/CcpSemaphore.cpp @@ -78,3 +78,45 @@ TEST( CcpSemaphore, CanWaitOnSemaphore ) EXPECT_GT( duration, 200 ); EXPECT_LT( duration, 1000 ); } + +TEST( CcpSemaphore, TimedWaitWaitsAndReturnsFalseOnTimeout ) +{ + CcpSemaphore s; + + bool acquired = true; + auto duration = TimeCallInMs( [&] { acquired = s.TimedWait( 500 ); } ); + + EXPECT_FALSE( acquired ); + EXPECT_GT( duration, 250 ); + EXPECT_LT( duration, 1500 ); +} + +TEST( CcpSemaphore, TimedWaitReturnsTrueWhenAlreadySignaled ) +{ + CcpSemaphore s; + s.Signal(); + + bool acquired = false; + auto duration = TimeCallInMs( [&] { acquired = s.TimedWait( 1000 ); } ); + + EXPECT_TRUE( acquired ); + EXPECT_LT( duration, 100 ); +} + +TEST( CcpSemaphore, TimedWaitReturnsTrueWhenSignaledBeforeTimeout ) +{ + CcpSemaphore s; + + auto thread = [&] { + CcpThreadSleep( 200 ); + s.Signal(); + }; + CcpCreateThread( &MakeThread, &thread, CCP_THREAD_PRIORITY_NORMAL ); + + bool acquired = false; + auto duration = TimeCallInMs( [&] { acquired = s.TimedWait( 2000 ); } ); + + EXPECT_TRUE( acquired ); + EXPECT_GT( duration, 100 ); + EXPECT_LT( duration, 1500 ); +}