31

The new std::shared_timed_mutex allows for two types of locks: shared and exclusive.

If one holds a shared lock, is there any way to atomically exchange it ("upgrade it") to an exclusive lock? In other words, given the following code, how can I avoid the non-atomic drop and re-lock?

std::shared_timed_mutex m; //Guards a std::vector.
m.lock_shared();

//Read from vector. (Shared lock is sufficient.)
// ...

//Now we want to write to the vector. We need an exclusive lock.
m.unlock_shared();
                   //     <---- Problem here: non-atomic! 
m.lock(); 

//Write to vector.
// ...

m.unlock();

Ideally, m.unlock_shared(); m.lock(); could be replaced with something like m.upgrade_to_exclusive(); (or something like the boost::.upgrade_to_unique_lock()).

In a similar question but for Boost's shared_mutex Dave S mentions

It is impossible to convert from a shared lock to a unique lock, or a shared lock to an upgradeable lock without releasing the shared lock first.

I'm not certain whether this applies to std::shared_mutex, though I suspect it does.

I would be happy with a reasonable work-around based on std::atomic/condition_variable or GCC's transactional memory.

Edit: Howard's answer addresses my question. His proposal N3427 contains nice descriptions of a mechanism to achieve mutex upgrading. I still welcome work-arounds based on std::atomic/condition_variable or GCC's transactional memory.

Community
  • 1
  • 1
user3761401
  • 378
  • 3
  • 10
  • 2
    seems std::shared_mutex cannot, but [boost::shared_mutex can!](https://stackoverflow.com/a/989816/1067003) – hanshenrik Sep 01 '18 at 11:53

1 Answers1

31

No, it can not. That functionality was proposed to the committee under the name upgrade_mutex and upgrade_lock, but the committee chose to reject that portion of the proposal. There is currently no work under way to re-prepose that functionality.

Edit

In response to the "where to go from here" edit in user3761401's question, I've created a partially crippled implementation of upgrade_mutex/upgrade_lock here:

https://github.com/HowardHinnant/upgrade_mutex

Feel free to use this. It is in the public domain. It is only lightly tested, and it does not have the full functionality described in N3427. Specifically the following functionality is missing:

  • One can not convert a unique_lock to a shared_timed_lock.
  • One can not try- or timed-convert a shared_timed_lock to a unique_lock.
  • One can not try- or timed-convert a upgrade_lock to a unique_lock.

That being said, I've included this functionality in upgrade_mutex and it can be accessed at this low level in a very ugly manner (such examples are in main.cpp).

The other lock conversions mentioned in N3427 are available.

  • try- and timed-conversions from shared_timed_lock to upgrade_lock.
  • conversion from upgrade_lock to shared_timed_lock.
  • blocking conversion from upgrade_lock to unique_lock.
  • conversion from unique_lock to upgrade_lock.

It has all been put in namespace acme. Put it in whatever namespace you want.

Requirements

The compiler needs to support "rvalue-this" qualifiers, and explicit conversion operators.

Disclaimers

The code has been only lightly tested. If you find bugs I would appreciate a pull request.

It is possible to optimize the upgrade_mutex through the use of std::atomic. No effort has been done on that front (it is a difficult and error prone task, taking more time than I have at the moment).

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • Thanks for the insight. I see [your proposal N3427](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3427.html), specifically the section beginning "Overview for upgrade locking" which addresses my question exactly. Are you aware of the rationale for the rejection? – user3761401 Jun 20 '14 at 22:22
  • 1
    Unfortunately I missed that meeting. So I don't know the details. It was also proposed for C++11 in the 2007 time frame, and rejected at that time because we needed to eliminate material so that we could ship C++09 on time (which didn't happen). – Howard Hinnant Jun 21 '14 at 07:04
  • @user3761401 Probably because it was desired to allow backing `shared_mutex` with a `SRWLOCK`, which does not provide that feature. – Billy ONeal Jul 07 '16 at 00:32
  • 3
    @BillyONeal: If that was the rationale, it was naively mistaken. `upgrade_mutex` is 100% independent of `shared_mutex`. VS could still have a `shared_mutex` backed by `SRWLOCK` in addition to an `upgrade_mutex`. – Howard Hinnant Jul 07 '16 at 00:44
  • @Howard: Oh, I see, under a different mutex type. Nevermind then – Billy ONeal Jul 07 '16 at 00:45
  • Some days I just need to keep my damn mouth shut. – Billy ONeal Jul 07 '16 at 00:52
  • 1
    @HowardHinnant was this re-proposed for c++17? It seems to me that it's a glaring hole in c++'s thread support library, and seemingly easily plugged. – Richard Hodges Sep 12 '16 at 09:40
  • 3
    @RichardHodges: No, it was not. No one on the committee expressed interest in having it. It is now too late to propose new features for C++17, however there is always a next standard (C++2x). If you would like to propose it, I can help with the logistics of how to do that. Proposals have the most success if you can present the proposal in person. – Howard Hinnant Sep 12 '16 at 14:33