Lifetime issues of std::promise in an async API

PeppeDx

I'm wondering how to develop an asynchronous API using promises and futures. The application is using a single data stream that is used for both unsolicited periodic data and requesty/reply communication.

For the requesty/reply blocking until the reply is received is not an option and I don't want lo litter the code using callbacks, so I'd like to write some kind of a SendMessage that accepts the id of the expected reply and exits only upon reception. It's up to the caller to read the reply.

A candidate API could be:

std::future<void> sendMessage(Message msg, id expected)
{
   // Write the message
   auto promise = make_shared<std::promise<void>>();
   // Memorize the promise somewhere accessible to the receiving thread
   return promise->get_future();
 }

The worker thread upon reception of a message should be able to query a data-structure to know if there is someone waiting for it and "release" the future.

Given that promises are not re-usable what I'm trying to understand is what kind of data-structure should I use to manage "in flight" promises.

felix

This answer has been rewritten.

Setting the state of a shared flag can enable the worker to know whether the other side, say boss, is still expecting the result.

The shared flag along with the promise and the future can be enclosed into a class (template), say Request. The boss set the flag by destructing his copy of the request. And the worker query whether the boss is still expecting the request being done by calling certain member function on his own copy of the request.

Simultaneous reading/writing on the flag should be probably synchronized.

The boss may not access the promise and the worker may not access the future.

There should be at most two copies of the request, becaue the flag will be set on the destruction of the request object. For achieving this, we can delcare corresponding member functions as delete or private, and provide two copies of the request on construction.

Here follows a simple implementation of request:

#include <atomic>
#include <future>
#include <memory>

template <class T>
class Request {
 public:
  struct Detail {
   std::atomic<bool> is_canceled_{false};
   std::promise<T> promise_;
   std::future<T> future_ = promise_.get_future();
  };

  static auto NewRequest() {
    std::unique_ptr<Request> copy1{new Request()};
    std::unique_ptr<Request> copy2{new Request(*copy1)};

    return std::make_pair(std::move(copy1), std::move(copy2));
  }

  Request(Request &&) = delete;

  ~Request() {
    detail_->is_canceled_.store(true);
  }

  Request &operator=(const Request &) = delete;
  Request &operator=(Request &&) = delete;

  // simple api
  std::promise<T> &Promise(const WorkerType &) {
    return detail_->promise_;
  }
  std::future<T> &Future(const BossType &) {
    return detail_->future_;
  }

  // return value:
  // true if available, false otherwise
  bool CheckAvailable() {
    return detail_->is_canceled_.load() == false;
  }

 private:
  Request() : detail_(new Detail{}) {}
  Request(const Request &) = default;

  std::shared_ptr<Detail> detail_;
};

template <class T>
auto SendMessage() {
  auto result = Request<T>::NewRequest();
  // TODO : send result.second(the another copy) to the worker
  return std::move(result.first);
}

New request is contructed by factroy function NewRequest, the return value is a std::pair which contains two std::unique_ptr, each hold a copy of the newly created request.

The worker can now use the member function CheckAvailable() to check whether the request is canceled.

And the shared state is managed proprely(I believe) by the std::shared_ptr.

Note on std::promise<T> &Promise(const WorkerType &): The const reference parameter(which should be replaced with a propre type according to your implementation) is for preventing the boss from calling this function by accident while the worker should be able to easily provide a propre argument for calling this function. The same for std::future<T> &Future(const BossType &).

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related