executors / futures

A proposal for a futures programming model for ISO C++
22 stars 7 forks source link

Support allocators in the standard types that we provide #119

Open griwes opened 6 years ago

brycelelbach commented 6 years ago

Note that this is just for std::execution::semi_future<T>, etc.

brycelelbach commented 6 years ago

@LeeHowes, please review, comment, discuss with @griwes and remove the discussion label when you have consensus.

@griwes, can you implement this?

LeeHowes commented 6 years ago

I think doing so makes sense. They will presumably need to allocate shared state so giving control of how that happens is a good idea.

Do we want to type erase the allocator?

griwes commented 6 years ago

I don't think we want to type erase the allocator. We can provide pmr aliases if someone deems that necessary. (Or people can just use polymorphic_allocator.)

I'll sit down to write the wording once I've given my talk.

LeeHowes commented 6 years ago

I think having an allocator template param on those types is fine.

When we construct off of an ContinuableFuture/SemiFuture concept implementation (or execution::promise off of a Promise) we should be clear that we don't assume that that concept also has an allocator. This would be for the shared state needed to allocate storage for that wrapping.

dhollman commented 6 years ago

Shouldn't this basically default to execution::query(fut.get_executor(), execution::allocator)? In other words, it's not really the Future allocating the shared state, but the executor, and that already has an allocator customization point.

I also think we should be clear that the concepts should not be required to support allocators (I know this isn't what you're suggesting here, but it's an important distinction to make). This also means that if the standard types we provide are to act as type-erasers, the type-erasing constructors, etc., should not be required to interoperate with allocators.

griwes commented 6 years ago

Concepts should not require supporting allocators, yes.

For the std::execution::semi_future and whatnot, when it's returned by the executor - sure, but sometimes you'll get it by other means, hence why std::execution::semi_future should support allocators for the type-erased case (and when returned from the default query, you could just propagate the allocator from the executor into the future) - because then it's the future itself that does the allocation of the shared state that does the erasure.

dhollman commented 6 years ago

sure, but sometimes you'll get it by other means

I'm still not convinced of the necessity of "getting it by other means," especially if the goal of this proposal is still to provide something for executors to interoperate with.

That minor soapbox aside, though, can't you just defer any allocation until it's bound to an executor? What allocation would need to be done for a semi_future that has never been bound to any executor? (If it comes from continuable_future::semi(), allocation will have been done in the presence of an executor there also).

griwes commented 6 years ago

"Other means" means constructing a type-erased wrapper. And no, you can't defer until being bound, because once you exit the templated constructor semi_future::semi_future(SemiFuture &&), you know longer know what the type was. You need to somehow save a typed shared state that refers to a type known only inside the constructor.

dhollman commented 6 years ago

Ah, good point. I was not thinking about the allocation of the type-erased wrapper itself.

LeeHowes commented 6 years ago

So I think we want an allocator parameter for:

via, then, make_promise_contract with executor would all use the target executor's allocator of they allocate.

and an allocator type in the promise/semi_future/continuable_future template parameter list.