I know it's possible using boost::UpgradeLockable
in C++14.
Is there anything similar for C++11?

- 378,754
- 76
- 643
- 1,055

- 5,721
- 9
- 60
- 115
-
1unhappily not until c++17: http://en.cppreference.com/w/cpp/thread/shared_mutex – Richard Hodges Jan 25 '16 at 14:05
-
@RichardHodges I don't see how that link is relevant. Where in that interface does that allow lock upgrading? – Yakk - Adam Nevraumont Jan 25 '16 at 14:23
-
@Yakk hmm you're right. This begs the question then, is the standard proposal missing something, or is an upgadable lock simply unnecessary? – Richard Hodges Jan 25 '16 at 14:29
-
@RichardHodges If it can be done as efficiently in client code as it could be done in `std`, should we put it in `std` at this point? Maybe. – Yakk - Adam Nevraumont Jan 25 '16 at 14:40
-
I don't get your question. If you are going to use Boost.Threads, then why do you need C++14? Boost.Threads has had an upgradable `shared_mutex` for many years and requires only C++98. – Arne Vogel Jan 25 '16 at 16:02
1 Answers
An upgradeable lock can be written on top of simpler locking primitives.
struct upgradeable_timed_mutex {
void lock() {
upgradable_lock();
upgrade_lock();
}
void unlock() {
upgrade_unlock();
upgradable_unlock();
}
void shared_lock() { shared.shared_lock(); }
void shared_unlock() { shared.shared_unlock(); }
void upgradable_lock() { unshared.lock(); }
void ungradable_unlock() { unshared.unlock(); }
void upgrade_lock() { shared.lock(); }
void upgrade_unlock() { shared.unlock(); }
private:
friend struct upgradable_lock;
std::shared_timed_mutex shared;
std::timed_mutex unshared;
};
and similar for the timed and try variants. Note that timed variants which access two mutexes in a row have to do some extra work to avoid spending up to 2x the requested time, and try_lock
has to be careful about the first lock's state in case the 2nd fails.
Then you have to write upgradable_lock
, with the ability to spawn a std::unique_lock
upon request.
Naturally this is hand-written thread safety code, so it is unlikely to be correct.
In C++1z you can write an untimed version as well (with std::shared_mutex
and std::mutex
).
Less concretely, there can be exactly one upgradeable or write lock at a time. This is what the unshared
mutex represents.
So long as you hold unshared
, nobody else is writing to the guarded data, so you can read from it without holding the shared mutex at all.
When you want to upgrade, you can grab a unique lock on the shared mutex. This cannot deadlock so long as no readers try to upgrade to upgradable. This excludes readers from reading, you can write, and then you release it and return back to a read only state (where you only hold the unshared mutex).

- 262,606
- 27
- 330
- 524
-
wouldn't this solution need to drop the shared_lock before seeking a unique_lock of the mutex called `shared` when upgrading? In which case the second mutex `unshared` would be redundant, or am I missing something? – Richard Hodges Jan 25 '16 at 14:36
-
1@RichardHodges The upgradable lock does not own a shared lock at all. It just owns a unique_lock on the unshared mutex. It then directly gets a unique_lock on the shared mutex when it upgrades. The point of this is you do NOT lose your read-lock when upgrading. – Yakk - Adam Nevraumont Jan 25 '16 at 14:41