dispenso
A library for task parallelism
 
Loading...
Searching...
No Matches
future.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
16#pragma once
17
18#include <functional>
19#include <vector>
20
21#include <dispenso/detail/future_impl.h>
22#include <dispenso/detail/result_of.h>
24
25namespace dispenso {
26
27// See https://en.cppreference.com/w/cpp/experimental/future for details on the API.
28
29// TODO(bbudge): Implement when_any(), ?unwrapping constructor? functionality.
30
35constexpr std::launch kNotAsync = static_cast<std::launch>(0);
41constexpr std::launch kNotDeferred = static_cast<std::launch>(0);
42
68template <typename Result>
69class Future : detail::FutureBase<Result> {
70 using Base = detail::FutureBase<Result>;
71
72 public:
76 Future() noexcept : Base() {}
77
83 Future(Future&& f) noexcept : Base(std::move(f)) {}
84 Future(Base&& f) noexcept : Base(std::move(f)) {}
85
92 Future(const Future& f) noexcept : Base(f) {}
93 Future(const Base& f) noexcept : Base(f) {}
94
109 template <typename F, typename Schedulable>
111 F&& f,
113 std::launch asyncPolicy = kNotAsync,
114 std::launch deferredPolicy = std::launch::deferred)
115 : Base(std::forward<F>(f), schedulable, asyncPolicy, deferredPolicy) {}
116
122 Future& operator=(Future&& f) noexcept {
123 Base::move(reinterpret_cast<Base&&>(f));
124 return *this;
125 }
132 Base::copy(f);
133 return *this;
134 }
135
140 ~Future() = default;
141
151 return Base::valid();
152 }
153
161 bool is_ready() const {
162 return Base::is_ready();
163 }
164
170 void wait() const {
171 Base::wait();
172 }
173
185 template <class Rep, class Period>
186 std::future_status wait_for(const std::chrono::duration<Rep, Period>& timeoutDuration) const {
187 return Base::wait_for(timeoutDuration);
188 }
189
200 template <class Clock, class Duration>
201 std::future_status wait_until(const std::chrono::time_point<Clock, Duration>& timeoutTime) const {
202 return Base::wait_until(timeoutTime);
203 }
204
210 return std::move(*this);
211 }
212
218 const Result& get() const {
219 wait();
220 return this->impl_->result();
221 }
222
238 template <typename F, typename Schedulable>
240 F&& f,
242 std::launch asyncPolicy = kNotAsync,
243 std::launch deferredPolicy = std::launch::deferred) {
246 std::forward<F>(f), sched, asyncPolicy, deferredPolicy);
247 return retFuture;
248 }
249 template <typename F>
251 return then(std::forward<F>(f), globalThreadPool(), kNotAsync, std::launch::deferred);
252 }
253
254 private:
255 template <typename T>
256 Future(T&& t, detail::ReadyTag) {
257 this->impl_ = detail::createValueFutureImplReady<Result>(std::forward<T>(t));
258 }
259
260 template <typename T>
261 friend Future<std::decay_t<T>> make_ready_future(T&& t);
262
263 template <typename R>
264 friend class Future;
265};
266
267template <typename Result>
268class Future<Result&> : detail::FutureBase<Result&> {
269 using Base = detail::FutureBase<Result&>;
270
271 public:
272 Future() noexcept : Base() {}
273 Future(Future&& f) noexcept : Base(std::move(f)) {}
274 Future(Base&& f) noexcept : Base(std::move(f)) {}
275 Future(const Future& f) noexcept : Base(f) {}
276 Future(const Base& f) noexcept : Base(f) {}
277 template <typename F, typename Schedulable>
278 Future(
279 F&& f,
281 std::launch asyncPolicy = kNotAsync,
282 std::launch deferredPolicy = std::launch::deferred)
283 : Base(std::forward<F>(f), schedulable, asyncPolicy, deferredPolicy) {}
284 Future& operator=(Future&& f) noexcept {
285 Base::move(reinterpret_cast<Base&&>(f));
286 return *this;
287 }
288 Future& operator=(const Future& f) {
289 Base::copy(f);
290 return *this;
291 }
292 ~Future() = default;
293 using Base::is_ready;
294 using Base::valid;
295 using Base::wait;
296 using Base::wait_for;
297 using Base::wait_until;
298
299 Future share() {
300 return std::move(*this);
301 }
302
308 Result& get() const {
309 wait();
310 return this->impl_->result();
311 }
312
313 template <typename F, typename Schedulable>
315 F&& f,
317 std::launch asyncPolicy = kNotAsync,
318 std::launch deferredPolicy = std::launch::deferred) {
321 std::forward<F>(f), sched, asyncPolicy, deferredPolicy);
322 return retFuture;
323 }
324 template <typename F>
325 Future<detail::ResultOf<F, Future<Result&>&&>> then(F&& f) {
326 return then(std::forward<F>(f), globalThreadPool(), kNotAsync, std::launch::deferred);
327 }
328
329 private:
330 template <typename T>
331 Future(std::reference_wrapper<T> t, detail::ReadyTag) {
332 this->impl_ = detail::createRefFutureImplReady<Result>(t);
333 }
334
335 template <typename X>
336 friend Future<X&> make_ready_future(std::reference_wrapper<X> x);
337
338 template <typename R>
339 friend class Future;
340};
341
342template <>
343class Future<void> : detail::FutureBase<void> {
344 using Base = detail::FutureBase<void>;
345
346 public:
347 Future() noexcept : Base() {}
348 Future(Future&& f) noexcept : Base(std::move(f)) {}
349 Future(Base&& f) noexcept : Base(std::move(f)) {}
350 Future(const Future& f) noexcept : Base(f) {}
351 Future(const Base& f) noexcept : Base(f) {}
352 template <typename F, typename Schedulable>
353 Future(
354 F&& f,
356 std::launch asyncPolicy = kNotAsync,
357 std::launch deferredPolicy = std::launch::deferred)
358 : Base(std::forward<F>(f), schedulable, asyncPolicy, deferredPolicy) {}
359 Future& operator=(Future&& f) noexcept {
360 Base::move(reinterpret_cast<Base&&>(f));
361 return *this;
362 }
363 Future& operator=(const Future& f) {
364 Base::copy(f);
365 return *this;
366 }
367 ~Future() = default;
368 using Base::is_ready;
369 using Base::valid;
370 using Base::wait;
371 using Base::wait_for;
372 using Base::wait_until;
373
374 Future share() {
375 return std::move(*this);
376 }
377
381 void get() const {
382 wait();
383 this->impl_->result();
384 }
385
386 template <typename F, typename Schedulable>
388 F&& f,
390 std::launch asyncPolicy = kNotAsync,
391 std::launch deferredPolicy = std::launch::deferred) {
393 retFuture.impl_ = this->template thenImpl<detail::ResultOf<F, Future<void>&&>>(
394 std::forward<F>(f), sched, asyncPolicy, deferredPolicy);
395 return retFuture;
396 }
397 template <typename F>
398 Future<detail::ResultOf<F, Future<void>&&>> then(F&& f) {
399 return then(std::forward<F>(f), globalThreadPool(), kNotAsync, std::launch::deferred);
400 }
401
402 private:
403 Future(detail::ReadyTag) {
404 impl_ = detail::createVoidFutureImplReady();
405 }
406
407 friend Future<void> make_ready_future();
408
409 template <typename R>
410 friend class Future;
411};
412
413// TODO(bbudge): Determine if we should
414// a. Expand launch policies, and logically inherit from std::launch and
415// b. Whether async should truly mean on a new thread.
416// For now we will treat std::launch::async such that we pass ForceQueuingTag
417
428template <class F, class... Args>
429inline Future<detail::ResultOf<F, Args...>> async(std::launch policy, F&& f, Args&&... args) {
430 return Future<detail::ResultOf<F, Args...>>(
431 std::bind(std::forward<F>(f), std::forward<Args>(args)...), globalThreadPool(), policy);
432}
433
440template <class F, class... Args>
441inline Future<detail::ResultOf<F, Args...>> async(F&& f, Args&&... args) {
442 return ::dispenso::async(std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...);
443}
444
456template <class F, class... Args>
457inline Future<detail::ResultOf<F, Args...>>
458async(ThreadPool& pool, std::launch policy, F&& f, Args&&... args) {
459 return Future<detail::ResultOf<F, Args...>>(
460 std::bind(std::forward<F>(f), std::forward<Args>(args)...), pool, policy);
461}
462
470template <class F, class... Args>
471inline Future<detail::ResultOf<F, Args...>> async(ThreadPool& pool, F&& f, Args&&... args) {
472 return ::dispenso::async(
473 pool, std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...);
474}
475
487template <class F, class... Args>
488inline Future<detail::ResultOf<F, Args...>>
489async(TaskSet& tasks, std::launch policy, F&& f, Args&&... args) {
490 return Future<detail::ResultOf<F, Args...>>(
491 std::bind(std::forward<F>(f), std::forward<Args>(args)...), tasks, policy);
492}
493
501template <class F, class... Args>
502inline Future<detail::ResultOf<F, Args...>> async(TaskSet& tasks, F&& f, Args&&... args) {
503 return ::dispenso::async(
504 tasks, std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...);
505}
506
518template <class F, class... Args>
519inline Future<detail::ResultOf<F, Args...>>
520async(ConcurrentTaskSet& tasks, std::launch policy, F&& f, Args&&... args) {
521 return Future<detail::ResultOf<F, Args...>>(
522 std::bind(std::forward<F>(f), std::forward<Args>(args)...), tasks, policy);
523}
524
532template <class F, class... Args>
533inline Future<detail::ResultOf<F, Args...>> async(ConcurrentTaskSet& tasks, F&& f, Args&&... args) {
534 return ::dispenso::async(
535 tasks, std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...);
536}
537
549template <class F, class... Args>
550inline Future<detail::ResultOf<F, Args...>>
551async(NewThreadInvoker sched, std::launch policy, F&& f, Args&&... args) {
552 return Future<detail::ResultOf<F, Args...>>(
553 std::bind(std::forward<F>(f), std::forward<Args>(args)...), sched, policy);
554}
555
563template <class F, class... Args>
564inline Future<detail::ResultOf<F, Args...>> async(NewThreadInvoker sched, F&& f, Args&&... args) {
565 return ::dispenso::async(
566 sched, std::launch::deferred, std::forward<F>(f), std::forward<Args>(args)...);
567}
568
575template <typename T>
577 return Future<std::decay_t<T>>(std::forward<T>(t), detail::ReadyTag());
578}
579
586template <typename X>
587inline Future<X&> make_ready_future(std::reference_wrapper<X> x) {
588 return Future<X&>(x, detail::ReadyTag());
589}
590
596 return Future<void>(detail::ReadyTag());
597}
598
610template <class InputIt>
613 InputIt last);
614
625template <class... Futures>
627
641template <class InputIt>
644template <class InputIt>
647
660template <class... Futures>
663
664template <class... Futures>
667} // namespace dispenso
668
669#include <dispenso/detail/future_impl2.h>
Result & get() const
Definition future.h:308
bool is_ready() const
Definition future.h:161
~Future()=default
std::future_status wait_until(const std::chrono::time_point< Clock, Duration > &timeoutTime) const
Definition future.h:201
void wait() const
Definition future.h:170
friend Future< std::decay_t< T > > make_ready_future(T &&t)
Definition future.h:576
std::future_status wait_for(const std::chrono::duration< Rep, Period > &timeoutDuration) const
Definition future.h:186
const Result & get() const
Definition future.h:218
Future share()
Definition future.h:209
Future(const Future &f) noexcept
Definition future.h:92
Future< detail::ResultOf< F, Future< Result > && > > then(F &&f, Schedulable &sched, std::launch asyncPolicy=kNotAsync, std::launch deferredPolicy=std::launch::deferred)
Definition future.h:239
Future & operator=(Future &&f) noexcept
Definition future.h:122
Future() noexcept
Definition future.h:76
Future(Future &&f) noexcept
Definition future.h:83
Future & operator=(const Future &f)
Definition future.h:131
bool valid() const noexcept
Definition future.h:150
Future(F &&f, Schedulable &schedulable, std::launch asyncPolicy=kNotAsync, std::launch deferredPolicy=std::launch::deferred)
Definition future.h:110
Future< detail::ResultOf< F, Args... > > async(std::launch policy, F &&f, Args &&... args)
Definition future.h:429
Future< std::decay_t< T > > make_ready_future(T &&t)
Definition future.h:576
Future< std::vector< typename std::iterator_traits< InputIt >::value_type > > when_all(InputIt first, InputIt last)
constexpr std::launch kNotAsync
Definition future.h:35
constexpr std::launch kNotDeferred
Definition future.h:41
detail::OpResult< T > OpResult
Definition pipeline.h:29