std :: обещание set_exception дважды вызывает ошибку сегментации - PullRequest
3 голосов
/ 30 апреля 2019

Допустим, у меня есть метод, который вызывает нестабильную стороннюю службу, поэтому я добавляю тайм-аут для этого вызова, скажем, 10 секунд.Вот что я попробовал:

int process()
{
    std::promise<int> promise;
    std::future<int> future = promise.get_future();

    std::thread([&]
    {
        try
        {
            int result = call_third_party_service();
            promise.set_value(result);
        }
        catch (std::exception&) //call_thrid_party_service can throw exceptions
        {
            promise.set_exception(std::current_exception());
        }
    }).detach();

    auto status = future.wait_for(std::chrono::seconds(10));
    if (status == std::future_status::timeout)
    {
        promise.set_exception(time_out_exception);
    }

    return future.get();
}

int main()
{
    try
    {
        int result = process();
    }
    catch(const std::exception& e)
    {
        //print
    }

    //blocks the thread to see what happens
    std::this_thread::sleep_for(std::chrono::minutes(1));        
    return 0;
}

Когда call_third_party_service не отвечает (предположим, оно выдает исключение, сообщающее об истечении времени ожидания через 30 секунд), * ​​1005 * срабатывает после ожидания 10 секунд, затем promise.set_exception работаети все выглядит хорошо.Однако, когда call_third_party_service выдает исключение, promise.set_exception снова, следовательно, ошибка сегментации.Как правильно реализовать этот шаблон?

1 Ответ

4 голосов
/ 30 апреля 2019

Как подсказывает Frax, вы должны переместить promise в лямбду и выбросить исключение сразу, когда future истечет:

int process() {
  std::promise<int> promise;
  std::future<int> future = promise.get_future();

  // move ownership of the promise into thread
  std::thread([prom = std::move(promise)]() mutable {
    try {
      int result = call_third_party_service();
      prom.set_value(result);
    } catch (std::exception&)  // call_thrid_party_service can throw exceptions
    {
      prom.set_exception(std::current_exception());
    }
  }).detach();

  auto status = future.wait_for(std::chrono::seconds(10));
  if (status == std::future_status::timeout) {
    // This exception is not part of an asynchronous computation and 
    // should be thrown immediately
    throw time_out_exception("timed out");
  }

  return future.get();
}

int main() {
  try {
    int result = process();
  } catch (const std::exception& e) {
    // print
  }

  // blocks the thread to see what happens
  std::this_thread::sleep_for(std::chrono::minutes(1)); 
  return 0;
}
...