16

I am wondering why we need both std::promise and std::future ? why c++11 standard divided get and set_value into two separate classes std::future and std::promise? In the answer of this post, it mentioned that :

The reason it is separated into these two separate "interfaces" is to hide the "write/set" functionality from the "consumer/reader".

I don't understand the benefit of hiding here. But isn't it simpler if we have only one class "future"? For example: promise.set_value can be replaced by future.set_value.

Community
  • 1
  • 1
camino
  • 10,085
  • 20
  • 64
  • 115
  • 6
    Why do you think smashing them together is a good idea? Why would someone want to both read and write the result? – T.C. Dec 09 '15 at 02:15
  • 1
    currently if we want to retrieve the value from future, we call promise.get_future().get(), if we want to set the value, we call promise.set_value(). I am just wondering we need the additional operation. – camino Dec 09 '15 at 02:25
  • @camino Well, as T.C. said too, there is no real reason to provide a shortcut to get a value which should be known anyways. – deviantfan Dec 09 '15 at 02:36
  • @camino: If you have a promise, you should never be the one retrieving the value. Especially since you can only ever call `get_future` *once*. – Nicol Bolas Dec 09 '15 at 02:51
  • FWIW, original 2006 proposals for std::future allowed both setting and retrieving the value: Peter Dimov's [n2096](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2096.html) and Howard Hinnant's [n2094](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2094.html). It was Anthony Williams's [n2276](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2276.html) when future lost set_value, but got two alternative ways to put the value in: a packaged_task and a promise. – Cubbi Dec 09 '15 at 15:25

2 Answers2

36

The problem that promise/future exist to solve is to shepherd a value from one thread to another. It may also transfer an exception instead.

So the source thread must have some object that it can talk to, in order to send the desired value to the other thread. Alright... who owns that object? If the source has a pointer to something that the destination thread owns, how does the source know if the destination thread has deleted the object? Maybe the destination thread no longer cares about the value; maybe something changed such that it decided to just drop your thread on the floor and forget about it.

That's entirely legitimate code in some cases.

So now the question becomes why the source doesn't own the promise and simply give the destination a pointer/reference to it? Well, there's a good reason for that: the promise is owned by the source thread. Once the source thread terminates, the promise will be destroyed. Thus leaving the destination thread with a reference to a destroyed promise.

Oops.

Therefore, the only viable solution is to have two full-fledged objects: one for the source and one for the destination. These objects share ownership of the value that gets transferred. Of course, that doesn't mean that they couldn't be the same type; you could have something like shared_ptr<promise> or somesuch. After all, promise/future must have some shared storage of some sort internally, correct?

However, consider the interface of promise/future as they currently stand.

promise is non-copyable. You can move it, but you can't copy it. future is also non-copyable, but a future can become a shared_future that is copyable. So you can have multiple destinations, but only one source.

promise can only set the value; it can't even get it back. future can only get the value; it cannot set it. Therefore, you have an asymmetric interface, which is entirely appropriate to this use case. You don't want the destination to be able to set the value and the source to be able to retrieve it. That's backwards code logic.

So that's why you want two objects. You have an asymmetric interface, and that's best handled with two related but separate types and objects.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
8

I would think of a promise/future as an asynchronous queue (that's only intended to hold a single value).

The future is the read end of the queue. The promise is the write end of the queue.

The use of the two is normally distinct: the producer normally just writes to the "queue", and the consume just reads from it. Although, as you've noted, it's possible for a producer to read the value, there's rarely much reason for it to do that, so optimizing that particular operation is rarely seen as much of a priority.

In the usual scheme of things, the producer produces the value, and puts it into the promise. The consumer gets the value from the future. Each "client" uses one simple interface dedicated exclusively to one simple task, so it's easier to design and document the code, as well as ensuring that (for example) the consumer code doesn't mess with something related to producing the value (or vice versa). Yes, it's possible to do that, but enough extra work that it's fairly unlikely to happen by accident.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111