std :: promise set_exception两次导致分段错误

时间:2019-04-30 07:05:11

标签: c++ c++14 c++17 std-future

假设我有一个方法可以调用不稳定的第三方服务,因此我为此调用添加了一个超时时间,例如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秒后超时的异常),status == std::future_status::timeout在等待10秒后命中,然后promise.set_exception正常工作,一切看起来都很好。但是,当call_third_party_service引发异常时,再次promise.set_exception,因此出现分段错误。实现此模式的正确方法是什么?

1 个答案:

答案 0 :(得分:4)

根据Frax的建议,您应该将promise移到lambda中,并在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;
}