PR116586/PR121141 guidance for shared_timed_mutex

Mike Crowe mac@mcrowe.com
Sun Sep 14 18:46:40 GMT 2025


On Sunday 03 August 2025 at 21:04:54 +0100, Mike Crowe wrote:
> I'm trying to fix https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116586 .
> Whilst fixing it for std::shared_timed_mutex I've uncovered inconsistencies
> in the behaviour of the existing implementation that I think are at the
> very least confusing and suboptimal. I know what I'd like to change but I'm
> worried that there may be reasons not to do that.
> 
> shared_timed_mutex has four "fundamental" try_lock function implementations
> for the product of steady vs system clock and exclusive vs shared. (There
> are others, but they end up calling one of the four.)
> 
> 1. shared_timed_mutex::try_lock_shared_until() on system_clock loops
>    indefinitely if pthread_rwlock_timedrdlock() returns EDEADLK. The
>    comment above justifies this by saying:
> 
>         // For cases where the implementation detects a deadlock we
>         // intentionally block and timeout so that an early return isn't
>         // mistaken for a spurious failure, which might help users realise
>         // there is a deadlock.
> 
>    This makes some sense but shared_timed_mutex::try_lock_shared_until() on
>    steady_clock, and shared_timed_mutex::try_lock_until() on both clocks
>    don't loop on EDEADLK. They just return false if
>    pthread_rwlock_{clockrd,clockwr,timedwr}lock() return EDEADLK. I can't
>    find any other std::*mutex::try_lock_until functions that loop on
>    EDEADLK.
> 
>    IMO if _GLIBCXX_ASSERTIONS are enabled then it would make more sense to
>    always assert on EDEADLK instead. If the looping is a good idea, then it
>    should be done in all four cases on std::shared_timed_mutex, and perhaps
>    for other std::*timed_mutex classes too. If it's not a good idea, then
>    the looping should be removed from this one place.
> 
>    As it stands, I'm inclined to assert on EDEADLK if _GLIBCXX_ASSERTIONS
>    are enabled and not loop. But does Hyrum's Law mean that it's too late
>    to change the behaviour now?
> 
> 2. All the shared_timed_mutex::try_ functions that take a timeout currently
>    return true (incorrectly indicating that the lock has been taken) in
>    reaction to errors that aren't EAGAIN (sometimes), ETIMEDOUT or EDEADLK.
>    This turns makes bugs like
>    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121141 even nastier. I
>    think that the functions should always return false on error since the
>    lock has not been taken.
> 
> The indicative patch below implements the above (except the loop removal)
> along with tests. It also has tests to show that std::*timed_mutex,
> std::condition_variable and std::future don't suffer from PR116586. I plan
> to add equivalent tests for std::*semaphore::try_acquire_until() too.

When I added tests for std::binary_semaphore::try_acquire_until() I
discovered that atomic.cc's __platform_wait_until() _does_ suffer from
PR116586. Since fixing it doesn't involve having an answer to the questions
above I will submit a separate patch for that.

Mike.


More information about the Libstdc++ mailing list