Можно ли обновить общую блокировку на std:: shared_timed_mutex до эксклюзивной блокировки?

Новый std:: shared_timed_mutex позволяет использовать два типа блокировок: общий и эксклюзивный.

Если у вас есть общий замок, есть ли способ его атомарного обмена ( "обновить его" ) до эксклюзивной блокировки? Другими словами, учитывая следующий код, как я могу избежать неатомной капли и повторной блокировки?

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();

В идеале m.unlock_shared(); m.lock(); можно заменить чем-то вроде m.upgrade_to_exclusive(); (или что-то вроде boost::.upgrade_to_unique_lock()).

В аналогичном вопросе, но для Boost shared_mutex Dave S упоминает

Невозможно преобразовать из общей блокировки в уникальную блокировку или общую блокировку в обновляемую блокировку, не отпуская сначала общую блокировку.

Я не уверен, относится ли это к std:: shared_mutex, хотя я подозреваю, что это так.

Я был бы доволен разумной практикой, основанной на транзакционной памяти std:: atomic/condition_variable или GCC.

Изменить: Ответ Говарда отвечает на мой вопрос. Его предложение N3427 содержит приятные описания механизма для обновления мьютекса. Я по-прежнему приветствую обходы, основанные на транзакционной памяти std:: atomic/condition_variable или GCC.

Ответ 1

Нет, не может. Эта функциональность была предложена комитету под названием upgrade_mutex и upgrade_lock, но комитет решил отклонить эту часть предложения. В настоящее время не проводится никакой работы, чтобы повторно использовать эту функциональность.

Edit

В ответ на вопрос "куда идти отсюда" отредактируйте в user3761401 вопрос, я создал частично искалеченную реализацию upgrade_mutex/upgrade_lock здесь:

https://github.com/HowardHinnant/upgrade_mutex

Не стесняйтесь использовать это. Он находится в общественном достоянии. Он только слегка протестирован и не обладает полной функциональностью, описанной в N3427. В частности, отсутствуют следующие функции:

  • Невозможно преобразовать unique_lock в shared_timed_lock.
  • Невозможно выполнить попытку или время-преобразовать a shared_timed_lock в unique_lock.
  • Невозможно выполнить попытку или timed-преобразовать a upgrade_lock в unique_lock.

Сказав это, я включил эту функцию в upgrade_mutex, и ее можно получить на этом низком уровне очень уродливо (такие примеры приведены в main.cpp).

Доступны другие преобразования блокировок, указанные в N3427.

  • выбор времени и времени от shared_timed_lock до upgrade_lock.
  • от upgrade_lock до shared_timed_lock.
  • блокировка преобразования от upgrade_lock до unique_lock.
  • от unique_lock до upgrade_lock.

Все было помещено в namespace acme. Поместите его в любое пространство имен, которое вы хотите.

Требования

Компилятор должен поддерживать "rvalue-this" квалификаторы и явные операторы преобразования.

Отказ

Код был только слегка протестирован. Если вы найдете ошибки, я был бы признателен за запрос на перенос.

Можно оптимизировать upgrade_mutex с помощью std::atomic. На этом фронте не было предпринято никаких усилий (это сложная задача с ошибкой, которая занимает больше времени, чем у меня на данный момент).