stlab 3.0.0
Modern, modular C++ algorithms, data structures, and concurrency primitives
Loading...
Searching...
No Matches
future.hpp
Go to the documentation of this file.
1/*
2 Copyright 2015 Adobe
3 Distributed under the Boost Software License, Version 1.0.
4 (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5*/
6
7/**************************************************************************************************/
8
9#ifndef STLAB_CONCURRENCY_FUTURE_HPP
10#define STLAB_CONCURRENCY_FUTURE_HPP
11
12#include <stlab/config.hpp>
13
14#include <algorithm>
15#include <array>
16#include <atomic>
17#include <cassert>
18#include <cstddef>
19#include <cstdint>
20#include <exception>
21#include <functional>
22#include <initializer_list>
23#include <iterator>
24#include <limits>
25#include <memory>
26#include <mutex>
27#include <optional>
28#include <stdexcept>
29#include <tuple>
30#include <type_traits>
31#include <utility>
32#include <variant> // for std::monostate
33#include <vector>
34
35#if STLAB_STD_COROUTINES()
36#include <coroutine>
37#endif
38
39#include <stlab/concurrency/executor_base.hpp>
40#include <stlab/concurrency/immediate_executor.hpp>
41#include <stlab/concurrency/task.hpp>
44#include <stlab/functional.hpp>
45#include <stlab/memory.hpp>
46#include <stlab/utility.hpp>
47
48/**************************************************************************************************/
49
97
98/**************************************************************************************************/
99
100namespace stlab {
101STLAB_VERSION_NAMESPACE_BEGIN()
102
103
114
115/**************************************************************************************************/
116
117
118template <class F, class... Args>
119auto invoke_void_to_monostate_result(F&& f, Args&&... args) {
120 if constexpr (std::is_void_v<std::invoke_result_t<F, Args...>>) {
121 std::forward<F>(f)(std::forward<Args>(args)...);
122 return std::monostate{};
123 } else {
124 return std::forward<F>(f)(std::forward<Args>(args)...);
125 }
126}
127
129template <class T>
131 using type = std::conditional_t<std::is_void_v<T>, std::monostate, T>;
132};
133
135template <class T>
136using void_to_monostate_t = typename void_to_monostate<T>::type;
137
139template <class T>
140inline constexpr bool is_monostate_v = std::is_same_v<T, std::monostate>;
141
143template <class T>
144auto optional_monostate_to_bool(std::optional<T>&& o) {
145 if constexpr (is_monostate_v<T>) {
146 return o.has_value();
147 } else {
148 return std::move(o);
149 }
150}
151
153template <class T>
154auto monostate_to_void(T&& a) {
155 if constexpr (is_monostate_v<std::decay_t<T>>) {
156 return;
157 } else {
158 return std::forward<T>(a);
159 }
160}
161
164template <class T>
166 if constexpr (is_monostate_v<T>) {
167 return std::tuple{};
168 } else {
169 return std::forward_as_tuple(std::forward<T>(a));
170 }
171}
172
174template <class F, class... Args>
175auto invoke_remove_monostate_arguments(F&& f, Args&&... args) {
176 return std::apply(
177 [&](auto&&... bargs) {
178 return std::forward<F>(f)(std::forward<decltype(bargs)>(bargs)...);
179 },
180 std::apply(
181 [&](auto&&... bargs) {
182 return std::tuple_cat(monostate_to_empty_tuple(std::forward<Args>(bargs))...);
183 },
184 std::forward_as_tuple(std::forward<Args>(args)...)));
185}
186
187/**************************************************************************************************/
188
190enum class future_error_codes : std::uint8_t {
192 no_state
193};
194
195/**************************************************************************************************/
196
197namespace detail {
198
199inline auto Future_error_map(future_error_codes code) noexcept -> const
200 char* { // convert to name of future error
201 switch (code) { // switch on error code value
203 return "broken promise";
204
206 return "no state";
207
208 default:
209 return nullptr;
210 }
211}
212
213/**************************************************************************************************/
214
215// This could be lifted into a common header if needed in other places
216#if STLAB_CPP_VERSION_AT_LEAST(17)
217template <class F, class... Args>
218using result_t = std::invoke_result_t<F, Args...>;
219#else
220template <class F, class... Args>
221using result_t = std::result_of_t<F(Args...)>;
222#endif
223
224/**************************************************************************************************/
225
226} // namespace detail
227
228/**************************************************************************************************/
229
231class future_error : public std::logic_error {
232public:
233 explicit future_error(future_error_codes code) : logic_error(""), _code(code) {}
234
236 [[nodiscard]] auto code() const noexcept -> const future_error_codes& { return _code; }
237
239 [[nodiscard]] auto what() const noexcept -> const char* override {
240 return detail::Future_error_map(_code);
241 }
242
243private:
244 const future_error_codes _code; // the stored error code
245};
246
247/**************************************************************************************************/
248
249namespace detail {
250
251/**************************************************************************************************/
252
253template <class>
254struct result_of_;
255
256template <class R, class... Args>
257struct result_of_<R(Args...)> {
258 using type = R;
259};
260
261template <class F>
262using result_of_t_ = typename result_of_<F>::type;
263
264template <class F, class T>
265struct result_of_when_all_t;
266
267template <class F>
268struct result_of_when_all_t<F, void> {
269 using result_type = detail::result_t<F>;
270};
271
272template <class F, class T>
273struct result_of_when_all_t {
274 using result_type = detail::result_t<F, const std::vector<T>&>;
275};
276
277template <class F, class T>
278struct result_of_when_any_t;
279
280template <class F>
281struct result_of_when_any_t<F, void> {
282 using result_type = detail::result_t<F, size_t>;
283};
284
285template <class F, class R>
286struct result_of_when_any_t {
287 using result_type = detail::result_t<F, R, size_t>;
288};
289
290template <class T>
291auto unique_usage(const std::shared_ptr<T>& p) -> bool {
292 return p.use_count() == 1;
293}
294
295/**************************************************************************************************/
296
297} // namespace detail
298
299/**************************************************************************************************/
300
301template <class...>
302class packaged_task;
303
305template <class, class = void>
306class future;
307
308/**************************************************************************************************/
309
310namespace detail {
311
312/**************************************************************************************************/
313
314template <class>
315struct packaged_task_from_signature;
316
317template <class R, class... Args>
318struct packaged_task_from_signature<R(Args...)> {
319 using type = packaged_task<Args...>;
320};
321
322template <class T>
323using packaged_task_from_signature_t = typename packaged_task_from_signature<T>::type;
324
325/**************************************************************************************************/
326
327template <class>
328struct reduced_signature;
329
330template <class R, class... Args>
331struct reduced_signature<R(Args...)> {
332 using type = R(Args...);
333};
334
335template <class R, class... Args>
336struct reduced_signature<future<R>(Args...)> {
337 using type = R(Args...);
338};
339
340template <class T>
341using reduced_signature_t = typename reduced_signature<T>::type;
342
343/**************************************************************************************************/
344
345template <class T>
346inline constexpr bool is_future_v = false;
347
348template <class T>
349inline constexpr bool is_future_v<future<T>> = true;
350
351template <class T>
352using reduced_t = std::conditional_t<is_future_v<T>, T, future<T>>;
353
354template <class Sig>
355using reduced_result_t = reduced_t<result_of_t_<Sig>>;
356
357/**************************************************************************************************/
358
359template <class T, class = void>
360struct value_;
361
362} // namespace detail
363
364/**************************************************************************************************/
365
368template <class Sig, class E, class F>
369auto package(E&&, F&&)
370 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
371
372/**************************************************************************************************/
373
374namespace detail {
375
376/**************************************************************************************************/
377
378template <class, class>
379struct shared;
380template <class, class = void>
381struct shared_base;
382
383/**************************************************************************************************/
384
385template <class... Args>
386struct shared_task {
387 void* _co_handle{nullptr}; // storing as void* for ABI stability.
388
389 virtual ~shared_task() {
390#if STLAB_STD_COROUTINES()
391 if (_co_handle) std::coroutine_handle<>::from_address(_co_handle).destroy();
392#endif
393 }
394
395 virtual void operator()(Args...) = 0;
396 virtual void set_exception(const std::exception_ptr&) noexcept = 0;
397};
398
399/**************************************************************************************************/
400
401template <class T>
402struct shared_base<T, enable_if_copyable<void_to_monostate_t<T>>>
403 : std::enable_shared_from_this<shared_base<T>> {
404 using then_t = std::vector<std::pair<executor_t, task<void() noexcept>>>;
405
406 using type = void_to_monostate_t<T>;
407
408 executor_t _executor;
409 std::optional<type> _result;
410 std::exception_ptr _exception;
411 std::mutex _mutex;
412 std::atomic_bool _ready{false};
413 then_t _then;
414
415 explicit shared_base(executor_t s) : _executor(std::move(s)) {}
416
417 template <class F>
418 auto recover(future<T>&& p, F&& f) {
419 return recover(std::move(p), _executor, std::forward<F>(f));
420 }
421
422 template <class E, class F>
423 auto recover(future<T>&& p, E executor, F&& f) {
424 using result_type = detail::result_t<F, future<T>>;
425
426 auto [pro, fut] = package<result_type()>(
427 executor, [_f = std::forward<F>(f), _p = std::move(p)]() mutable {
428 return std::move(_f)(std::move(_p));
429 });
430
431 bool ready{false};
432 {
433 std::unique_lock<std::mutex> lock(_mutex);
434 ready = _ready;
435 if (!ready) _then.emplace_back(std::move(executor), std::move(pro));
436 }
437
438 if (ready) executor(std::move(pro)); // cannot reference this after here
439
440 return std::move(fut);
441 }
442
443 template <class F>
444 void _detach(future<T>&& p, F&& f) {
445 auto pro = [_f = std::forward<F>(f), _p = std::move(p)]() mutable noexcept {
446 std::move(_f)(std::move(_p));
447 };
448
449 bool ready{false};
450 {
451 std::unique_lock<std::mutex> lock(_mutex);
452 ready = _ready;
453 if (!ready) _then.emplace_back(immediate_executor, std::move(pro));
454 }
455
456 if (ready) std::move(pro)(); // cannot reference this after here
457 }
458
459 void _detach(future<T>&& p) noexcept {
460 std::unique_lock<std::mutex> lock(_mutex);
461 if (!_ready) _then.emplace_back([](auto&&) {}, [_p = std::move(p)]() noexcept {});
462 }
463
464 template <class F>
465 void _on_completion(F&& f) {
466 task<void() noexcept> t(std::forward<F>(f));
467 {
468 std::unique_lock<std::mutex> lock(_mutex);
469 if (!_ready) {
470 _then.emplace_back(immediate_executor, std::move(t));
471 return;
472 }
473 }
474 std::move(t)();
475 }
476
477 template <class E, class F>
478 void _on_completion(E executor, F&& f) {
479 task<void() noexcept> t(std::forward<F>(f));
480 {
481 std::unique_lock<std::mutex> lock(_mutex);
482 if (!_ready) {
483 _then.emplace_back(std::move(executor), std::move(t));
484 return;
485 }
486 }
487 executor(std::move(t));
488 }
489
490 void _set_exception(const std::exception_ptr& error) noexcept {
491 _exception = error;
492 then_t then;
493 {
494 std::unique_lock<std::mutex> lock(_mutex);
495 then = std::move(_then);
496 _ready = true;
497 }
498 // propagate exception with scheduling
499 for (auto& e : then) {
500 e.first(std::move(e.second));
501 }
502 }
503
504 template <class A>
505 void set_value(A&& a);
506
507 auto is_ready() const& -> bool { return _ready; }
508
509 // get_ready() is called internally on continuations when we know _ready is true;
510 auto get_ready() -> const type& {
511 assert(is_ready() && "FATAL (sean.parent) : get_ready() called but not ready!");
512
513 if (_exception) std::rethrow_exception(_exception);
514 return *_result;
515 }
516
517 auto get_ready_r(bool unique) -> type {
518 if (!unique) return get_ready();
519
520 assert(is_ready() && "FATAL (sean.parent) : get_ready() called but not ready!");
521
522 if (_exception) std::rethrow_exception(_exception);
523 return std::move(*_result);
524 }
525
526 auto get_try() -> std::optional<type> {
527 bool ready = false;
528 {
529 std::unique_lock<std::mutex> lock(_mutex);
530 ready = _ready;
531 }
532 if (ready) {
533 if (_exception) std::rethrow_exception(_exception);
534 return _result;
535 }
536 return std::nullopt;
537 }
538
539 auto get_try_r(bool unique) -> std::optional<type> {
540 if (!unique) return get_try();
541
542 bool ready = false;
543 {
544 std::unique_lock<std::mutex> lock(_mutex);
545 ready = _ready;
546 }
547 if (ready) {
548 if (_exception) std::rethrow_exception(_exception);
549 return std::move(_result);
550 }
551 return std::nullopt;
552 }
553};
554
555/**************************************************************************************************/
556
557template <class T>
558struct shared_base<T, enable_if_not_copyable<void_to_monostate_t<T>>>
559 : std::enable_shared_from_this<shared_base<T>> {
560 using then_t = std::pair<executor_t, task<void() noexcept>>;
561
562 executor_t _executor;
563 std::optional<T> _result;
564 std::exception_ptr _exception;
565 std::mutex _mutex;
566 std::atomic_bool _ready{false};
567 then_t _then;
568
569 explicit shared_base(executor_t s) : _executor(std::move(s)) {}
570
571 template <class F>
572 auto recover(future<T>&& p, F&& f) {
573 return recover(std::move(p), _executor, std::forward<F>(f));
574 }
575
576 /*
577 NOTE : executor cannot be a reference type here. When invoked it could
578 cause _this_ to be deleted, and the executor passed in may be
579 this->_executor
580 */
581 template <class E, class F>
582 auto recover(future<T>&& p, E executor, F&& f) {
583 using result_type = detail::result_t<F, future<T>>;
584
585 auto [pro, fut] = package<result_type()>(
586 executor, [_f = std::forward<F>(f), _p = std::move(p)]() mutable {
587 return std::move(_f)(std::move(_p));
588 });
589
590 bool ready{false};
591 {
592 std::unique_lock<std::mutex> lock(_mutex);
593 ready = _ready;
594 if (!ready) {
595 assert(!_then.second && "recover: _then slot already occupied");
596 _then = {std::move(executor), std::move(pro)};
597 }
598 }
599 if (ready) executor(std::move(pro)); // cannot reference this after here
600
601 return std::move(fut);
602 }
603
604 template <class F>
605 void _detach(future<T>&& p, F&& f) {
606 auto pro = [_f = std::forward<F>(f), _p = std::move(p)]() mutable noexcept {
607 std::move(_f)(std::move(_p));
608 };
609
610 bool ready{false};
611 {
612 std::unique_lock<std::mutex> lock(_mutex);
613 ready = _ready;
614 if (!ready) _then = {immediate_executor, std::move(pro)};
615 }
616
617 if (ready) std::move(pro)(); // cannot reference this after here
618 }
619
620 void _detach(future<T>&& p) noexcept {
621 std::unique_lock<std::mutex> lock(_mutex);
622 if (!_ready) _then = then_t([](auto&&) {}, [_p = std::move(p)]() noexcept {});
623 }
624
625 template <class F>
626 void _on_completion(F&& f) {
627 task<void() noexcept> t(std::forward<F>(f));
628 {
629 std::unique_lock<std::mutex> lock(_mutex);
630 if (!_ready) {
631 assert(!_then.second && "on_completion: _then slot already occupied");
632 _then = {immediate_executor, std::move(t)};
633 return;
634 }
635 }
636 std::move(t)();
637 }
638
639 template <class E, class F>
640 void _on_completion(E executor, F&& f) {
641 task<void() noexcept> t(std::forward<F>(f));
642 {
643 std::unique_lock<std::mutex> lock(_mutex);
644 if (!_ready) {
645 assert(!_then.second && "on_completion: _then slot already occupied");
646 _then = {std::move(executor), std::move(t)};
647 return;
648 }
649 }
650 executor(std::move(t));
651 }
652
653 void _set_exception(const std::exception_ptr& error) noexcept {
654 _exception = error;
655 then_t then;
656 {
657 std::unique_lock<std::mutex> lock(_mutex);
658 if (_then.second) then = std::move(_then);
659 _ready = true;
660 }
661 // propagate exception with scheduling
662 if (then.second) then.first(std::move(then.second));
663 }
664
665 template <class A>
666 void set_value(A&& a);
667
668 auto is_ready() const -> bool { return _ready; }
669
672 auto get_ready() -> T {
673 assert(is_ready() && "FATAL (sean.parent) : get_ready() called but not ready!");
674
675 if (_exception) std::rethrow_exception(_exception);
676 return std::move(*_result);
677 }
678
680 auto get_try() -> std::optional<T> {
681 bool ready = false;
682 {
683 std::unique_lock<std::mutex> lock(_mutex);
684 ready = _ready;
685 }
686 if (ready) {
687 if (_exception) std::rethrow_exception(_exception);
688 return std::move(_result);
689 }
690 return std::nullopt;
691 }
692};
693
694/**************************************************************************************************/
695
698template <class R>
699class promise {
700 using type = void_to_monostate_t<R>;
701 std::weak_ptr<shared_base<R>> _p;
702
703public:
704 explicit promise(const std::shared_ptr<shared_base<R>>& p) noexcept : _p{p} {}
705
706 ~promise() {
707 if (auto p = _p.lock()) {
708 p->_set_exception(
709 std::make_exception_ptr(future_error(future_error_codes::broken_promise)));
710 }
711 }
712
713 promise(promise&&) noexcept = default;
714 auto operator=(promise&& x) noexcept -> promise& {
715 if (this == &x) return *this;
716 if (auto p = _p.lock()) {
717 p->_set_exception(
718 std::make_exception_ptr(future_error(future_error_codes::broken_promise)));
719 }
720 _p = std::move(x._p);
721 return *this;
722 }
723
724 promise(const promise&) = delete;
725 auto operator=(const promise&) -> promise& = delete;
726
729 void set_value(type&& value) && noexcept {
730 if (auto p = _p.lock()) {
731 _p.reset();
732 p->set_value(std::move(value));
733 }
734 }
735
737 auto set_value() && noexcept { set_value(std::monostate{}); }
738
740 void set_exception(const std::exception_ptr& error) && noexcept {
741 if (auto p = _p.lock()) {
742 _p.reset();
743 p->_set_exception(error);
744 }
745 }
746
748 [[nodiscard]] auto canceled() const -> bool { return _p.expired(); }
749};
750
752template <class R>
753promise(std::shared_ptr<shared_base<R>>) -> promise<R>;
754
755template <class F, class R, class... Args>
756struct shared<F, R(Args...)> final : shared_base<R>, shared_task<Args...> {
757 std::optional<F> _f;
758
759 shared(executor_t s, F&& f) : shared_base<R>(std::move(s)), _f(std::move(f)) {}
760
761 void operator()(Args... args) noexcept override {
762 static_assert(
763 std::is_nothrow_invocable_v<F, decltype(promise{this->shared_from_this()}), Args...>);
764 assert(!this->weak_from_this().expired());
765 std::move (*_f)(promise{this->shared_from_this()}, std::move(args)...);
766 // After invoking `_f`, it is not destructed because it could be satisfying the promise
767 // asynchronously. `_f` is responsible for any cleanup prior to the future being released
768 // and must be aware that it may be destructed at any time after this point.
769 }
770
771 void set_exception(const std::exception_ptr& error) noexcept override {
772 shared_base<R>::_set_exception(error);
773 _f.reset(); // _f will not be invoked, release it and any resources it holds
774 }
775};
776
777template <class Sig, class F>
778auto make_shared_state(executor_t s, F&& f) -> std::shared_ptr<shared<F, Sig>> {
779 return std::make_shared<shared<F, Sig>>(std::move(s), std::forward<F>(f));
780}
781
782template <class... Args>
783[[nodiscard]] auto weak_state(const packaged_task<Args...>& p)
784 -> std::weak_ptr<shared_task<Args...>>;
785
786/**************************************************************************************************/
787
788} // namespace detail
789
790/**************************************************************************************************/
791
794template <class... Args>
795class packaged_task {
796 using ptr_t = std::weak_ptr<detail::shared_task<Args...>>;
797 ptr_t _p;
798
799 explicit packaged_task(ptr_t&& p) : _p(std::move(p)) {}
800
801 template <class Sig, class E, class F>
802 friend auto package(E&&, F&&)
803 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
804
805 template <class T, class E>
806 friend auto future_with_broken_promise(E&&) -> detail::reduced_t<T>;
807
808 template <class... Brgs>
809 friend auto detail::weak_state(const packaged_task<Brgs...>& p)
810 -> std::weak_ptr<detail::shared_task<Brgs...>>;
811
812public:
813 packaged_task() = default;
814 ~packaged_task() {
815 if (auto p = _p.lock()) {
816 p->set_exception(
817 std::make_exception_ptr(future_error(future_error_codes::broken_promise)));
818 }
819 }
820
821 packaged_task(const packaged_task&) = delete;
822 auto operator=(const packaged_task&) -> packaged_task& = delete;
823
824 packaged_task(packaged_task&&) noexcept = default;
825 auto operator=(packaged_task&& x) noexcept -> packaged_task& {
826 if (this == &x) return *this;
827 if (auto p = _p.lock()) {
828 p->set_exception(
829 std::make_exception_ptr(future_error(future_error_codes::broken_promise)));
830 }
831 _p = std::move(x._p);
832 return *this;
833 }
834
838 template <class... A>
839 void operator()(A&&... args) noexcept {
840 if (auto p = _p.lock()) {
841 p->_co_handle = nullptr;
842 _p.reset();
843 (*p)(std::forward<A>(args)...);
844 }
845 }
846
848 [[nodiscard]] auto canceled() const -> bool { return _p.expired(); }
849
851 void set_exception(const std::exception_ptr& error) noexcept {
852 if (auto p = _p.lock()) {
853 p->_co_handle = nullptr;
854 _p.reset();
855 p->set_exception(error);
856 }
857 }
858};
859
860namespace detail {
861
862template <class... Args>
863[[nodiscard]] auto weak_state(const packaged_task<Args...>& p)
864 -> std::weak_ptr<detail::shared_task<Args...>> {
865 return p._p;
866}
867
868} // namespace detail
869
870/**************************************************************************************************/
871
879template <class T>
880class STLAB_NODISCARD() future<T, enable_if_copyable<void_to_monostate_t<T>>> {
881 using type = void_to_monostate_t<T>;
882 using ptr_t = std::shared_ptr<detail::shared_base<T>>;
883 ptr_t _p;
884
885 explicit future(ptr_t p) : _p(std::move(p)) {}
886
887 template <class Sig, class E, class F>
888 friend auto package(E&&, F&&)
889 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
890
891 template <class U, class E>
892 friend auto future_with_broken_promise(E&&) -> detail::reduced_t<U>;
893
895 friend struct detail::shared_base<type>;
896 template <class, class>
897 friend struct detail::value_;
899
900public:
902 using result_type = T;
903
904 future() = default;
905 future(const future&) = default;
906 future(future&&) noexcept = default;
907 auto operator=(const future&) -> future& = default;
908 auto operator=(future&&) noexcept -> future& = default;
909
911 void swap(future& x) noexcept { std::swap(_p, x._p); }
912
914 inline friend void swap(future& x, future& y) noexcept { x.swap(y); }
915
917
918 inline friend auto operator==(const future& x, const future& y) -> bool { return x._p == y._p; }
919 inline friend auto operator!=(const future& x, const future& y) -> bool { return !(x == y); }
921
923 [[nodiscard]] auto valid() const -> bool { return static_cast<bool>(_p); }
924
927 template <class F>
928 auto then(F&& f) const& {
929 return recover([_f = std::forward<F>(f)](future<result_type>&& p) mutable {
931 std::move(_f),
932 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
933 });
934 }
935
941
943 template <class F>
944 auto operator|(F&& f) const& {
945 return then(std::forward<F>(f));
946 }
947
950 template <class E, class F>
951 auto then(E&& executor, F&& f) const& {
952 return recover(
953 std::forward<E>(executor), [_f = std::forward<F>(f)](future<result_type>&& p) mutable {
955 std::move(_f),
956 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
957 });
958 }
959
961 template <class F>
962 auto operator|(executor_task_pair<F> etp) const& {
963 return then(std::move(etp)._executor, std::move(etp)._f);
964 }
965
968 template <class F>
969 auto then(F&& f) && {
970 return std::move(*this).recover([_f = std::forward<F>(f)](future<result_type>&& p) mutable {
971 return invoke_remove_monostate_arguments(
972 std::move(_f),
973 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
974 });
975 }
976
978 template <class F>
979 auto operator|(F&& f) && {
980 return std::move(*this).then(std::forward<F>(f));
981 }
982
985 template <class E, class F>
986 auto then(E&& executor, F&& f) && {
987 return std::move(*this).recover(
988 std::forward<E>(executor), [_f = std::forward<F>(f)](future<result_type>&& p) mutable {
989 return invoke_remove_monostate_arguments(
990 std::move(_f),
991 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
992 });
993 }
994
997 template <class F>
998 auto operator|(executor_task_pair<F> etp) && {
999 return std::move(*this).then(std::move(etp)._executor, std::move(etp)._f);
1000 }
1001
1004 template <class F>
1005 auto recover(F&& f) const& {
1006 return _p->recover(copy(*this), std::forward<F>(f));
1007 }
1008
1010 template <class F>
1011 auto operator^(F&& f) const& {
1012 return recover(std::forward<F>(f));
1013 }
1014
1016 template <class E, class F>
1017 auto recover(E&& executor, F&& f) const& {
1018 return _p->recover(copy(*this), std::forward<E>(executor), std::forward<F>(f));
1019 }
1020
1022 template <class F>
1023 auto operator^(executor_task_pair<F> etp) const& {
1024 return recover(std::move(etp)._executor, std::move(etp)._f);
1025 }
1026
1029 template <class F>
1030 auto recover(F&& f) && {
1031 auto& self = *_p.get();
1032 return self.recover(std::move(*this), std::forward<F>(f));
1033 }
1034
1036 template <class F>
1037 auto operator^(F&& f) && {
1038 return std::move(*this).recover(std::forward<F>(f));
1039 }
1040
1043 template <class E, class F>
1044 auto recover(E&& executor, F&& f) && {
1045 auto& self = *_p.get();
1046 return self.recover(std::move(*this), std::forward<E>(executor), std::forward<F>(f));
1047 }
1048
1051 template <class F>
1052 auto operator^(executor_task_pair<F> etp) && {
1053 return std::move(*this).recover(std::move(etp)._executor, std::move(etp)._f);
1054 }
1055
1057 void detach() && {
1058 auto& self = *_p.get();
1059 self._detach(std::move(*this));
1060 }
1061
1064 template <class F>
1065 void detach(F&& f) && {
1066 auto& self = *_p.get();
1067 self._detach(std::move(*this), std::forward<F>(f));
1068 }
1069
1072 template <class F>
1073 void on_completion(F&& f) {
1074 _p->_on_completion(std::forward<F>(f));
1075 }
1076
1079 template <class E, class F>
1080 void on_completion(E&& executor, F&& f) {
1081 _p->_on_completion(std::forward<E>(executor), std::forward<F>(f));
1082 }
1083
1085 void reset() noexcept { _p.reset(); }
1086
1088 [[nodiscard]] auto is_ready() const& -> bool { return _p && _p->is_ready(); }
1089
1092 [[nodiscard]] auto get_try() const& { return optional_monostate_to_bool(_p->get_try()); }
1093
1095 auto get_try() && { return optional_monostate_to_bool(_p->get_try_r(unique_usage(_p))); }
1096
1098 [[nodiscard]] auto get_ready() const& { return monostate_to_void(_p->get_ready()); }
1099
1101 auto get_ready() && { return monostate_to_void(_p->get_ready_r(unique_usage(_p))); }
1102
1105 [[nodiscard]] auto exception() const& -> std::exception_ptr {
1106 assert(is_ready());
1107 return _p->_exception;
1108 }
1109};
1110
1111/**************************************************************************************************/
1112
1119template <class T>
1120class STLAB_NODISCARD() future<T, enable_if_not_copyable<void_to_monostate_t<T>>> {
1121 using ptr_t = std::shared_ptr<detail::shared_base<T>>;
1122 ptr_t _p;
1123
1124 explicit future(ptr_t p) : _p(std::move(p)) {}
1125
1126 template <class Sig, class E, class F>
1127 friend auto package(E&&, F&&)
1128 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
1129
1130 template <class U, class E>
1131 friend auto future_with_broken_promise(E&&) -> detail::reduced_t<U>;
1132
1134 friend struct detail::shared_base<T>;
1135 template <class, class>
1136 friend struct detail::value_;
1138
1139public:
1141 using result_type = T;
1142
1143 future() = default;
1144 future(const future&) = delete;
1145 future(future&&) noexcept = default;
1146 auto operator=(const future&) -> future& = delete;
1147 auto operator=(future&&) noexcept -> future& = default;
1148
1150 void swap(future& x) noexcept { std::swap(_p, x._p); }
1151
1153 inline friend void swap(future& x, future& y) noexcept { x.swap(y); }
1154
1156
1157 inline friend auto operator==(const future& x, const future& y) -> bool { return x._p == y._p; }
1158 inline friend auto operator!=(const future& x, const future& y) -> bool { return !(x == y); }
1160
1162 [[nodiscard]] auto valid() const -> bool { return static_cast<bool>(_p); }
1163
1166 template <class F>
1167 auto then(F&& f) && {
1168 return std::move(*this).recover([_f = std::forward<F>(f)](future<result_type>&& p) mutable {
1169 return std::move(_f)(std::move(p).get_ready());
1170 });
1171 }
1172
1176
1178 template <class F>
1179 auto operator|(F&& f) && {
1180 return std::move(*this).then(std::forward<F>(f));
1181 }
1182
1185 template <class E, class F>
1186 auto then(E&& executor, F&& f) && {
1187 return std::move(*this).recover(std::forward<E>(executor),
1188 [_f = std::forward<F>(f)](future<result_type>&& p) mutable {
1189 return std::move(_f)(std::move(p).get_ready());
1190 });
1191 }
1192
1194 template <class F>
1195 auto operator|(executor_task_pair<F> etp) && {
1196 return std::move(*this).then(std::move(etp)._executor, std::move(etp)._f);
1197 }
1198
1201 template <class F>
1202 auto recover(F&& f) && {
1203 auto& self = *_p.get();
1204 return self.recover(std::move(*this), std::forward<F>(f));
1205 }
1206
1208 template <class F>
1209 auto operator^(F&& f) && {
1210 return std::move(*this).recover(std::forward<F>(f));
1211 }
1212
1214 template <class E, class F>
1215 auto recover(E&& executor, F&& f) && {
1216 auto& self = *_p.get();
1217 return self.recover(std::move(*this), std::forward<E>(executor), std::forward<F>(f));
1218 }
1219
1221 template <class F>
1222 auto operator^(executor_task_pair<F> etp) && {
1223 return std::move(*this).recover(std::move(etp)._executor, std::move(etp)._f);
1224 }
1225
1227 void detach() && {
1228 auto& self = *_p.get();
1229 self._detach(std::move(*this));
1230 }
1231
1234 template <class F>
1235 void detach(F&& f) && {
1236 auto& self = *_p.get();
1237 self._detach(std::move(*this), std::forward<F>(f));
1238 }
1239
1243 template <class F>
1244 void on_completion(F&& f) {
1245 _p->_on_completion(std::forward<F>(f));
1246 }
1247
1251 template <class E, class F>
1252 void on_completion(E&& executor, F&& f) {
1253 _p->_on_completion(std::forward<E>(executor), std::forward<F>(f));
1254 }
1255
1257 void reset() noexcept { _p.reset(); }
1258
1260 [[nodiscard]] auto is_ready() const& -> bool { return _p && _p->is_ready(); }
1261
1265 [[nodiscard]] auto get_try() & -> std::optional<T> { return _p->get_try(); }
1266
1269 [[nodiscard]] auto get_ready() && -> T { return _p->get_ready(); }
1270
1273 [[nodiscard]] auto exception() const& -> std::exception_ptr {
1274 assert(is_ready());
1275 return _p->_exception;
1276 }
1277};
1278
1281template <class Sig, class E, class F>
1282auto package(E&& executor, F&& f)
1283 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>> {
1284 if constexpr (std::is_same_v<std::decay_t<E>, executor_t>) {
1285 assert(executor && "FATAL (sean.parent) : executor is null!");
1286 }
1287
1288 using result_t = detail::result_of_t_<Sig>;
1289
1290 if constexpr (detail::is_future_v<result_t>) {
1291 auto p = detail::make_shared_state<detail::reduced_signature_t<Sig>>(
1292 std::forward<E>(executor),
1293 [_f = std::make_optional(std::forward<F>(f)),
1294 _hold = future<void>{}](auto&& promise, auto&&... args) mutable noexcept {
1295 assert(_f && "packaged task invoked twice");
1296 try {
1297 auto r = std::move(*_f)(std::forward<decltype(args)>(args)...);
1298 try {
1299 _hold = std::move(r).recover(
1300 immediate_executor,
1301 [_p = std::move(promise)](result_t&& f) mutable noexcept {
1302 if (auto e = f.exception()) {
1303 std::move(_p).set_exception(std::move(e));
1304 } else {
1305 std::move(_p).set_value(invoke_void_to_monostate_result(
1306 [&] { return std::move(f).get_ready(); }));
1307 }
1308 });
1309 } catch (...) {
1310 /* NOTE: an exception here is reported as a broken promise. Ideally recover
1311 * would be passed the initial promise (it flows through the chain), but the
1312 * API isn't there yet. */
1313 }
1314 } catch (...) {
1315 std::move(promise).set_exception(std::current_exception());
1316 }
1317 _f.reset();
1318 });
1319 return {detail::packaged_task_from_signature_t<Sig>{p}, result_t{p}};
1320 } else {
1321 auto p = detail::make_shared_state<Sig>(
1322 std::forward<E>(executor), [_f = std::make_optional(std::forward<F>(f))](
1323 auto&& promise, auto&&... args) mutable noexcept {
1324 assert(_f && "packaged task invoked twice");
1325 try {
1327 std::move(*_f), std::forward<decltype(args)>(args)...);
1328 std::move(promise).set_value(std::move(tmp)); // noexcept
1329 } catch (...) {
1330 std::move(promise).set_exception(std::current_exception());
1331 }
1332 _f.reset();
1333 });
1334 return {detail::packaged_task_from_signature_t<Sig>{p}, future<result_t>{p}};
1335 }
1336}
1337
1340template <class T, class E>
1341auto future_with_broken_promise(E&& executor) -> detail::reduced_t<T> {
1342 auto p = std::make_shared<detail::shared_base<typename detail::reduced_t<T>::result_type>>(
1343 std::forward<E>(executor));
1344 p->_exception = std::make_exception_ptr(future_error(future_error_codes::broken_promise));
1345 p->_ready = true;
1346
1347 return detail::reduced_t<T>{p};
1348}
1349
1350/**************************************************************************************************/
1351
1352namespace detail {
1353
1354template <class F>
1355struct assign_ready_future {
1356 template <class T>
1357 static void assign(T& x, F&& f) {
1358 x = std::move(f).get_ready();
1359 }
1360};
1361
1362template <>
1363struct assign_ready_future<future<void>> {
1364 template <class T>
1365 static void assign(T& x, const future<void>&) {
1366 x = std::move(typename T::value_type()); // to set the optional
1367 }
1368};
1369
1372template <class... Ts, class F, std::size_t... I>
1373void for_each_when_input_(std::tuple<Ts...>& inputs, F&& f, std::index_sequence<I...>) {
1374 (f(std::get<I>(inputs), std::integral_constant<std::size_t, I>{}), ...);
1375}
1376
1379template <class... Ts, class F>
1380void for_each_when_input(std::tuple<Ts...>& inputs, F&& f) {
1381 for_each_when_input_(inputs, std::forward<F>(f), std::index_sequence_for<Ts...>{});
1382}
1383
1386template <class T, class F>
1387void for_each_when_input(std::vector<future<T>>& inputs, F&& f) {
1388 std::size_t index = 0;
1389 // Visit each owned future without copying its shared state.
1390 for (auto& input : inputs)
1391 f(input, index++);
1392}
1393
1397template <class Inputs, bool Any>
1398struct when_shared {
1399 Inputs _inputs;
1400 executor_t _executor;
1401 packaged_task<> _f;
1402 std::mutex _guard;
1403 std::size_t _remaining;
1404 std::size_t _index{0};
1405 std::exception_ptr _exception;
1406 bool _registering{true};
1407 bool _decided{false};
1408
1411 when_shared(Inputs inputs, executor_t executor, std::size_t count) :
1412 _inputs(std::move(inputs)), _executor(std::move(executor)), _remaining(count) {}
1413
1416 void dispatch(packaged_task<> ready) {
1417 if (ready.canceled()) return;
1418 for_each_when_input(_inputs, [&](auto& input, std::size_t index) {
1419 if (_exception || (Any && index != _index)) input.reset();
1420 });
1421 _executor(std::move(ready));
1422 }
1423
1427 template <class T>
1428 void done(const future<T>& input, std::size_t index) {
1429 packaged_task<> ready;
1430 {
1431 std::unique_lock<std::mutex> lock{_guard};
1432 if (_decided) return;
1433 auto error = input.exception();
1434 if constexpr (Any) {
1435 if (!error || --_remaining == 0) {
1436 _index = index;
1437 _exception = std::move(error);
1438 _decided = true;
1439 }
1440 } else {
1441 if (error || --_remaining == 0) {
1442 _exception = std::move(error);
1443 _decided = true;
1444 }
1445 }
1446 if (_decided && !_registering) ready = std::move(_f);
1447 }
1448 dispatch(std::move(ready));
1449 }
1450
1453 void finish_registration() {
1454 packaged_task<> ready;
1455 {
1456 std::unique_lock<std::mutex> lock{_guard};
1457 _registering = false;
1458 if (_decided) ready = std::move(_f);
1459 }
1460 dispatch(std::move(ready));
1461 }
1462};
1463
1466template <class P>
1467void attach_when_inputs(const std::shared_ptr<P>& shared) {
1468 for_each_when_input(shared->_inputs, [&](auto& input, std::size_t index) {
1469 input.on_completion([weak = std::weak_ptr<P>(shared), source = &input, index]() noexcept {
1470 if (auto p = weak.lock()) p->done(*source, index);
1471 });
1472 });
1473 shared->finish_registration();
1474}
1475
1479template <class F, class... Ts>
1480auto apply_when_all_args(F& f, std::tuple<future<Ts>...>& inputs) {
1481 using args_t = optional_placeholder_tuple<Ts...>;
1482 args_t args;
1483 for_each_when_input(inputs, [&](auto& input, auto index) {
1484 assign_ready_future<std::decay_t<decltype(input)>>::assign(
1485 std::get<decltype(index)::value>(args), std::move(input));
1486 });
1487 return apply_optional_indexed<index_sequence_transform_t<
1488 std::index_sequence_for<Ts...>, remove_placeholder<args_t>::template function>>(f, args);
1489}
1490
1494template <class T, class F, class P>
1495auto apply_when_any_arg(F& f, P& shared) {
1496 if constexpr (std::is_void_v<T>) {
1497 return f(shared->_index);
1498 } else {
1499 std::optional<T> arg;
1500 for_each_when_input(shared->_inputs, [&](auto& input, std::size_t index) {
1501 if (index == shared->_index) arg.emplace(std::move(input).get_ready());
1502 });
1503 return f(std::move(*arg), shared->_index);
1504 }
1505}
1506
1507} // namespace detail
1508
1509/**************************************************************************************************/
1510
1523template <class E, class F, class... Ts>
1524auto when_all(const E& executor, F f, future<Ts>... args) {
1525 using vt_t = voidless_tuple<Ts...>;
1526 using result_t = decltype(apply_ignore_placeholders(std::declval<F>(), std::declval<vt_t>()));
1527
1528 using inputs_t = std::tuple<future<Ts>...>;
1529 auto shared = std::make_shared<detail::when_shared<inputs_t, false>>(
1530 inputs_t{std::move(args)...}, executor, sizeof...(Ts));
1531 auto p = package<result_t()>(executor, [_f = std::move(f), _p = shared]() mutable {
1532 if (_p->_exception) std::rethrow_exception(_p->_exception);
1533 return detail::apply_when_all_args(_f, _p->_inputs);
1534 });
1535 shared->_f = std::move(p.first);
1536
1537 detail::attach_when_inputs(shared);
1538
1539 return std::move(p.second);
1540}
1541
1542/**************************************************************************************************/
1543
1545template <class T>
1549 template <class E, class F, class... Ts>
1550 static auto make(const E& executor, F f, future<T> arg, future<Ts>... args) {
1551 using result_t = detail::result_t<F, T, size_t>;
1552
1553 using inputs_t = std::tuple<future<T>, future<Ts>...>;
1554 auto shared = std::make_shared<detail::when_shared<inputs_t, true>>(
1555 inputs_t{std::move(arg), std::move(args)...}, executor, sizeof...(Ts) + 1);
1556 auto p = package<result_t()>(executor, [_f = std::move(f), _p = shared]() mutable {
1557 if (_p->_exception) std::rethrow_exception(_p->_exception);
1558 return detail::apply_when_any_arg<T>(_f, _p);
1559 });
1560 shared->_f = std::move(p.first);
1561
1562 detail::attach_when_inputs(shared);
1563
1564 return std::move(p.second);
1565 }
1566};
1567
1568/**************************************************************************************************/
1569
1571template <>
1572struct make_when_any<void> {
1575 template <class E, class F, class... Ts>
1576 static auto make(E&& executor, F&& f, future<Ts>... args) {
1577 using result_t = detail::result_t<F, size_t>;
1578
1579 using inputs_t = std::tuple<future<Ts>...>;
1580 auto shared = std::make_shared<detail::when_shared<inputs_t, true>>(
1581 inputs_t{std::move(args)...}, executor, sizeof...(Ts));
1582 auto p = package<result_t()>(executor, [_f = std::forward<F>(f), _p = shared]() mutable {
1583 if (_p->_exception) std::rethrow_exception(_p->_exception);
1584 return detail::apply_when_any_arg<void>(_f, _p);
1585 });
1586 shared->_f = std::move(p.first);
1587
1588 detail::attach_when_inputs(shared);
1589
1590 return std::move(p.second);
1591 }
1592};
1593
1594/**************************************************************************************************/
1595
1612template <class E, class F, class T, class... Ts>
1613auto when_any(E&& executor, F&& f, future<T>&& arg, future<Ts>&&... args) {
1614 return make_when_any<T>::make(std::forward<E>(executor), std::forward<F>(f), std::move(arg),
1615 std::move(args)...);
1616}
1617
1618/**************************************************************************************************/
1619
1620namespace detail {
1624template <bool Any, class R, class E, class F, class I>
1625auto create_range_of_futures(const E& executor, F&& f, I first, I last) {
1626 using future_t = typename std::iterator_traits<I>::value_type;
1627 using value_t = typename future_t::result_type;
1628 std::vector<future_t> inputs;
1629 inputs.reserve(std::distance(first, last));
1630 std::transform(first, last, std::back_inserter(inputs), [](auto&& input) -> future_t {
1631 if constexpr (std::is_copy_constructible_v<future_t>)
1632 return std::forward<decltype(input)>(input);
1633 else
1634 return std::move(input);
1635 });
1636 auto count = inputs.size();
1637 auto shared =
1638 std::make_shared<when_shared<decltype(inputs), Any>>(std::move(inputs), executor, count);
1639 auto p = package<R()>(executor, [_f = std::forward<F>(f), _p = shared]() mutable -> R {
1640 if (_p->_exception) std::rethrow_exception(_p->_exception);
1641 if constexpr (Any) {
1642 if constexpr (std::is_void_v<value_t>)
1643 return _f(_p->_index);
1644 else
1645 return _f(std::move(_p->_inputs[_p->_index]).get_ready(), _p->_index);
1646 } else {
1647 if constexpr (std::is_void_v<value_t>) {
1648 return _f();
1649 } else {
1650 std::vector<value_t> values;
1651 values.reserve(_p->_inputs.size());
1652 std::transform(_p->_inputs.begin(), _p->_inputs.end(), std::back_inserter(values),
1653 [](auto& input) { return std::move(input).get_ready(); });
1654 return _f(std::move(values));
1655 }
1656 }
1657 });
1658 shared->_f = std::move(p.first);
1659 attach_when_inputs(shared);
1660 return std::move(p.second);
1661}
1662
1663/**************************************************************************************************/
1664
1665} // namespace detail
1666
1667/**************************************************************************************************/
1668
1683template <class E, class F, class I>
1684auto when_all(const E& executor, F f, std::pair<I, I> range) {
1685 using param_t = typename std::iterator_traits<I>::value_type::result_type;
1686 using result_t = typename detail::result_of_when_all_t<F, param_t>::result_type;
1687 if (range.first == range.second) {
1688 auto p = package<result_t()>(executor, [_f = std::move(f)]() mutable -> result_t {
1689 if constexpr (std::is_void_v<param_t>)
1690 return _f();
1691 else
1692 return _f(std::vector<param_t>{});
1693 });
1694 executor(std::move(p.first));
1695 return std::move(p.second);
1696 }
1697
1698 return detail::create_range_of_futures<false, result_t>(executor, std::move(f), range.first,
1699 range.second);
1700}
1701
1702/**************************************************************************************************/
1703
1719template <class E, class F, class I>
1720auto when_any(const E& executor, F&& f, std::pair<I, I> range) {
1721 using param_t = typename std::iterator_traits<I>::value_type::result_type;
1722 using result_t = std::decay_t<typename detail::result_of_when_any_t<F, param_t>::result_type>;
1723 if (range.first == range.second) {
1725 }
1726
1727 return detail::create_range_of_futures<true, result_t>(std::move(executor), std::forward<F>(f),
1728 range.first, range.second);
1729}
1730
1731/**************************************************************************************************/
1732
1738template <class E, class F, class... Args>
1739auto async(const E& executor, F&& f, Args&&... args)
1740 -> detail::reduced_t<detail::result_t<std::decay_t<F>, std::decay_t<Args>...>> {
1741 using result_type = detail::result_t<std::decay_t<F>, std::decay_t<Args>...>;
1742
1743 auto [pro, fut] = package<result_type()>(
1744 executor,
1745 [f = std::forward<F>(f), args = std::make_tuple(std::forward<Args>(args)...)]() mutable
1746 -> result_type { return std::apply(std::move(f), std::move(args)); });
1747
1748 executor(std::move(pro));
1749
1750 return std::move(fut);
1751}
1752
1753/**************************************************************************************************/
1754
1755namespace detail {
1756
1757/**************************************************************************************************/
1758
1759template <class T>
1760struct value_<T, enable_if_copyable<void_to_monostate_t<T>>> {
1761 template <class C>
1762 static void proceed(C& sb) {
1763 typename C::then_t then;
1764 {
1765 std::unique_lock<std::mutex> lock(sb._mutex);
1766 sb._ready = true;
1767 then = std::move(sb._then);
1768 }
1769 for (auto& e : then)
1770 e.first(std::move(e.second));
1771 }
1772
1773 template <class R, class A>
1774 static void set(shared_base<R>& sb, A&& a) {
1775 sb._result = std::forward<A>(a);
1776 proceed(sb);
1777 }
1778};
1779
1780template <class T>
1781struct value_<T, enable_if_not_copyable<void_to_monostate_t<T>>> {
1782 template <class C>
1783 static void proceed(C& sb) {
1784 typename C::then_t then;
1785 {
1786 std::unique_lock<std::mutex> lock(sb._mutex);
1787 sb._ready = true;
1788 then = std::move(sb._then);
1789 }
1790 if (then.first) then.first(std::move(then.second));
1791 }
1792
1793 template <class R, class A>
1794 static void set(shared_base<R>& sb, A&& a) {
1795 sb._result = std::forward<A>(a);
1796 proceed(sb);
1797 }
1798};
1799
1800/**************************************************************************************************/
1801
1802template <class T>
1803template <class A>
1804void shared_base<T, enable_if_copyable<void_to_monostate_t<T>>>::set_value(A&& a) {
1805 value_<T>::set(*this, std::forward<A>(a));
1806}
1807
1808template <class T>
1809template <class A>
1810void shared_base<T, enable_if_not_copyable<void_to_monostate_t<T>>>::set_value(A&& a) {
1811 value_<T>::set(*this, std::forward<A>(a));
1812}
1813
1814/**************************************************************************************************/
1815
1816} // namespace detail
1817
1818/**************************************************************************************************/
1819
1821
1822STLAB_VERSION_NAMESPACE_END()
1823} // namespace stlab
1824
1825/**************************************************************************************************/
1826
1827#if STLAB_STD_COROUTINES()
1828
1829/**************************************************************************************************/
1830
1831namespace stlab {
1832STLAB_VERSION_NAMESPACE_BEGIN()
1833
1834
1837
1838namespace detail {
1839
1840// --- Generic awaitable support for resume_on(executor, any_awaitable) ---
1841// C++ coroutine protocol: awaitable is (1) has member operator co_await, (2) has free
1842// operator co_await, or (3) is directly an awaiter (await_ready/await_suspend/await_resume).
1843template <class A, class = void>
1844struct has_member_co_await : std::false_type {};
1845template <class A>
1846struct has_member_co_await<A, std::void_t<decltype(std::declval<A>().operator co_await())>>
1847 : std::true_type {};
1848
1849template <class A, class = void>
1850struct has_free_co_await : std::false_type {};
1851template <class A>
1852struct has_free_co_await<A, std::void_t<decltype(operator co_await(std::declval<A>()))>>
1853 : std::true_type {};
1854
1855template <class A>
1856auto get_awaiter(A&& a) {
1857 if constexpr (has_member_co_await<A>::value) {
1858 return std::forward<A>(a).operator co_await();
1859 } else if constexpr (has_free_co_await<A>::value) {
1860 return operator co_await(std::forward<A>(a));
1861 } else {
1862 return std::forward<A>(a); // direct awaiter (e.g. std::suspend_always)
1863 }
1864}
1865
1866template <class A>
1867using awaiter_t = decltype(get_awaiter(std::declval<A&>()));
1868
1869template <class A>
1870using await_result_t = decltype(std::declval<awaiter_t<A>>().await_resume());
1871
1872template <class A>
1873using await_result_storage_t = void_to_monostate_t<await_result_t<A>>;
1874
1875// Fire-and-forget return type for the proxy coroutine. No handle is retained after launch;
1876// the proxy owns itself and self-destroys at final_suspend (suspend_never).
1877struct proxy_fire_and_forget {
1878 struct promise_type {
1879 proxy_fire_and_forget get_return_object() { return {}; }
1880 std::suspend_never initial_suspend() { return {}; }
1881 std::suspend_never final_suspend() noexcept { return {}; }
1882 void return_void() {}
1883 void unhandled_exception() { std::terminate(); }
1884 };
1885};
1886
1892template <class A, class Inner, class E, class F>
1893proxy_fire_and_forget resume_on_proxy_coro(E executor,
1894 F resume_cb,
1895 Inner inner,
1896 std::optional<await_result_storage_t<A>>* result_ptr,
1897 std::exception_ptr* exception_ptr) {
1898 try {
1899 if constexpr (std::is_void_v<await_result_t<A>>) {
1900 co_await std::move(inner);
1901 result_ptr->emplace(std::monostate{});
1902 } else {
1903 result_ptr->emplace(co_await std::move(inner));
1904 }
1905 } catch (...) {
1906 if (exception_ptr) *exception_ptr = std::current_exception();
1907 }
1908 executor(std::move(resume_cb));
1909}
1910
1913template <class A, class Inner, class E, class F, class WeakPtr>
1914proxy_fire_and_forget resume_on_proxy_coro_controlled(
1915 E executor,
1916 F resume_cb,
1917 Inner inner,
1918 WeakPtr weak,
1919 std::optional<await_result_storage_t<A>>* result_ptr,
1920 std::exception_ptr* exception_ptr) {
1921 // proceed with co_await; if weak_state is destroyed during suspension, the proxy is
1922 // responsible for not writing to awaiter storage and not invoking the resume callback.
1923 bool was_alive = false;
1924 try {
1925 if constexpr (std::is_void_v<await_result_t<A>>) {
1926 co_await std::move(inner);
1927 if (auto keepalive = weak.lock()) {
1928 (void)keepalive;
1929 result_ptr->emplace(std::monostate{});
1930 was_alive = true;
1931 }
1932 } else {
1933 auto tmp = co_await std::move(inner);
1934 if (auto keepalive = weak.lock()) {
1935 (void)keepalive;
1936 result_ptr->emplace(std::move(tmp));
1937 was_alive = true;
1938 }
1939 }
1940 } catch (...) {
1941 if (auto keepalive = weak.lock()) {
1942 (void)keepalive;
1943 if (exception_ptr) *exception_ptr = std::current_exception();
1944 was_alive = true;
1945 }
1946 }
1947 if (was_alive) executor(std::move(resume_cb));
1948}
1949
1950template <class... Args>
1951struct final_awaiter {
1952 bool await_ready() noexcept { return false; }
1953 void await_resume() noexcept {}
1954 void await_suspend(std::coroutine_handle<> h) noexcept { h.destroy(); }
1955};
1956
1959template <class R, class E>
1960struct resume_on_awaiter {
1961 E _executor;
1962 future<R> _input;
1963
1964 bool await_ready() const noexcept { return false; }
1965
1966 auto await_resume() { return std::move(_input).get_ready(); }
1967
1968 void await_suspend(std::coroutine_handle<> ch) {
1969 _input.on_completion(std::move(_executor), [ch]() noexcept { ch.resume(); });
1970 }
1971};
1972
1973template <class R, class E>
1974resume_on_awaiter(E, future<R>) -> resume_on_awaiter<R, std::decay_t<E>>;
1975
1980template <class R, class E, class WeakPtr, bool AllowSkipSuspend>
1981struct resume_on_awaiter_with_control {
1982 E _executor;
1983 future<R> _input;
1984 WeakPtr _weak;
1985
1986 bool await_ready() const noexcept {
1987 if constexpr (AllowSkipSuspend) {
1988 return _input.is_ready();
1989 } else {
1990 return false;
1991 }
1992 }
1993 auto await_resume() { return std::move(_input).get_ready(); }
1994 void await_suspend(std::coroutine_handle<> ch) {
1995 assert(_weak.lock() && "await_suspend: weak_state is gone");
1996 _weak.lock()->_co_handle = ch.address();
1997 _input.on_completion(std::move(_executor), [weak = std::move(_weak)]() noexcept {
1998 if (auto state = weak.lock())
1999 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2000 });
2001 }
2002};
2003
2006template <class E>
2007struct resume_on_executor_awaiter {
2008 E _executor;
2009
2010 bool await_ready() const noexcept { return false; }
2011 void await_resume() noexcept {}
2012 void await_suspend(std::coroutine_handle<> ch) {
2013 std::move(_executor)([ch]() noexcept { ch.resume(); });
2014 }
2015};
2016
2019template <class E, class WeakPtr>
2020struct resume_on_executor_awaiter_with_control {
2021 E _executor;
2022 WeakPtr _weak;
2023
2024 [[nodiscard]] bool await_ready() const noexcept { return false; }
2025 void await_resume() noexcept {}
2026 void await_suspend(std::coroutine_handle<> ch) {
2027 assert(_weak.lock() && "await_suspend: weak_state is gone");
2028 _weak.lock()->_co_handle = ch.address();
2029 std::move(_executor)([weak = std::move(_weak)]() noexcept {
2030 if (auto state = weak.lock())
2031 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2032 });
2033 }
2034};
2035
2038template <class E, class A>
2039struct resume_on_any_awaiter {
2040 E _executor;
2041 A _awaitable;
2042 std::optional<await_result_storage_t<A>> _result;
2043 std::exception_ptr _exception;
2044
2045 bool await_ready() const noexcept { return false; }
2046
2047 await_result_t<A> await_resume() {
2048 if (_exception) std::rethrow_exception(_exception);
2049 if constexpr (std::is_void_v<await_result_t<A>>) {
2050 return;
2051 } else {
2052 return std::move(*_result);
2053 }
2054 }
2055
2056 void await_suspend(std::coroutine_handle<> ch) {
2057 auto inner = get_awaiter(std::move(_awaitable));
2058 if (inner.await_ready()) {
2059 try {
2060 if constexpr (std::is_void_v<await_result_t<A>>) {
2061 inner.await_resume();
2062 _result.emplace(std::monostate{});
2063 } else {
2064 _result.emplace(inner.await_resume());
2065 }
2066 } catch (...) {
2067 _exception = std::current_exception();
2068 }
2069 std::move(_executor)([ch]() noexcept { ch.resume(); });
2070 return;
2071 }
2072 resume_on_proxy_coro<std::decay_t<A>>(
2073 std::move(_executor), [ch]() noexcept { ch.resume(); }, std::move(inner), &_result,
2074 &_exception);
2075 }
2076};
2077
2081template <class E, class A, class WeakPtr>
2082struct resume_on_any_awaiter_with_control {
2083 E _executor;
2084 A _awaitable;
2085 WeakPtr _weak;
2086 std::optional<await_result_storage_t<A>> _result;
2087 std::exception_ptr _exception;
2088
2089 bool await_ready() const noexcept { return false; }
2090
2091 await_result_t<A> await_resume() {
2092 if (_exception) std::rethrow_exception(_exception);
2093 if constexpr (std::is_void_v<await_result_t<A>>) {
2094 return;
2095 } else {
2096 return std::move(*_result);
2097 }
2098 }
2099
2100 void await_suspend(std::coroutine_handle<> ch) {
2101 assert(_weak.lock() && "await_suspend: weak_state is gone");
2102 _weak.lock()->_co_handle = ch.address();
2103 auto inner = get_awaiter(std::move(_awaitable));
2104 if (inner.await_ready()) {
2105 try {
2106 if constexpr (std::is_void_v<await_result_t<A>>) {
2107 inner.await_resume();
2108 _result.emplace(std::monostate{});
2109 } else {
2110 _result.emplace(inner.await_resume());
2111 }
2112 } catch (...) {
2113 _exception = std::current_exception();
2114 }
2115 std::move(_executor)([weak = _weak]() noexcept {
2116 if (auto state = weak.lock())
2117 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2118 });
2119 return;
2120 }
2121 resume_on_proxy_coro_controlled<std::decay_t<A>>(
2122 std::move(_executor),
2123 [weak = _weak]() noexcept {
2124 if (auto state = weak.lock())
2125 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2126 },
2127 std::move(inner), _weak, &_result, &_exception);
2128 }
2129};
2130
2135template <class A>
2136struct cancelable_requires_stlab_future_coroutine {
2137 A _awaitable;
2138};
2139
2140template <>
2141struct cancelable_requires_stlab_future_coroutine<void> {};
2142
2147template <class WeakPtr>
2148struct cancelable_unique_checkpoint_awaiter {
2149 WeakPtr _weak;
2150
2151 [[nodiscard]] bool await_ready() const noexcept { return !_weak.expired(); }
2152 void await_resume() noexcept {}
2153 void await_suspend(std::coroutine_handle<> ch) { ch.destroy(); }
2154};
2155
2156} // namespace detail
2157
2160template <class E, class R>
2161auto resume_on(E&& executor, future<R>&& f) -> detail::resume_on_awaiter<R, std::decay_t<E>> {
2162 return detail::resume_on_awaiter{std::forward<E>(executor), std::move(f)};
2163}
2164
2167template <class E, class A>
2168auto resume_on(E&& executor, A&& awaitable)
2169 -> detail::resume_on_any_awaiter<std::decay_t<E>, std::decay_t<A>> {
2170 return detail::resume_on_any_awaiter<std::decay_t<E>, std::decay_t<A>>{
2171 std::forward<E>(executor), std::forward<A>(awaitable), {}, {}};
2172}
2173
2176template <class E>
2177auto resume_on(E&& executor) -> detail::resume_on_executor_awaiter<std::decay_t<E>> {
2178 return detail::resume_on_executor_awaiter<std::decay_t<E>>{std::forward<E>(executor)};
2179}
2180
2184template <class A, std::enable_if_t<!detail::is_future_v<std::decay_t<A>>, int> = 0>
2185auto cancelable(A&& awaitable)
2186 -> detail::cancelable_requires_stlab_future_coroutine<std::decay_t<A>> {
2187 return {std::forward<A>(awaitable)};
2188}
2189
2190template <class R>
2191auto cancelable(future<R>&) // Use co_await std::move(f); it is already cancelable.
2192 -> future<R> = delete;
2193
2194template <class R>
2195auto cancelable(future<R>&&) // Use co_await std::move(f); it is already cancelable.
2196 -> future<R> = delete;
2197
2198template <class R>
2199auto cancelable(const future<R>&) // Use co_await std::move(f); it is already cancelable.
2200 -> future<R> = delete;
2201
2205[[nodiscard]] inline auto cancelable() -> detail::cancelable_requires_stlab_future_coroutine<void> {
2206 return {};
2207}
2208
2210
2211STLAB_VERSION_NAMESPACE_END()
2212} // namespace stlab
2213
2214/**************************************************************************************************/
2215// Coroutine ownership and destruction rules (implementation notes):
2216//
2217// We destroy the coroutine only while it is suspended, in final_awaiter::await_suspend
2218// (h.destroy()). Before invoking the packaged_task (in
2219// return_value/return_void/unhandled_exception), we clear _co_handle in the shared state so
2220// ~shared_task() will not try to destroy the handle when the future is later destroyed. When
2221// suspended on a future we store the handle in _co_handle so we can resume via weak_state. For
2222// non-future awaitables we clear _co_handle before suspending (give up ownership for that suspend).
2223// initial_suspend is suspend_never so we never own before the first suspend.
2224// `co_await cancelable()` with no arguments: if weak_state is expired (last future already
2225// released), await_suspend destroys the coroutine handle directly — not via _co_handle. If the weak
2226// is still valid, await_ready avoids suspension so execution continues.
2227/**************************************************************************************************/
2228
2232
2233template <class T, class... Args>
2234struct std::coroutine_traits<stlab::future<T>, Args...> { // NOLINT(cert-dcl58-cpp)
2235 struct promise_type {
2236 stlab::packaged_task<std::variant<T, std::exception_ptr>> _promise;
2237
2238 stlab::future<T> get_return_object() {
2239 auto [pro, fut] = stlab::package<T(std::variant<T, std::exception_ptr>)>(
2240 stlab::immediate_executor,
2241 [](std::variant<T, std::exception_ptr>&& v) mutable -> T {
2242 // Use index (1 = exception) to avoid ambiguity when T is std::exception_ptr.
2243 if (auto* ep = std::get_if<1>(&v)) {
2244 std::rethrow_exception(*ep);
2245 }
2246 return std::get<0>(std::move(v));
2247 });
2248 _promise = std::move(pro);
2249 return std::move(fut);
2250 }
2251
2252 auto initial_suspend() const noexcept { return std::suspend_never{}; }
2253
2254 auto final_suspend() noexcept {
2255 assert(_promise.canceled() && "final_suspend: promise not fulfilled");
2256 return stlab::detail::final_awaiter<std::variant<T, std::exception_ptr>>{};
2257 }
2258
2259 template <class U>
2260 void return_value(U&& val) {
2261 _promise(
2262 std::variant<T, std::exception_ptr>{std::in_place_index<0>, std::forward<U>(val)});
2263 }
2264
2265 void unhandled_exception() {
2266 _promise(std::variant<T, std::exception_ptr>{std::in_place_index<1>,
2267 std::current_exception()});
2268 }
2269
2270 template <class R>
2271 auto await_transform(stlab::future<R>&& f) {
2272 return stlab::detail::resume_on_awaiter_with_control<
2273 R, decltype(stlab::immediate_executor),
2274 decltype(stlab::detail::weak_state(_promise)), true>{
2275 stlab::immediate_executor, std::move(f), stlab::detail::weak_state(_promise)};
2276 }
2277 template <class R, class E>
2278 auto await_transform(stlab::detail::resume_on_awaiter<R, E> a) {
2279 return stlab::detail::resume_on_awaiter_with_control<
2280 R, E, decltype(stlab::detail::weak_state(_promise)), false>{
2281 std::move(a._executor), std::move(a._input), stlab::detail::weak_state(_promise)};
2282 }
2283 template <class E>
2284 auto await_transform(stlab::detail::resume_on_executor_awaiter<E> a) {
2285 return stlab::detail::resume_on_executor_awaiter_with_control<
2286 E, decltype(stlab::detail::weak_state(_promise))>{
2287 std::move(a._executor), stlab::detail::weak_state(_promise)};
2288 }
2289 template <class E, class A>
2290 auto await_transform(stlab::detail::resume_on_any_awaiter<E, A> a) {
2291 return stlab::detail::resume_on_any_awaiter_with_control<
2292 E, A, decltype(stlab::detail::weak_state(_promise))>{
2293 std::move(a._executor), std::move(a._awaitable),
2294 stlab::detail::weak_state(_promise), std::move(a._result), std::move(a._exception)};
2295 }
2296 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<void>) {
2297 return stlab::detail::cancelable_unique_checkpoint_awaiter<
2298 decltype(stlab::detail::weak_state(_promise))>{stlab::detail::weak_state(_promise)};
2299 }
2300 template <class A, std::enable_if_t<!std::is_void_v<A>, int> = 0>
2301 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<A> c) {
2302 return stlab::detail::resume_on_any_awaiter_with_control<
2303 decltype(stlab::immediate_executor), A,
2304 decltype(stlab::detail::weak_state(_promise))>{stlab::immediate_executor,
2305 std::move(c._awaitable),
2306 stlab::detail::weak_state(_promise),
2307 {},
2308 {}};
2309 }
2310 template <class U>
2311 U&& await_transform(U&& u) {
2312 if (auto state = stlab::detail::weak_state(_promise).lock())
2313 state->_co_handle = nullptr;
2314 return std::forward<U>(u);
2315 }
2316 };
2317};
2318
2319template <class... Args>
2320struct std::coroutine_traits<stlab::future<void>, Args...> {
2321 struct promise_type {
2322 stlab::packaged_task<std::exception_ptr> _promise;
2323
2324 stlab::future<void> get_return_object() {
2326 stlab::immediate_executor, [](const std::exception_ptr& ep) mutable {
2327 if (ep) std::rethrow_exception(ep);
2328 });
2329 _promise = std::move(pro);
2330 return std::move(fut);
2331 }
2332
2333 auto initial_suspend() const noexcept { return std::suspend_never{}; }
2334
2335 auto final_suspend() noexcept {
2336 assert(_promise.canceled() && "final_suspend: promise not fulfilled");
2337 return stlab::detail::final_awaiter<std::exception_ptr>{};
2338 }
2339
2340 void return_void() { _promise(std::exception_ptr{}); }
2341
2342 void unhandled_exception() { _promise(std::current_exception()); }
2343
2344 template <class R>
2345 auto await_transform(stlab::future<R>&& f) {
2346 return stlab::detail::resume_on_awaiter_with_control<
2347 R, decltype(stlab::immediate_executor),
2348 decltype(stlab::detail::weak_state(_promise)), true>{
2349 stlab::immediate_executor, std::move(f), stlab::detail::weak_state(_promise)};
2350 }
2351 template <class R, class E>
2352 auto await_transform(stlab::detail::resume_on_awaiter<R, E> a) {
2353 return stlab::detail::resume_on_awaiter_with_control<
2354 R, E, decltype(stlab::detail::weak_state(_promise)), false>{
2355 std::move(a._executor), std::move(a._input), stlab::detail::weak_state(_promise)};
2356 }
2357 template <class E>
2358 auto await_transform(stlab::detail::resume_on_executor_awaiter<E> a) {
2359 return stlab::detail::resume_on_executor_awaiter_with_control<
2360 E, decltype(stlab::detail::weak_state(_promise))>{
2361 std::move(a._executor), stlab::detail::weak_state(_promise)};
2362 }
2363 template <class E, class A>
2364 auto await_transform(stlab::detail::resume_on_any_awaiter<E, A> a) {
2365 return stlab::detail::resume_on_any_awaiter_with_control<
2366 E, A, decltype(stlab::detail::weak_state(_promise))>{
2367 std::move(a._executor), std::move(a._awaitable),
2368 stlab::detail::weak_state(_promise), std::move(a._result), std::move(a._exception)};
2369 }
2370 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<void>) {
2371 return stlab::detail::cancelable_unique_checkpoint_awaiter<
2372 decltype(stlab::detail::weak_state(_promise))>{stlab::detail::weak_state(_promise)};
2373 }
2374 template <class A, std::enable_if_t<!std::is_void_v<A>, int> = 0>
2375 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<A> c) {
2376 return stlab::detail::resume_on_any_awaiter_with_control<
2377 decltype(stlab::immediate_executor), A,
2378 decltype(stlab::detail::weak_state(_promise))>{stlab::immediate_executor,
2379 std::move(c._awaitable),
2380 stlab::detail::weak_state(_promise),
2381 {},
2382 {}};
2383 }
2384 template <class U>
2385 U&& await_transform(U&& u) {
2386 if (auto state = stlab::detail::weak_state(_promise).lock())
2387 state->_co_handle = nullptr;
2388 return std::forward<U>(u);
2389 }
2390 };
2391};
2392
2393/**************************************************************************************************/
2394
2395template <class R>
2396auto operator co_await(stlab::future<R>&& f) {
2397 return stlab::detail::resume_on_awaiter{stlab::immediate_executor, std::move(f)};
2398}
2399
2400// co_await on an lvalue future is deleted — use std::move(f) to make cancellation semantics
2401// explicit
2402template <class R>
2403auto operator co_await(stlab::future<R>& f) = delete;
2404
2406
2407#endif // STLAB_STD_COROUTINES()
2408
2409#endif
auto get_ready() &&
Same as get_ready() but may move the value when this is the only reference.
Definition future.hpp:1101
auto get_ready() const &
Returns the value.
Definition future.hpp:1098
auto recover(E &&executor, F &&f) &&
Returns a future that completes with the result of f given this future, run on executor....
Definition future.hpp:1044
auto recover(E &&executor, F &&f) const &
Returns a future that completes with the result of f given this future, run on executor.
Definition future.hpp:1017
auto get_try() &&
Same as get_try() but may move the value when this is the only reference.
Definition future.hpp:1095
auto operator|(executor_task_pair< F > etp) const &
Pipe operator: same as then(etp.executor(), etp.task()).
Definition future.hpp:962
auto operator^(F &&f) &&
Pipe operator: same as recover(f). Rvalue overload; consumes *this.
Definition future.hpp:1037
auto then(E &&executor, F &&f) &&
Returns a future that completes with the result of f on executor applied to this future's value....
Definition future.hpp:986
auto then(F &&f) &&
Returns a future that completes with the result of f applied to this future's value (default executor...
Definition future.hpp:969
void on_completion(F &&f)
Invokes f immediately when this future completes (value or exception). Requires f is noexcept.
Definition future.hpp:1073
void swap(future &x) noexcept
Exchanges the shared state with x.
Definition future.hpp:911
auto operator^(F &&f) const &
Pipe operator: same as recover(f).
Definition future.hpp:1011
void detach(F &&f) &&
When this future completes (value or exception), invokes f with it and does not propagate further.
Definition future.hpp:1065
auto operator|(executor_task_pair< F > etp) &&
Pipe operator: same as then(etp.executor(), etp.task()). Rvalue overload; consumes *this.
Definition future.hpp:998
friend auto operator!=(const future &x, const future &y) -> bool
Equality compares identity of the shared state (the underlying shared_ptr), not the value.
Definition future.hpp:919
friend auto future_with_broken_promise(E &&) -> detail::reduced_t< U >
Returns a future of type T that is already ready with a broken_promise error (e.g....
Definition future.hpp:1341
auto then(F &&f) const &
Returns a future that completes with the result of f applied to this future's value (default executor...
Definition future.hpp:928
friend void swap(future &x, future &y) noexcept
Exchanges the shared states of x and y.
Definition future.hpp:914
auto get_try() const &
Returns the value if ready, or std::nullopt / false (for void) if not; rethrows if completed with exc...
Definition future.hpp:1092
void detach() &&
Drops this future without cancelling the associated task.
Definition future.hpp:1057
auto exception() const &-> std::exception_ptr
Returns the stored exception, or a null exception_ptr if the future completed with a value.
Definition future.hpp:1105
friend auto package(E &&, F &&) -> std::pair< detail::packaged_task_from_signature_t< Sig >, detail::reduced_result_t< Sig > >
Creates a packaged task and its future for the callable f, run on executor.
Definition future.hpp:1282
auto valid() const -> bool
True if this future has an associated shared state.
Definition future.hpp:923
void reset() noexcept
Releases the shared state; valid() becomes false.
Definition future.hpp:1085
auto then(E &&executor, F &&f) const &
Returns a future that completes with the result of f on executor applied to this future's value.
Definition future.hpp:951
auto operator|(F &&f) const &
Pipe operator: same as then(f).
Definition future.hpp:944
auto is_ready() const &-> bool
True if the result or exception is available.
Definition future.hpp:1088
auto recover(F &&f) &&
Returns a future that completes with the result of f given this future (possibly in error state); def...
Definition future.hpp:1030
T result_type
The type of the value this future holds.
Definition future.hpp:902
void on_completion(E &&executor, F &&f)
Invokes f when this future completes (value or exception), on executor. Requires f is noexcept.
Definition future.hpp:1080
auto operator^(executor_task_pair< F > etp) const &
Pipe operator: same as recover(etp.executor(), etp.task()).
Definition future.hpp:1023
auto recover(F &&f) const &
Returns a future that completes with the result of f given this future (possibly in error state); def...
Definition future.hpp:1005
auto operator^(executor_task_pair< F > etp) &&
Pipe operator: same as recover(etp.executor(), etp.task()). Rvalue overload; consumes *this.
Definition future.hpp:1052
auto operator|(F &&f) &&
Pipe operator: same as then(f). Rvalue overload; consumes *this.
Definition future.hpp:979
friend auto operator==(const future &x, const future &y) -> bool
Equality compares identity of the shared state (the underlying shared_ptr), not the value.
Definition future.hpp:918
auto get_ready() &&-> T
Moves out the value; rethrows the stored exception if the future failed.
Definition future.hpp:1269
auto exception() const &-> std::exception_ptr
Returns the stored exception, or a null exception_ptr if the future completed with a value.
Definition future.hpp:1273
auto recover(E &&executor, F &&f) &&
Returns a future that completes with the result of f given this future, run on executor.
Definition future.hpp:1215
void detach() &&
Drops this future without cancelling the associated task.
Definition future.hpp:1227
auto then(E &&executor, F &&f) &&
Returns a future that completes with the result of f on executor applied to this future's value.
Definition future.hpp:1186
auto then(F &&f) &&
Returns a future that completes with the result of f applied to this future's value (rvalue only).
Definition future.hpp:1167
auto valid() const -> bool
True if this future has an associated shared state.
Definition future.hpp:1162
auto recover(F &&f) &&
Returns a future that completes with the result of f given this future (possibly in error state); def...
Definition future.hpp:1202
T result_type
The type of the value this future holds.
Definition future.hpp:1141
void reset() noexcept
Releases the shared state; valid() becomes false.
Definition future.hpp:1257
auto is_ready() const &-> bool
True if the result or exception is available.
Definition future.hpp:1260
friend auto operator!=(const future &x, const future &y) -> bool
Equality compares identity of the shared state (the underlying shared_ptr), not the value.
Definition future.hpp:1158
auto operator^(F &&f) &&
Pipe operator: same as recover(f).
Definition future.hpp:1209
friend auto future_with_broken_promise(E &&) -> detail::reduced_t< U >
Returns a future of type T that is already ready with a broken_promise error (e.g....
Definition future.hpp:1341
void detach(F &&f) &&
When this future completes (value or exception), invokes f with it and does not propagate further.
Definition future.hpp:1235
void swap(future &x) noexcept
Exchanges the shared state with x.
Definition future.hpp:1150
auto operator^(executor_task_pair< F > etp) &&
Pipe operator: same as recover(etp.executor(), etp.task()).
Definition future.hpp:1222
friend void swap(future &x, future &y) noexcept
Exchanges the shared states of x and y.
Definition future.hpp:1153
friend auto package(E &&, F &&) -> std::pair< detail::packaged_task_from_signature_t< Sig >, detail::reduced_result_t< Sig > >
Creates a packaged task and its future for the callable f, run on executor.
Definition future.hpp:1282
void on_completion(E &&executor, F &&f)
Invokes f when this future completes (value or exception), on executor. This consumes the continuatio...
Definition future.hpp:1252
auto operator|(executor_task_pair< F > etp) &&
Pipe operator: same as then(etp.executor(), etp.task()).
Definition future.hpp:1195
auto get_try() &-> std::optional< T >
Moves out the value if ready, otherwise returns std::nullopt without changing the future....
Definition future.hpp:1265
auto operator|(F &&f) &&
Pipe operator: same as then(f).
Definition future.hpp:1179
void on_completion(F &&f)
Invokes f immediately when this future completes (value or exception). This consumes the continuation...
Definition future.hpp:1244
friend auto operator==(const future &x, const future &y) -> bool
Equality compares identity of the shared state (the underlying shared_ptr), not the value.
Definition future.hpp:1157
Exception thrown when a future-related contract is violated (e.g. broken_promise, no_state).
Definition future.hpp:231
auto what() const noexcept -> const char *override
Human-readable message for the stored error code.
Definition future.hpp:239
auto code() const noexcept -> const future_error_codes &
The error code that caused this exception.
Definition future.hpp:236
One-shot asynchronous result: holds a value or exception produced by a promise or packaged_task.
Definition future.hpp:306
Invocable that completes a future when called with arguments of type Args...; created by package().
Definition future.hpp:795
auto canceled() const -> bool
True if the associated future was released.
Definition future.hpp:848
friend auto future_with_broken_promise(E &&) -> detail::reduced_t< T >
Returns a future of type T that is already ready with a broken_promise error (e.g....
Definition future.hpp:1341
void set_exception(const std::exception_ptr &error) noexcept
Completes the future with error without invoking the callable.
Definition future.hpp:851
friend auto package(E &&, F &&) -> std::pair< detail::packaged_task_from_signature_t< Sig >, detail::reduced_result_t< Sig > >
Creates a packaged task and its future for the callable f, run on executor.
Definition future.hpp:1282
void operator()(A &&... args) noexcept
Invokes the packaged callable with args and completes the associated future. Clears _co_handle before...
Definition future.hpp:839
Reference unwrapping and related functional helpers.
constexpr bool is_monostate_v
True if T is std::monostate.
Definition future.hpp:140
auto when_all(const E &executor, F f, future< Ts >... args)
Returns a future for f scheduled on executor with all input values.
Definition future.hpp:1524
auto async(const E &executor, F &&f, Args &&... args) -> detail::reduced_t< detail::result_t< std::decay_t< F >, std::decay_t< Args >... > >
Runs f with args on executor and returns a future for the result.
Definition future.hpp:1739
auto monostate_to_empty_tuple(T &&a)
Converts std::monostate to std::tuple{}; wraps other types in std::tuple for uniform application.
Definition future.hpp:165
auto package(E &&, F &&) -> std::pair< detail::packaged_task_from_signature_t< Sig >, detail::reduced_result_t< Sig > >
Creates a packaged task and its future for the callable f, run on executor.
Definition future.hpp:1282
auto invoke_void_to_monostate_result(F &&f, Args &&... args)
Invokes f with args and returns its result, or std::monostate{} if the result is void.
Definition future.hpp:119
future_error_codes
Error codes for future_error.
Definition future.hpp:190
auto future_with_broken_promise(E &&executor) -> detail::reduced_t< T >
Returns a future of type T that is already ready with a broken_promise error (e.g....
Definition future.hpp:1341
auto when_any(E &&executor, F &&f, future< T > &&arg, future< Ts > &&... args)
Returns a future for f invoked with the first successful input's value and its std::size_t index....
Definition future.hpp:1613
auto optional_monostate_to_bool(std::optional< T > &&o)
Returns o.has_value() when T is std::monostate, otherwise std::move(o).
Definition future.hpp:144
auto monostate_to_void(T &&a)
Converts std::monostate to void (no return); forwards other types unchanged.
Definition future.hpp:154
auto invoke_remove_monostate_arguments(F &&f, Args &&... args)
Invokes f with args after removing std::monostate values (for void future results).
Definition future.hpp:175
typename void_to_monostate< T >::type void_to_monostate_t
Alias for void_to_monostate<T>::type.
Definition future.hpp:136
@ broken_promise
Promise was destroyed without setting a value or exception.
Definition future.hpp:191
@ no_state
Operation required a valid shared state.
Definition future.hpp:192
constexpr auto copy(T &&value) noexcept(noexcept(std::decay_t< T >{static_cast< T && >(value)})) -> std::decay_t< T >
Returns a copy of the argument. Used to pass an lvalue to a function taking an rvalue,...
Definition utility.hpp:144
constexpr auto move(T &&t) noexcept -> std::remove_reference_t< T > &&
A standard move implementation but with a compile-time check for const types.
Definition utility.hpp:154
Small memory-related utilities (make_weak_ptr).
Definition reverse.hpp:28
static auto make(E &&executor, F &&f, future< Ts >... args)
Joins inputs and schedules f with the first successful index.
Definition future.hpp:1576
Helper to implement when_any for result type T.
Definition future.hpp:1546
static auto make(const E &executor, F f, future< T > arg, future< Ts >... args)
Joins inputs and schedules f with the first successful value and index.
Definition future.hpp:1550
Maps void to std::monostate for uniform future result storage; other types unchanged.
Definition future.hpp:130
Type traits and detection helpers used by the concurrency library.
Tuple algorithms and utilities (including for future combiners).
Type utilities (move, copy, static_max, etc.).