C++ Logo

std-discussion

Advanced search

shared_mutex halting problem

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Tue, 29 Apr 2025 15:30:20 +0100
Hi,

Consider the following program:

#include <shared_mutex>
#include <mutex>
#include <thread>
#include <barrier>
#include <iostream>

int main() {
     std::shared_mutex smtx;
     std::barrier b(2);
     auto reader_1 = std::jthread([&]{
         auto lock = std::shared_lock{smtx};
         std::cout << "shared lock acquired\n";
         b.arrive_and_wait();
     });

     std::this_thread::sleep_for(std::chrono::seconds(1));

     auto writer = std::jthread([&]{
         auto lock = std::unique_lock{smtx};
         std::cout << "unique lock acquired\n";
     });

     std::this_thread::sleep_for(std::chrono::seconds(1));

     auto reader_2 = std::jthread([&]{
         auto lock = std::shared_lock{smtx};
         std::cout << "shared lock acquired\n";
         b.arrive_and_wait();
     });
}

Is this program guaranteed to terminate, assuming the implementation provides concurrent
progress guarantees? The behaviour is different for libstdc++ and libc++:

https://godbolt.org/z/vzdYnvGPr

Seemingly libc++ gets deadlocked in the following configuration:
* reader_1 is blocked on b.arrive_and_wait()
* writer is blocked on smtx.lock()
* reader_2 is blocked on smtx.lock_shared()

I think it's questionable whether the standard allows smtx.lock_shared() to block while no
thread holds exclusive ownership over smtx.

Relevant wording:

https://eel.is/c++draft/thread.mutex#thread.sharedmutex.requirements.general-5

"Blocks the calling thread until shared ownership of the mutex can be obtained for the
calling thread."

My reading of "shared ownership of the mutex can be obtained": if shared ownership would
be acquired for the current execution agent, then the condition described in the paragraph
[thread.sharedmutex.requirements.general]/2 would hold.

https://eel.is/c++draft/thread.mutex#thread.sharedmutex.requirements.general-2

"Multiple execution agents can simultaneously hold a shared lock ownership of a shared
mutex type. But no execution agent holds a shared lock while another execution agent holds
an exclusive lock on the same shared mutex type, and vice-versa."

This suggests that if there is no exclusive lock on smtx then smtx.lock_shared() in
reader_2 does not block.

Is my reading correct?

Is this a bug in libc++ or a defect in the standard? Notably the the reference
implementation in N2406 also has the same blocking behaviour.

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html

Cheers,
Lénárd

Received on 2025-04-29 14:30:30