Обработка исключений для <mutex> и <condition_variable>

Полагая

  • no undefined,
  • нет взаимоблокировок,
  • мьютексы заблокированы и разблокированы в правильном порядке правильными потоками правильное количество раз,
  • нерекурсивные мьютексы не блокируются несколько раз,
  • блокировка рекурсивных мьютексов не превышает максимального уровня владения,
  • предикаты не передаются в переменные условия throw, а
  • используются только часы, точки времени и длительности, предоставляемые стандартной библиотекой, с мьютексами std:: и переменными условия

гарантировано, что работа над различными типами мутетексов std:: и переменными условия (кроме их построения) не генерирует никаких исключений (особенно типа std::system_error)?

Например, в случае таких методов, как:

void MyClass::setVariable() {
    std::lock_guard<std::mutex> const guard(m_mutex);
    m_var = 42; // m_var is of type int
    m_conditionVariable.notify_all();
}

void MyClass::waitVariable() {
    std::unique_lock<std::mutex> lock(m_mutex);
    m_conditionVariable.wait(lock, [this]() noexcept { return m_var == 42; });
}

Можно ли предположить noexcept или написать несколько блоков try-catch вокруг звонков? Или есть какие-либо оговорки?

Пожалуйста, рассмотрите все типы мьютексов и переменных условий в С++ 11, С++ 14 и более поздних версиях.

Ответ 1

Благодарим ссылка T.C. теперь я бы сказал, что да - ваш код должен быть в безопасности. Так как в будущем стандарт device_or_resource_busy будет удален, и, как автор проблемы говорит, что эта ситуация не может произойти разумным образом, есть только 2 возможности для lock:

(13.1) - operation_not_permitted - если поток не имеет привилегии для выполнения операции.

(13.2) - resource_deadlock_would_occur - если реализация обнаруживает что произойдет тупик.

И обе эти ситуации исключаются из ваших предварительных условий. Поэтому ваш код должен быть безопасным для использования noexcept.

Ответ 2

Короткий ответ: Нет (извините)

Любая из этих операций будет бросать std::system_error, если основной объект синхронизации не сможет выполнить свою операцию.

Это связано с тем, что правильная работа примитивов синхронизации зависит от:

  • доступные системные ресурсы.

  • какая-то другая часть программы не отменяет примитив

Несмотря на справедливость, если (1) происходит, вероятно, пора перепроектировать приложение или запустить его на менее загруженной машине.

И если (2) происходит, программа не логически последовательна.

При этом

или нужно написать несколько блоков try-catch вокруг звонков?

Также нет.

Вы должны написать блоки try/catch при следующих условиях:

  • Если программа в состоянии сделать что-то полезное о состоянии ошибки (например, отремонтировать ее или попросить пользователя, хочет ли он повторить попытку)

  • Вы хотели бы добавить некоторую информацию об ошибке и повторно бросить ее, чтобы предоставить диагностический маршрут (вложенные исключения, например)

  • Вы хотите зарегистрировать сбой и продолжить.

В противном случае вся обработка исключений С++ заключается в том, что вы разрешаете RAII позаботиться о повторном сбое ресурсов и разрешить исключению перетекать в стек вызовов, пока не найдет обработчик, который хочет обработать его.

пример создания дорожки для панировки:

void wait_for_object()
try
{
    _x.wait();  // let say it throws a system_error on a loaded system
}
catch(...)
{
  std::throw_with_nested(std::runtime_error(__func__));
}