stlab 2.3.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 <cstdint>
19#include <exception>
20#include <functional>
21#include <initializer_list>
22#include <memory>
23#include <mutex>
24#include <optional>
25#include <utility>
26#include <variant> // for std::monostate
27#include <vector>
28
29#if STLAB_STD_COROUTINES()
30#include <coroutine>
31#endif
32
38#include <stlab/functional.hpp>
39#include <stlab/memory.hpp>
40#include <stlab/utility.hpp>
41
42/**************************************************************************************************/
43
93
94/**************************************************************************************************/
95
96namespace stlab {
97STLAB_VERSION_NAMESPACE_BEGIN()
98
99
110
111/**************************************************************************************************/
112
113
114template <class F, class... Args>
115auto invoke_void_to_monostate_result(F&& f, Args&&... args) {
116 if constexpr (std::is_void_v<std::invoke_result_t<F, Args...>>) {
117 std::forward<F>(f)(std::forward<Args>(args)...);
118 return std::monostate{};
119 } else {
120 return std::forward<F>(f)(std::forward<Args>(args)...);
121 }
122}
123
125template <class T>
127 using type = std::conditional_t<std::is_void_v<T>, std::monostate, T>;
128};
129
131template <class T>
132using void_to_monostate_t = typename void_to_monostate<T>::type;
133
135template <class T>
136inline constexpr bool is_monostate_v = std::is_same_v<T, std::monostate>;
137
139template <class T>
140auto optional_monostate_to_bool(std::optional<T>&& o) {
141 if constexpr (is_monostate_v<T>) {
142 return o.has_value();
143 } else {
144 return std::move(o);
145 }
146}
147
149template <class T>
150auto monostate_to_void(T&& a) {
151 if constexpr (is_monostate_v<T>) {
152 return;
153 } else {
154 return std::forward<T>(a);
155 }
156}
157
160template <class T>
162 if constexpr (is_monostate_v<T>) {
163 return std::tuple{};
164 } else {
165 return std::forward_as_tuple(std::forward<T>(a));
166 }
167}
168
170template <class F, class... Args>
171auto invoke_remove_monostate_arguments(F&& f, Args&&... args) {
172 return std::apply(
173 [&](auto&&... args) { return std::forward<F>(f)(std::forward<decltype(args)>(args)...); },
174 std::apply(
175 [&](auto&&... args) {
176 return std::tuple_cat(monostate_to_empty_tuple(std::forward<Args>(args))...);
177 },
178 std::forward_as_tuple(std::forward<Args>(args)...)));
179}
180
181/**************************************************************************************************/
182
184enum class future_error_codes : std::uint8_t {
186 no_state
187};
188
189/**************************************************************************************************/
190
191namespace detail {
192
193inline auto Future_error_map(future_error_codes code) noexcept -> const
194 char* { // convert to name of future error
195 switch (code) { // switch on error code value
197 return "broken promise";
198
200 return "no state";
201
202 default:
203 return nullptr;
204 }
205}
206
207/**************************************************************************************************/
208
209// This could be lifted into a common header if needed in other places
210#if STLAB_CPP_VERSION_AT_LEAST(17)
211template <class F, class... Args>
212using result_t = std::invoke_result_t<F, Args...>;
213#else
214template <class F, class... Args>
215using result_t = std::result_of_t<F(Args...)>;
216#endif
217
218/**************************************************************************************************/
219
220} // namespace detail
221
222/**************************************************************************************************/
223
225class future_error : public std::logic_error {
226public:
227 explicit future_error(future_error_codes code) : logic_error(""), _code(code) {}
228
230 [[nodiscard]] auto code() const noexcept -> const future_error_codes& { return _code; }
231
233 [[nodiscard]] auto what() const noexcept -> const char* override {
234 return detail::Future_error_map(_code);
235 }
236
237private:
238 const future_error_codes _code; // the stored error code
239};
240
241/**************************************************************************************************/
242
243namespace detail {
244
245/**************************************************************************************************/
246
247template <class>
248struct result_of_;
249
250template <class R, class... Args>
251struct result_of_<R(Args...)> {
252 using type = R;
253};
254
255template <class F>
256using result_of_t_ = typename result_of_<F>::type;
257
258template <class F, class T>
259struct result_of_when_all_t;
260
261template <class F>
262struct result_of_when_all_t<F, void> {
263 using result_type = detail::result_t<F>;
264};
265
266template <class F, class T>
267struct result_of_when_all_t {
268 using result_type = detail::result_t<F, const std::vector<T>&>;
269};
270
271template <class F, class T>
272struct result_of_when_any_t;
273
274template <class F>
275struct result_of_when_any_t<F, void> {
276 using result_type = detail::result_t<F, size_t>;
277};
278
279template <class F, class R>
280struct result_of_when_any_t {
281 using result_type = detail::result_t<F, R, size_t>;
282};
283
284template <class T>
285auto unique_usage(const std::shared_ptr<T>& p) -> bool {
286 return p.use_count() == 1;
287}
288
289/**************************************************************************************************/
290
291} // namespace detail
292
293/**************************************************************************************************/
294
295template <class...>
296class packaged_task;
297
299template <class, class = void>
300class future;
301
302/**************************************************************************************************/
303
304namespace detail {
305
306/**************************************************************************************************/
307
308template <class>
309struct packaged_task_from_signature;
310
311template <class R, class... Args>
312struct packaged_task_from_signature<R(Args...)> {
313 using type = packaged_task<Args...>;
314};
315
316template <class T>
317using packaged_task_from_signature_t = typename packaged_task_from_signature<T>::type;
318
319/**************************************************************************************************/
320
321template <class>
322struct reduced_signature;
323
324template <class R, class... Args>
325struct reduced_signature<R(Args...)> {
326 using type = R(Args...);
327};
328
329template <class R, class... Args>
330struct reduced_signature<future<R>(Args...)> {
331 using type = R(Args...);
332};
333
334template <class T>
335using reduced_signature_t = typename reduced_signature<T>::type;
336
337/**************************************************************************************************/
338
339template <class T>
340inline constexpr bool is_future_v = false;
341
342template <class T>
343inline constexpr bool is_future_v<future<T>> = true;
344
345template <class T>
346using reduced_t = std::conditional_t<is_future_v<T>, T, future<T>>;
347
348template <class Sig>
349using reduced_result_t = reduced_t<result_of_t_<Sig>>;
350
351/**************************************************************************************************/
352
353template <class T, class = void>
354struct value_;
355
356} // namespace detail
357
358/**************************************************************************************************/
359
362template <class Sig, class E, class F>
363auto package(E, F&&)
364 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
365
366/**************************************************************************************************/
367
368namespace detail {
369
370/**************************************************************************************************/
371
372template <class, class>
373struct shared;
374template <class, class = void>
375struct shared_base;
376
377/**************************************************************************************************/
378
379template <class... Args>
380struct shared_task {
381 void* _co_handle{nullptr}; // storing as void* for ABI stability.
382
383 virtual ~shared_task() {
384#if STLAB_STD_COROUTINES()
385 if (_co_handle) std::coroutine_handle<>::from_address(_co_handle).destroy();
386#endif
387 }
388
389 virtual void operator()(Args...) = 0;
390 virtual void set_exception(const std::exception_ptr&) noexcept = 0;
391};
392
393/**************************************************************************************************/
394
395template <class T>
396struct shared_base<T, enable_if_copyable<void_to_monostate_t<T>>>
397 : std::enable_shared_from_this<shared_base<T>> {
398 using then_t = std::vector<std::pair<executor_t, task<void() noexcept>>>;
399
400 using type = void_to_monostate_t<T>;
401
402 executor_t _executor;
403 std::optional<type> _result;
404 std::exception_ptr _exception;
405 std::mutex _mutex;
406 std::atomic_bool _ready{false};
407 then_t _then;
408
409 explicit shared_base(executor_t s) : _executor(std::move(s)) {}
410
411 template <class F>
412 auto recover(future<T>&& p, F&& f) {
413 return recover(std::move(p), _executor, std::forward<F>(f));
414 }
415
416 template <class E, class F>
417 auto recover(future<T>&& p, E executor, F&& f) {
418 using result_type = detail::result_t<F, future<T>>;
419
420 auto [pro, fut] = package<result_type()>(
421 executor, [_f = std::forward<F>(f), _p = std::move(p)]() mutable {
422 return std::move(_f)(std::move(_p));
423 });
424
425 bool ready{false};
426 {
427 std::unique_lock<std::mutex> lock(_mutex);
428 ready = _ready;
429 if (!ready) _then.emplace_back(std::move(executor), std::move(pro));
430 }
431
432 if (ready) executor(std::move(pro)); // cannot reference this after here
433
434 return std::move(fut);
435 }
436
437 template <class F>
438 void _detach(future<T>&& p, F&& f) {
439 auto pro = [_f = std::forward<F>(f), _p = std::move(p)]() mutable noexcept {
440 std::move(_f)(std::move(_p));
441 };
442
443 bool ready{false};
444 {
445 std::unique_lock<std::mutex> lock(_mutex);
446 ready = _ready;
447 if (!ready) _then.emplace_back(immediate_executor, std::move(pro));
448 }
449
450 if (ready) std::move(pro)(); // cannot reference this after here
451 }
452
453 void _detach() {
454 std::unique_lock<std::mutex> lock(_mutex);
455 if (!_ready)
456 _then.emplace_back([](auto&&) {}, [_p = this->shared_from_this()]() noexcept {});
457 }
458
459 template <class F>
460 void _on_completion(F&& f) {
461 task<void() noexcept> t(std::forward<F>(f));
462 {
463 std::unique_lock<std::mutex> lock(_mutex);
464 if (!_ready) {
465 _then.emplace_back(immediate_executor, std::move(t));
466 return;
467 }
468 }
469 std::move(t)();
470 }
471
472 template <class E, class F>
473 void _on_completion(E executor, F&& f) {
474 task<void() noexcept> t(std::forward<F>(f));
475 {
476 std::unique_lock<std::mutex> lock(_mutex);
477 if (!_ready) {
478 _then.emplace_back(std::move(executor), std::move(t));
479 return;
480 }
481 }
482 executor(std::move(t));
483 }
484
485 void _set_exception(const std::exception_ptr& error) noexcept {
486 _exception = error;
487 then_t then;
488 {
489 std::unique_lock<std::mutex> lock(_mutex);
490 then = std::move(_then);
491 _ready = true;
492 }
493 // propagate exception with scheduling
494 for (auto& e : then) {
495 e.first(std::move(e.second));
496 }
497 }
498
499 template <class A>
500 void set_value(A&& a);
501
502 auto is_ready() const& -> bool { return _ready; }
503
504 // get_ready() is called internally on continuations when we know _ready is true;
505 auto get_ready() -> const type& {
506 assert(is_ready() && "FATAL (sean.parent) : get_ready() called but not ready!");
507
508 if (_exception) std::rethrow_exception(_exception);
509 return *_result;
510 }
511
512 auto get_ready_r(bool unique) -> type {
513 if (!unique) return get_ready();
514
515 assert(is_ready() && "FATAL (sean.parent) : get_ready() called but not ready!");
516
517 if (_exception) std::rethrow_exception(_exception);
518 return std::move(*_result);
519 }
520
521 auto get_try() -> std::optional<type> {
522 bool ready = false;
523 {
524 std::unique_lock<std::mutex> lock(_mutex);
525 ready = _ready;
526 }
527 if (ready) {
528 if (_exception) std::rethrow_exception(_exception);
529 return _result;
530 }
531 return std::nullopt;
532 }
533
534 auto get_try_r(bool unique) -> std::optional<type> {
535 if (!unique) return get_try();
536
537 bool ready = false;
538 {
539 std::unique_lock<std::mutex> lock(_mutex);
540 ready = _ready;
541 }
542 if (ready) {
543 if (_exception) std::rethrow_exception(_exception);
544 return std::move(_result);
545 }
546 return std::nullopt;
547 }
548};
549
550/**************************************************************************************************/
551
552template <class T>
553struct shared_base<T, enable_if_not_copyable<void_to_monostate_t<T>>>
554 : std::enable_shared_from_this<shared_base<T>> {
555 using then_t = std::pair<executor_t, task<void() noexcept>>;
556
557 executor_t _executor;
558 std::optional<T> _result;
559 std::exception_ptr _exception;
560 std::mutex _mutex;
561 std::atomic_bool _ready{false};
562 then_t _then;
563
564 explicit shared_base(executor_t s) : _executor(std::move(s)) {}
565
566 template <class F>
567 auto recover(future<T>&& p, F&& f) {
568 return recover(std::move(p), _executor, std::forward<F>(f));
569 }
570
571 /*
572 NOTE : executor cannot be a reference type here. When invoked it could
573 cause _this_ to be deleted, and the executor passed in may be
574 this->_executor
575 */
576 template <class E, class F>
577 auto recover(future<T>&& p, E executor, F&& f) {
578 using result_type = detail::result_t<F, future<T>>;
579
580 auto [pro, fut] = package<result_type()>(
581 executor, [_f = std::forward<F>(f), _p = std::move(p)]() mutable {
582 return std::move(_f)(std::move(_p));
583 });
584
585 bool ready{false};
586 {
587 std::unique_lock<std::mutex> lock(_mutex);
588 ready = _ready;
589 if (!ready) {
590 assert(!_then.second && "recover: _then slot already occupied");
591 _then = {std::move(executor), std::move(pro)};
592 }
593 }
594 if (ready) executor(std::move(pro)); // cannot reference this after here
595
596 return std::move(fut);
597 }
598
599 template <class F>
600 void _detach(future<T>&& p, F&& f) {
601 auto pro = [_f = std::forward<F>(f), _p = std::move(p)]() mutable noexcept {
602 std::move(_f)(std::move(_p));
603 };
604
605 bool ready{false};
606 {
607 std::unique_lock<std::mutex> lock(_mutex);
608 ready = _ready;
609 if (!ready) _then = {immediate_executor, std::move(pro)};
610 }
611
612 if (ready) std::move(pro)(); // cannot reference this after here
613 }
614
615 void _detach() {
616 std::unique_lock<std::mutex> lock(_mutex);
617 if (!_ready) _then = then_t([](auto&&) {}, [_p = this->shared_from_this()]() noexcept {});
618 }
619
620 template <class F>
621 void _on_completion(F&& f) {
622 task<void() noexcept> t(std::forward<F>(f));
623 {
624 std::unique_lock<std::mutex> lock(_mutex);
625 if (!_ready) {
626 assert(!_then.second && "on_completion: _then slot already occupied");
627 _then = {immediate_executor, std::move(t)};
628 return;
629 }
630 }
631 std::move(t)();
632 }
633
634 template <class E, class F>
635 void _on_completion(E executor, F&& f) {
636 task<void() noexcept> t(std::forward<F>(f));
637 {
638 std::unique_lock<std::mutex> lock(_mutex);
639 if (!_ready) {
640 assert(!_then.second && "on_completion: _then slot already occupied");
641 _then = {std::move(executor), std::move(t)};
642 return;
643 }
644 }
645 executor(std::move(t));
646 }
647
648 void _set_exception(const std::exception_ptr& error) noexcept {
649 _exception = error;
650 then_t then;
651 {
652 std::unique_lock<std::mutex> lock(_mutex);
653 if (_then.second) then = std::move(_then);
654 _ready = true;
655 }
656 // propagate exception with scheduling
657 if (then.second) then.first(std::move(then.second));
658 }
659
660 template <class A>
661 void set_value(A&& a);
662
663 auto is_ready() const -> bool {
664 return _ready;
665 } // get_ready() is called internally on continuations when we know _ready is true;
666
667 auto get_ready() -> const T& { return get_ready_r(true); }
668
669 auto get_ready_r(bool) -> T {
670 assert(is_ready() && "FATAL (sean.parent) : get_ready() called but not ready!");
671
672 if (_exception) std::rethrow_exception(_exception);
673 return std::move(*_result);
674 }
675
676 auto get_try() -> std::optional<T> { return get_try_r(true); }
677
678 auto get_try_r(bool) -> std::optional<T> {
679 bool ready = false;
680 {
681 std::unique_lock<std::mutex> lock(_mutex);
682 ready = _ready;
683 }
684 if (ready) {
685 if (_exception) std::rethrow_exception(_exception);
686 return std::move(_result);
687 }
688 return {};
689 }
690};
691
692/**************************************************************************************************/
693
696template <class R>
697class promise {
698 using type = void_to_monostate_t<R>;
699 std::weak_ptr<shared_base<R>> _p;
700
701public:
702 explicit promise(const std::shared_ptr<shared_base<R>>& p) : _p{p} {}
703
704 ~promise() {
705 if (auto p = _p.lock()) {
706 p->_set_exception(
707 std::make_exception_ptr(future_error(future_error_codes::broken_promise)));
708 }
709 }
710
711 promise(promise&&) noexcept = default;
712 auto operator=(promise&&) noexcept -> promise& = default;
713
714 promise(const promise&) = delete;
715 auto operator=(const promise&) -> promise& = delete;
716
719 void set_value(type&& value) && noexcept {
720 if (auto p = _p.lock()) {
721 _p.reset();
722 p->set_value(std::move(value));
723 }
724 }
725
727 auto set_value() && noexcept { set_value(std::monostate{}); }
728
730 void set_exception(const std::exception_ptr& error) && noexcept {
731 if (auto p = _p.lock()) {
732 _p.reset();
733 p->_set_exception(error);
734 }
735 }
736
738 [[nodiscard]] auto canceled() const -> bool { return _p.expired(); }
739};
740
742template <class R>
743promise(std::shared_ptr<shared_base<R>>) -> promise<R>;
744
745template <class F, class R, class... Args>
746struct shared<F, R(Args...)> final : shared_base<R>, shared_task<Args...> {
747 std::optional<F> _f;
748
749 shared(executor_t s, F&& f) : shared_base<R>(std::move(s)), _f(std::move(f)) {}
750
751 void operator()(Args... args) noexcept override {
752 std::move (*_f)(promise{this->shared_from_this()}, std::move(args)...);
753 // After invoking `_f`, it is not destructed because it could be satisfying the promise
754 // asynchronously. `_f` is responsible for any cleanup prior to the future being released
755 // and must be aware that it may be destructed at any time after this point.
756 }
757
758 void set_exception(const std::exception_ptr& error) noexcept override {
759 shared_base<R>::_set_exception(error);
760 _f.reset(); // _f will not be invoked, release it and any resources it holds
761 }
762};
763
764template <class Sig, class F>
765auto make_shared_state(executor_t s, F&& f) -> std::shared_ptr<shared<F, Sig>> {
766 return std::make_shared<shared<F, Sig>>(std::move(s), std::forward<F>(f));
767}
768
769template <class... Args>
770[[nodiscard]] auto weak_state(const packaged_task<Args...>& p)
771 -> std::weak_ptr<shared_task<Args...>>;
772
773/**************************************************************************************************/
774
775} // namespace detail
776
777/**************************************************************************************************/
778
781template <class... Args>
782class packaged_task {
783 using ptr_t = std::weak_ptr<detail::shared_task<Args...>>;
784 ptr_t _p;
785
786 explicit packaged_task(ptr_t&& p) : _p(std::move(p)) {}
787
788 template <class Sig, class E, class F>
789 friend auto package(E, F&&)
790 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
791
792 template <class T, class E>
793 friend auto future_with_broken_promise(E) -> detail::reduced_t<T>;
794
795 template <class... Brgs>
796 friend auto detail::weak_state(const packaged_task<Brgs...>& p)
797 -> std::weak_ptr<detail::shared_task<Brgs...>>;
798
799public:
800 packaged_task() = default;
801 ~packaged_task() {
802 if (auto p = _p.lock()) {
803 p->set_exception(
804 std::make_exception_ptr(future_error(future_error_codes::broken_promise)));
805 }
806 }
807
808 packaged_task(const packaged_task&) = delete;
809 auto operator=(const packaged_task&) -> packaged_task& = delete;
810
811 packaged_task(packaged_task&&) noexcept = default;
812 auto operator=(packaged_task&& x) noexcept -> packaged_task& = default;
813
817 template <class... A>
818 void operator()(A&&... args) noexcept {
819 if (auto p = _p.lock()) {
820 p->_co_handle = nullptr;
821 _p.reset();
822 (*p)(std::forward<A>(args)...);
823 }
824 }
825
827 [[nodiscard]] auto canceled() const -> bool { return _p.expired(); }
828
830 void set_exception(const std::exception_ptr& error) noexcept {
831 if (auto p = _p.lock()) {
832 p->_co_handle = nullptr;
833 _p.reset();
834 p->set_exception(error);
835 }
836 }
837};
838
839namespace detail {
840
841template <class... Args>
842[[nodiscard]] auto weak_state(const packaged_task<Args...>& p)
843 -> std::weak_ptr<detail::shared_task<Args...>> {
844 return p._p;
845}
846
847} // namespace detail
848
849/**************************************************************************************************/
850
858template <class T>
859class STLAB_NODISCARD() future<T, enable_if_copyable<void_to_monostate_t<T>>> {
860 using type = void_to_monostate_t<T>;
861 using ptr_t = std::shared_ptr<detail::shared_base<T>>;
862 ptr_t _p;
863
864 explicit future(ptr_t p) : _p(std::move(p)) {}
865
866 template <class Sig, class E, class F>
867 friend auto package(E, F&&)
868 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
869
870 template <class U, class E>
871 friend auto future_with_broken_promise(E) -> detail::reduced_t<U>;
872
874 friend struct detail::shared_base<type>;
875 template <class, class>
876 friend struct detail::value_;
878
879public:
881 using result_type = T;
882
883 future() = default;
884 future(const future&) = default;
885 future(future&&) noexcept = default;
886 auto operator=(const future&) -> future& = default;
887 auto operator=(future&&) noexcept -> future& = default;
888
890 void swap(future& x) noexcept { std::swap(_p, x._p); }
891
893 inline friend void swap(future& x, future& y) noexcept { x.swap(y); }
894
896
897 inline friend auto operator==(const future& x, const future& y) -> bool { return x._p == y._p; }
898 inline friend auto operator!=(const future& x, const future& y) -> bool { return !(x == y); }
900
902 [[nodiscard]] auto valid() const -> bool { return static_cast<bool>(_p); }
903
906 template <class F>
907 auto then(F&& f) const& {
908 return recover([_f = std::forward<F>(f)](future<result_type>&& p) mutable {
910 std::move(_f),
911 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
912 });
913 }
914
920
922 template <class F>
923 auto operator|(F&& f) const& {
924 return then(std::forward<F>(f));
925 }
926
929 template <class E, class F>
930 auto then(E&& executor, F&& f) const& {
931 return recover(
932 std::forward<E>(executor), [_f = std::forward<F>(f)](future<result_type>&& p) mutable {
934 std::move(_f),
935 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
936 });
937 }
938
940 template <class F>
942 return then(std::move(etp)._executor, std::move(etp)._f);
943 }
944
947 template <class F>
948 auto then(F&& f) && {
949 return std::move(*this).recover([_f = std::forward<F>(f)](future<result_type>&& p) mutable {
950 return invoke_remove_monostate_arguments(
951 std::move(_f),
952 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
953 });
954 }
955
957 template <class F>
958 auto operator|(F&& f) && {
959 return std::move(*this).then(std::forward<F>(f));
960 }
961
964 template <class E, class F>
965 auto then(E&& executor, F&& f) && {
966 return std::move(*this).recover(
967 std::forward<E>(executor), [_f = std::forward<F>(f)](future<result_type>&& p) mutable {
968 return invoke_remove_monostate_arguments(
969 std::move(_f),
970 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
971 });
972 }
973
976 template <class F>
978 return std::move(*this).then(std::move(etp)._executor, std::move(etp)._f);
979 }
980
983 template <class F>
984 auto recover(F&& f) const& {
985 return _p->recover(copy(*this), std::forward<F>(f));
986 }
987
989 template <class F>
990 auto operator^(F&& f) const& {
991 return recover(std::forward<F>(f));
992 }
993
995 template <class E, class F>
996 auto recover(E&& executor, F&& f) const& {
997 return _p->recover(copy(*this), std::forward<E>(executor), std::forward<F>(f));
998 }
999
1001 template <class F>
1003 return recover(std::move(etp)._executor, std::move(etp)._f);
1004 }
1005
1008 template <class F>
1009 auto recover(F&& f) && {
1010 auto& self = *_p.get();
1011 return self.recover(std::move(*this), std::forward<F>(f));
1012 }
1013
1015 template <class F>
1016 auto operator^(F&& f) && {
1017 return std::move(*this).recover(std::forward<F>(f));
1018 }
1019
1022 template <class E, class F>
1023 auto recover(E&& executor, F&& f) && {
1024 auto& self = *_p.get();
1025 return self.recover(std::move(*this), std::forward<E>(executor), std::forward<F>(f));
1026 }
1027
1030 template <class F>
1032 return std::move(*this).recover(std::move(etp)._executor, std::move(etp)._f);
1033 }
1034
1036 void detach() const { _p->_detach(); }
1037
1040 template <class F>
1041 void detach(F&& f) && {
1042 auto& self = *_p.get();
1043 self._detach(std::move(*this), std::forward<F>(f));
1044 }
1045
1048 template <class F>
1049 void on_completion(F&& f) {
1050 _p->_on_completion(std::forward<F>(f));
1051 }
1052
1055 template <class E, class F>
1056 void on_completion(E&& executor, F&& f) {
1057 _p->_on_completion(std::forward<E>(executor), std::forward<F>(f));
1058 }
1059
1061 void reset() noexcept { _p.reset(); }
1062
1064 [[nodiscard]] auto is_ready() const& -> bool { return _p && _p->is_ready(); }
1065
1068 [[nodiscard]] auto get_try() const& { return optional_monostate_to_bool(_p->get_try()); }
1069
1071 auto get_try() && { return optional_monostate_to_bool(_p->get_try_r(unique_usage(_p))); }
1072
1074 [[nodiscard]] auto get_ready() const& { return monostate_to_void(_p->get_ready()); }
1075
1077 auto get_ready() && { return monostate_to_void(_p->get_ready_r(unique_usage(_p))); }
1078
1081 [[deprecated("Use exception() instead")]] [[nodiscard]] auto error()
1082 const& -> std::optional<std::exception_ptr> {
1083 return _p->_exception ? std::optional<std::exception_ptr>{_p->_exception} : std::nullopt;
1084 }
1085
1088 [[nodiscard]] auto exception() const& -> std::exception_ptr {
1089 assert(is_ready());
1090 return _p->_exception;
1091 }
1092};
1093
1094/**************************************************************************************************/
1095
1098template <class T>
1099class STLAB_NODISCARD() future<T, enable_if_not_copyable<void_to_monostate_t<T>>> {
1100 using ptr_t = std::shared_ptr<detail::shared_base<T>>;
1101 ptr_t _p;
1102
1103 explicit future(ptr_t p) : _p(std::move(p)) {}
1104
1105 template <class Sig, class E, class F>
1106 friend auto package(E, F&&)
1107 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
1108
1109 template <class U, class E>
1110 friend auto future_with_broken_promise(E) -> detail::reduced_t<U>;
1111
1113 friend struct detail::shared_base<T>;
1114 template <class, class>
1115 friend struct detail::value_;
1117
1118public:
1120 using result_type = T;
1121
1122 future() = default;
1123 future(const future&) = delete;
1124 future(future&&) noexcept = default;
1125 auto operator=(const future&) -> future& = delete;
1126 auto operator=(future&&) noexcept -> future& = default;
1127
1129 void swap(future& x) noexcept { std::swap(_p, x._p); }
1130
1132 inline friend void swap(future& x, future& y) noexcept { x.swap(y); }
1133
1135
1136 inline friend auto operator==(const future& x, const future& y) -> bool { return x._p == y._p; }
1137 inline friend auto operator!=(const future& x, const future& y) -> bool { return !(x == y); }
1139
1141 [[nodiscard]] auto valid() const -> bool { return static_cast<bool>(_p); }
1142
1145 template <class F>
1146 auto then(F&& f) && {
1147 return std::move(*this).recover([_f = std::forward<F>(f)](future<result_type>&& p) mutable {
1148 return std::move(_f)(*std::move(p).get_try());
1149 });
1150 }
1151
1155
1157 template <class F>
1158 auto operator|(F&& f) && {
1159 return std::move(*this).then(std::forward<F>(f));
1160 }
1161
1164 template <class E, class F>
1165 auto then(E&& executor, F&& f) && {
1166 return std::move(*this).recover(std::forward<E>(executor),
1167 [_f = std::forward<F>(f)](future<result_type>&& p) mutable {
1168 return std::move(_f)(*std::move(p).get_try());
1169 });
1170 }
1171
1173 template <class F>
1175 return std::move(*this).then(std::move(etp)._executor, std::move(etp)._f);
1176 }
1177
1180 template <class F>
1181 auto recover(F&& f) && {
1182 auto& self = *_p.get();
1183 return self.recover(std::move(*this), std::forward<F>(f));
1184 }
1185
1187 template <class F>
1188 auto operator^(F&& f) && {
1189 return std::move(*this).recover(std::forward<F>(f));
1190 }
1191
1193 template <class E, class F>
1194 auto recover(E&& executor, F&& f) && {
1195 auto& self = *_p.get();
1196 return self.recover(std::move(*this), std::forward<E>(executor), std::forward<F>(f));
1197 }
1198
1200 template <class F>
1202 return std::move(*this).recover(std::move(etp)._executor, std::move(etp)._f);
1203 }
1204
1206 void detach() const { _p->_detach(); }
1207
1210 template <class F>
1211 void detach(F&& f) && {
1212 auto& self = *_p.get();
1213 self._detach(std::move(*this), std::forward<F>(f));
1214 }
1215
1219 template <class F>
1220 void on_completion(F&& f) {
1221 _p->_on_completion(std::forward<F>(f));
1222 }
1223
1227 template <class E, class F>
1228 void on_completion(E&& executor, F&& f) {
1229 _p->_on_completion(std::forward<E>(executor), std::forward<F>(f));
1230 }
1231
1233 void reset() noexcept { _p.reset(); }
1234
1236 [[nodiscard]] auto is_ready() const& -> bool { return _p && _p->is_ready(); }
1237
1240 [[nodiscard]] auto get_try() const& { return optional_monostate_to_bool(_p->get_try()); }
1241
1243 auto get_try() && { return optional_monostate_to_bool(_p->get_try_r(unique_usage(_p))); }
1244
1246 [[nodiscard]] auto get_ready() const& { return monostate_to_void(_p->get_ready()); }
1247
1249 auto get_ready() && { return monostate_to_void(_p->get_ready_r(unique_usage(_p))); }
1250
1253 [[deprecated("Use exception() instead")]] [[nodiscard]] auto error()
1254 const& -> std::optional<std::exception_ptr> {
1255 return _p->_exception ? std::optional<std::exception_ptr>{_p->_exception} : std::nullopt;
1256 }
1257
1260 [[nodiscard]] auto exception() const& -> std::exception_ptr {
1261 assert(is_ready());
1262 return _p->_exception;
1263 }
1264};
1265
1268template <class Sig, class E, class F>
1269auto package(E executor, F&& f)
1270 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>> {
1271 if constexpr (std::is_same_v<E, executor_t>) {
1272 assert(executor && "FATAL (sean.parent) : executor is null!");
1273 }
1274
1275 using result_t = detail::result_of_t_<Sig>;
1276
1277 if constexpr (detail::is_future_v<result_t>) {
1278 auto p = detail::make_shared_state<detail::reduced_signature_t<Sig>>(
1279 std::move(executor),
1280 [_f = std::make_optional(std::forward<F>(f)),
1281 _hold = future<void>{}](auto&& promise, auto&&... args) mutable noexcept {
1282 assert(_f && "packaged task invoked twice");
1283 try {
1284 auto r = std::move(*_f)(std::forward<decltype(args)>(args)...);
1285 try {
1286 _hold = std::move(r).recover(
1288 [_p = std::move(promise)](result_t&& f) mutable noexcept {
1289 if (auto e = f.exception()) {
1290 std::move(_p).set_exception(std::move(e));
1291 } else {
1292 std::move(_p).set_value(invoke_void_to_monostate_result(
1293 [&] { return std::move(f).get_ready(); }));
1294 }
1295 });
1296 } catch (...) {
1297 /* NOTE: an exception here is reported as a broken promise. Ideally recover
1298 * would be passed the initial promise (it flows through the chain), but the
1299 * API isn't there yet. */
1300 }
1301 } catch (...) {
1302 std::move(promise).set_exception(std::current_exception());
1303 }
1304 _f.reset();
1305 });
1306 return {detail::packaged_task_from_signature_t<Sig>{p}, result_t{p}};
1307 } else {
1308 auto p = detail::make_shared_state<Sig>(
1309 std::move(executor), [_f = std::make_optional(std::forward<F>(f))](
1310 auto&& promise, auto&&... args) mutable noexcept {
1311 assert(_f && "packaged task invoked twice");
1312 try {
1314 std::move(*_f), std::forward<decltype(args)>(args)...);
1315 std::move(promise).set_value(std::move(tmp)); // noexcept
1316 } catch (...) {
1317 std::move(promise).set_exception(std::current_exception());
1318 }
1319 _f.reset();
1320 });
1321 return {detail::packaged_task_from_signature_t<Sig>{p}, future<result_t>{p}};
1322 }
1323}
1324
1327template <class T, class E>
1328auto future_with_broken_promise(E executor) -> detail::reduced_t<T> {
1329 auto p = std::make_shared<detail::shared_base<typename detail::reduced_t<T>::result_type>>(
1330 std::move(executor));
1331 p->_exception = std::make_exception_ptr(future_error(future_error_codes::broken_promise));
1332 p->_ready = true;
1333
1334 return detail::reduced_t<T>{p};
1335}
1336
1337/**************************************************************************************************/
1338
1339namespace detail {
1340
1341template <class F>
1342struct assign_ready_future {
1343 template <class T>
1344 static void assign(T& x, F&& f) {
1345 x = std::move(*(std::move(f).get_try()));
1346 }
1347};
1348
1349template <>
1350struct assign_ready_future<future<void>> {
1351 template <class T>
1352 static void assign(T& x, const future<void>&) {
1353 x = std::move(typename T::value_type()); // to set the optional
1354 }
1355};
1356
1357template <class F, class Args>
1358struct when_all_shared {
1359 // decay
1360 Args _args;
1361 std::mutex _guard;
1362 std::array<future<void>, std::tuple_size_v<Args>> _holds;
1363 std::size_t _remaining{std::tuple_size_v<Args>};
1364 std::exception_ptr _exception;
1365 packaged_task<> _f;
1366
1367 // require f is sink.
1368 template <std::size_t index, class FF>
1369 auto done(FF&& f) -> std::enable_if_t<!std::is_lvalue_reference_v<FF>> {
1370 auto run{false};
1371 {
1372 std::unique_lock<std::mutex> lock{_guard};
1373 if (!_exception) {
1374 assign_ready_future<FF>::assign(std::get<index>(_args), std::move(f));
1375 if (--_remaining == 0) run = true;
1376 }
1377 }
1378 if (run) _f();
1379 }
1380
1381 void failure(const std::exception_ptr& error) {
1382 auto run{false};
1383 {
1384 std::unique_lock<std::mutex> lock{_guard};
1385 if (!_exception) {
1386 for (auto& h : _holds)
1387 h.reset();
1388 _exception = error;
1389 run = true;
1390 }
1391 }
1392 if (run) _f();
1393 }
1394};
1395
1396template <size_t S, class R>
1397struct when_any_shared {
1398 using result_type = R;
1399 // decay
1400 std::optional<R> _arg;
1401 std::mutex _guard;
1402 std::array<future<void>, S> _holds;
1403 std::size_t _remaining{S};
1404 std::exception_ptr _exception;
1405 std::size_t _index = std::numeric_limits<std::size_t>::max();
1406 packaged_task<> _f;
1407
1408 void failure(const std::exception_ptr& error) {
1409 auto run{false};
1410 {
1411 std::unique_lock<std::mutex> lock{_guard};
1412 if (--_remaining == 0) {
1413 _exception = error;
1414 run = true;
1415 }
1416 }
1417 if (run) _f();
1418 }
1419
1420 template <size_t index, class FF>
1421 void done(FF&& f) {
1422 auto run{false};
1423 {
1424 std::unique_lock<std::mutex> lock{_guard};
1425 if (_index == std::numeric_limits<std::size_t>::max()) {
1426 _arg = std::move(*std::forward<FF>(f).get_try());
1427 _index = index;
1428 run = true;
1429 }
1430 }
1431 if (run) _f();
1432 }
1433
1434 template <class F>
1435 auto apply(F& f) {
1436 return f(std::move(*_arg), _index);
1437 }
1438};
1439
1440template <size_t S>
1441struct when_any_shared<S, void> {
1442 using result_type = void;
1443 // decay
1444 std::mutex _guard;
1445 std::array<future<void>, S> _holds;
1446 std::size_t _remaining{S};
1447 std::exception_ptr _exception;
1448 std::size_t _index = std::numeric_limits<std::size_t>::max();
1449 packaged_task<> _f;
1450
1451 void failure(const std::exception_ptr& error) {
1452 auto run{false};
1453 std::unique_lock<std::mutex> lock{_guard};
1454 {
1455 if (--_remaining == 0) {
1456 _exception = error;
1457 run = true;
1458 }
1459 }
1460 if (run) _f();
1461 }
1462
1463 template <size_t index, class FF>
1464 void done(FF&&) {
1465 auto run{false};
1466 {
1467 std::unique_lock<std::mutex> lock{_guard};
1468 if (_index == std::numeric_limits<std::size_t>::max()) {
1469 _index = index;
1470 run = true;
1471 }
1472 }
1473 if (run) _f();
1474 }
1475
1476 template <class F>
1477 auto apply(F& f) {
1478 return f(_index);
1479 }
1480};
1481
1482inline void rethrow_if_false(bool x, const std::exception_ptr& p) {
1483 if (!x) std::rethrow_exception(p);
1484}
1485
1486template <class F, class Args, class P, std::size_t... I>
1487auto apply_when_all_args_(F& f, Args& args, P& p, std::index_sequence<I...>) {
1488 (void)std::initializer_list<int>{
1489 (rethrow_if_false(static_cast<bool>(std::get<I>(args)), p->_exception), 0)...};
1490 return apply_optional_indexed<
1491 index_sequence_transform_t<std::make_index_sequence<std::tuple_size_v<Args>>,
1492 remove_placeholder<Args>::template function>>(f, args);
1493}
1494
1495template <class F, class P>
1496auto apply_when_all_args(F& f, P& p) {
1497 return apply_when_all_args_(f, p->_args, p,
1498 std::make_index_sequence<std::tuple_size_v<decltype(p->_args)>>());
1499}
1500
1501template <class F, class P>
1502auto apply_when_any_arg(F& f, P& p) {
1503 if (p->_exception) {
1504 std::rethrow_exception(p->_exception);
1505 }
1506
1507 return p->apply(f);
1508}
1509
1510template <std::size_t i, class E, class P, class T>
1511void attach_when_arg_(E&& executor, std::shared_ptr<P>& shared, T a) {
1512 auto holds =
1513 std::move(a).recover(std::forward<E>(executor), [_w = std::weak_ptr<P>(shared)](auto&& x) {
1514 auto p = _w.lock();
1515 if (!p) return;
1516
1517 if (auto ex = x.exception()) {
1518 p->failure(std::move(ex));
1519 } else {
1520 p->template done<i>(std::move(x));
1521 }
1522 });
1523 std::unique_lock<std::mutex> lock{shared->_guard};
1524 shared->_holds[i] = std::move(holds);
1525}
1526
1527template <class E, class P, class... Ts, std::size_t... I>
1528void attach_when_args_(std::index_sequence<I...>, E&& executor, std::shared_ptr<P>& p, Ts... a) {
1529 (void)std::initializer_list<int>{
1530 (attach_when_arg_<I>(std::forward<E>(executor), p, std::move(a)), 0)...};
1531}
1532
1533template <class E, class P, class... Ts>
1534void attach_when_args(E&& executor, std::shared_ptr<P>& p, Ts... a) {
1535 attach_when_args_(std::make_index_sequence<sizeof...(Ts)>(), std::forward<E>(executor), p,
1536 std::move(a)...);
1537}
1538
1539} // namespace detail
1540
1541/**************************************************************************************************/
1542
1549template <class E, class F, class... Ts>
1550auto when_all(const E& executor, F f, future<Ts>... args) {
1551 using vt_t = voidless_tuple<Ts...>;
1552 using opt_t = optional_placeholder_tuple<Ts...>;
1553 using result_t = decltype(apply_ignore_placeholders(std::declval<F>(), std::declval<vt_t>()));
1554
1555 auto shared = std::make_shared<detail::when_all_shared<F, opt_t>>();
1556 auto p = package<result_t()>(
1557 executor, [_f = std::move(f), _p = shared] { return detail::apply_when_all_args(_f, _p); });
1558 shared->_f = std::move(p.first);
1559
1560 detail::attach_when_args(executor, shared, std::move(args)...);
1561
1562 return std::move(p.second);
1563}
1564
1565/**************************************************************************************************/
1566
1568template <class T>
1570 template <class E, class F, class... Ts>
1571 static auto make(const E& executor, F f, future<T> arg, future<Ts>... args) {
1572 using result_t = detail::result_t<F, T, size_t>;
1573
1574 auto shared = std::make_shared<detail::when_any_shared<sizeof...(Ts) + 1, T>>();
1575 auto p = package<result_t()>(executor, [_f = std::move(f), _p = shared]() mutable {
1576 return detail::apply_when_any_arg(_f, _p);
1577 });
1578 shared->_f = std::move(p.first);
1579
1580 detail::attach_when_args(executor, shared, std::move(arg), std::move(args)...);
1581
1582 return std::move(p.second);
1583 }
1584};
1585
1586/**************************************************************************************************/
1587
1589template <>
1590struct make_when_any<void> {
1591 template <class E, class F, class... Ts>
1592 static auto make(E&& executor, F&& f, future<Ts>... args) {
1593 using result_t = detail::result_t<F, size_t>;
1594
1595 auto shared = std::make_shared<detail::when_any_shared<sizeof...(Ts), void>>();
1596 auto p = package<result_t()>(executor, [_f = std::forward<F>(f), _p = shared]() mutable {
1597 return detail::apply_when_any_arg(_f, _p);
1598 });
1599 shared->_f = std::move(p.first);
1600
1601 detail::attach_when_args(std::forward<E>(executor), shared, std::move(args)...);
1602
1603 return std::move(p.second);
1604 }
1605};
1606
1607/**************************************************************************************************/
1608
1615template <class E, class F, class T, class... Ts>
1616auto when_any(E&& executor, F&& f, future<T>&& arg, future<Ts>&&... args) {
1617 return make_when_any<T>::make(std::forward<E>(executor), std::forward<F>(f), std::move(arg),
1618 std::move(args)...);
1619}
1620
1621/**************************************************************************************************/
1622
1623namespace detail {
1624template <class T>
1625struct value_storer {
1626 template <class C, class F>
1627 static void store(C& context, F&& f, std::size_t index) {
1628 context._results = std::move(*std::forward<F>(f).get_try());
1629 context._index = index;
1630 }
1631};
1632
1633template <class T>
1634struct value_storer<std::vector<T>> {
1635 template <class C, class F>
1636 static void store(C& context, F&& f, std::size_t index) {
1637 context._results[index] = std::move(*std::forward<F>(f).get_try());
1638 }
1639};
1640
1641template <bool Indexed, class R>
1642struct result_creator;
1643
1644template <>
1645struct result_creator<true, void> {
1646 template <class C>
1647 static auto go(C& context) {
1648 return context._f(context._index);
1649 }
1650};
1651
1652template <>
1653struct result_creator<false, void> {
1654 template <class C>
1655 static auto go(C& context) {
1656 return context._f();
1657 }
1658};
1659
1660template <class R>
1661struct result_creator<true, R> {
1662 template <class C>
1663 static auto go(C& context) {
1664 return context._f(std::move(context._results), context._index);
1665 }
1666};
1667
1668template <class R>
1669struct result_creator<false, R> {
1670 template <class C>
1671 static auto go(C& context) {
1672 return context._f(std::move(context._results));
1673 }
1674};
1675
1676template <class F, bool Indexed, class R>
1677struct context_result {
1678 using result_type = R;
1679
1680 R _results;
1681 std::exception_ptr _exception;
1682 std::size_t _index{0};
1683 F _f;
1684
1685 context_result(F f, std::size_t s) : _f(std::move(f)) { init(_results, s); }
1686
1687 template <class T>
1688 void init(std::vector<T>& v, std::size_t s) {
1689 v.resize(s);
1690 }
1691
1692 template <class T>
1693 void init(T&, std::size_t) {}
1694
1695 template <class FF>
1696 void apply(FF&& f, std::size_t index) {
1697 value_storer<R>::store(*this, std::forward<FF>(f), index);
1698 }
1699
1700 void apply(const std::exception_ptr& error, std::size_t) { _exception = error; }
1701
1702 auto operator()() { return result_creator<Indexed, R>::go(*this); }
1703};
1704
1705template <class F, bool Indexed>
1706struct context_result<F, Indexed, void> {
1707 std::exception_ptr _exception;
1708 std::size_t _index{0};
1709 F _f;
1710
1711 context_result(F f, std::size_t) : _f(std::move(f)) {}
1712
1713 template <class FF>
1714 void apply(FF&&, std::size_t index) {
1715 _index = index;
1716 }
1717
1718 void apply(const std::exception_ptr& error, std::size_t) { _exception = error; }
1719
1720 auto operator()() { return result_creator<Indexed, void>::go(*this); }
1721};
1722
1723/**************************************************************************************************/
1724
1725/*
1726 * This specialization is used for cases when only one ready future is enough to move forward.
1727 * In case of when_any, the first successful future triggers the continuation. All others are
1728 * cancelled. In case of when_all, after the first error, this future cannot be fulfilled
1729 * anymore and so we cancel the all the others.
1730 */
1731struct single_trigger {
1732 template <class C, class F>
1733 static auto go(C& context, F&& f, size_t index) -> bool {
1734 auto run{false};
1735 {
1736 std::unique_lock<std::mutex> lock{context._guard};
1737 if (!context._single_event) {
1738 for (auto i = 0u; i < context._holds.size(); ++i) {
1739 if (i != index) context._holds[i].reset();
1740 }
1741 context._single_event = true;
1742 context.apply(std::forward<F>(f), index);
1743 run = true;
1744 }
1745 }
1746 return run;
1747 }
1748};
1749
1750/*
1751 * This specialization is used for cases when all futures must be fulfilled before the
1752 * continuation is triggered. In case of when_any it means, that the error case handling is
1753 * started, because all futures failed. In case of when_all it means, that after all futures
1754 * were fulfilled, the continuation is started.
1755 */
1756struct all_trigger {
1757 template <class C, class F>
1758 static auto go(C& context, F&& f, size_t index) -> bool {
1759 auto run{false};
1760 {
1761 std::unique_lock<std::mutex> lock{context._guard};
1762 context.apply(std::forward<F>(f), index);
1763 if (--context._remaining == 0) run = true;
1764 }
1765 return run;
1766 }
1767
1768 template <class C>
1769 static auto go(C& context, const std::exception_ptr& error, size_t index) -> bool {
1770 auto run{false};
1771 {
1772 std::unique_lock<std::mutex> lock{context._guard};
1773 if (--context._remaining == 0) {
1774 context.apply(error, index);
1775 run = true;
1776 }
1777 }
1778 return run;
1779 }
1780};
1781
1782template <class CR, class F, class ResultCollector, class FailureCollector>
1783struct common_context : CR {
1784 std::mutex _guard;
1785 std::size_t _remaining;
1786 bool _single_event{false};
1787 std::vector<future<void>> _holds;
1788 packaged_task<> _f;
1789
1790 common_context(F f, size_t s) : CR(std::move(f), s), _remaining(s), _holds(_remaining) {}
1791
1792 auto execute() {
1793 if (this->_exception) {
1794 std::rethrow_exception(this->_exception);
1795 }
1796 return CR::operator()();
1797 }
1798
1799 void failure(const std::exception_ptr& error, size_t index) {
1800 if (FailureCollector::go(*this, error, index)) _f();
1801 }
1802
1803 template <class FF>
1804 void done(FF&& f, size_t index) {
1805 if (ResultCollector::go(*this, std::forward<FF>(f), index)) _f();
1806 }
1807};
1808
1809/**************************************************************************************************/
1810
1811template <class C, class E, class T>
1812void attach_tasks(size_t index, E&& executor, const std::shared_ptr<C>& context, T&& a) {
1813 auto&& hold = std::forward<T>(a).recover(
1814 std::forward<E>(executor), [_context = make_weak_ptr(context), _i = index](const auto& x) {
1815 auto p = _context.lock();
1816 if (!p) return;
1817 if (auto ex = x.exception()) {
1818 p->failure(std::move(ex), _i);
1819 } else {
1820 p->done(std::move(x), _i);
1821 }
1822 });
1823
1824 std::unique_lock<std::mutex> guard(context->_guard);
1825 context->_holds[index] = std::move(hold);
1826}
1827
1828template <class R, class T, class C, class Enabled = void>
1829struct create_range_of_futures;
1830
1831template <class R, class T, class C>
1832struct create_range_of_futures<R, T, C, enable_if_copyable<T>> {
1833 template <class E, class F, class I>
1834 static auto do_it(const E& executor, F&& f, I first, I last) {
1835 assert(first != last);
1836
1837 auto context = std::make_shared<C>(std::forward<F>(f), std::distance(first, last));
1838 auto p = package<R()>(executor, [_c = context]() mutable { return _c->execute(); });
1839
1840 context->_f = std::move(p.first);
1841
1842 size_t index(0);
1843 for (; first != last; ++first) {
1844 attach_tasks(index++, executor, context, *first);
1845 }
1846
1847 return std::move(p.second);
1848 }
1849};
1850
1851template <class R, class T, class C>
1852struct create_range_of_futures<R, T, C, enable_if_not_copyable<T>> {
1853 template <class E, class F, class I>
1854 static auto do_it(const E& executor, F&& f, I first, I last) {
1855 assert(first != last);
1856
1857 auto context = std::make_shared<C>(std::forward<F>(f), std::distance(first, last));
1858 auto p = package<R()>(executor, [_c = context] { return _c->execute(); });
1859
1860 context->_f = std::move(p.first);
1861
1862 size_t index(0);
1863 for (; first != last; ++first) {
1864 attach_tasks(index++, executor, context, std::move(*first));
1865 }
1866
1867 return std::move(p.second);
1868 }
1869};
1870
1871/**************************************************************************************************/
1872
1873} // namespace detail
1874
1875/**************************************************************************************************/
1876
1884template <class E, class F, class I>
1885auto when_all(const E& executor, F f, std::pair<I, I> range) {
1886 using param_t = typename std::iterator_traits<I>::value_type::result_type;
1887 using result_t = typename detail::result_of_when_all_t<F, param_t>::result_type;
1888 using context_result_t =
1889 std::conditional_t<std::is_same_v<void, param_t>, void, std::vector<param_t>>;
1890 using context_t = detail::common_context<detail::context_result<F, false, context_result_t>, F,
1891 detail::all_trigger, detail::single_trigger>;
1892
1893 if (range.first == range.second) {
1894 auto p = package<result_t()>(
1895 executor, detail::context_result<F, false, context_result_t>(std::move(f), 0));
1896 executor(std::move(p.first));
1897 return std::move(p.second);
1898 }
1899
1900 return detail::create_range_of_futures<result_t, param_t, context_t>::do_it(
1901 executor, std::move(f), range.first, range.second);
1902}
1903
1904/**************************************************************************************************/
1905
1913template <class E, class F, class I>
1914auto when_any(const E& executor, F&& f, std::pair<I, I> range) {
1915 using param_t = typename std::iterator_traits<I>::value_type::result_type;
1916 using result_t = std::decay_t<typename detail::result_of_when_any_t<F, param_t>::result_type>;
1917 using context_result_t = std::conditional_t<std::is_same_v<void, param_t>, void, param_t>;
1918 using context_t = detail::common_context<detail::context_result<F, true, context_result_t>, F,
1919 detail::single_trigger, detail::all_trigger>;
1920
1921 if (range.first == range.second) {
1923 }
1924
1925 return detail::create_range_of_futures<result_t, param_t, context_t>::do_it(
1926 std::move(executor), std::forward<F>(f), range.first, range.second);
1927}
1928
1929/**************************************************************************************************/
1930
1936template <class E, class F, class... Args>
1937auto async(const E& executor, F&& f, Args&&... args)
1938 -> detail::reduced_t<detail::result_t<std::decay_t<F>, std::decay_t<Args>...>> {
1939 using result_type = detail::result_t<std::decay_t<F>, std::decay_t<Args>...>;
1940
1941 auto [pro, fut] = package<result_type()>(
1942 executor,
1943 [f = std::forward<F>(f), args = std::make_tuple(std::forward<Args>(args)...)]() mutable
1944 -> result_type { return std::apply(std::move(f), std::move(args)); });
1945
1946 executor(std::move(pro));
1947
1948 return std::move(fut);
1949}
1950
1951/**************************************************************************************************/
1952
1953namespace detail {
1954
1955/**************************************************************************************************/
1956
1957template <class T>
1958struct value_<T, enable_if_copyable<void_to_monostate_t<T>>> {
1959 template <class C>
1960 static void proceed(C& sb) {
1961 typename C::then_t then;
1962 {
1963 std::unique_lock<std::mutex> lock(sb._mutex);
1964 sb._ready = true;
1965 then = std::move(sb._then);
1966 }
1967 for (auto& e : then)
1968 e.first(std::move(e.second));
1969 }
1970
1971 template <class R, class A>
1972 static void set(shared_base<R>& sb, A&& a) {
1973 sb._result = std::forward<A>(a);
1974 proceed(sb);
1975 }
1976};
1977
1978template <class T>
1979struct value_<T, enable_if_not_copyable<void_to_monostate_t<T>>> {
1980 template <class C>
1981 static void proceed(C& sb) {
1982 typename C::then_t then;
1983 {
1984 std::unique_lock<std::mutex> lock(sb._mutex);
1985 sb._ready = true;
1986 then = std::move(sb._then);
1987 }
1988 if (then.first) then.first(std::move(then.second));
1989 }
1990
1991 template <class R, class A>
1992 static void set(shared_base<R>& sb, A&& a) {
1993 sb._result = std::forward<A>(a);
1994 proceed(sb);
1995 }
1996};
1997
1998/**************************************************************************************************/
1999
2000template <class T>
2001template <class A>
2002void shared_base<T, enable_if_copyable<void_to_monostate_t<T>>>::set_value(A&& a) {
2003 value_<T>::set(*this, std::forward<A>(a));
2004}
2005
2006template <class T>
2007template <class A>
2008void shared_base<T, enable_if_not_copyable<void_to_monostate_t<T>>>::set_value(A&& a) {
2009 value_<T>::set(*this, std::forward<A>(a));
2010}
2011
2012/**************************************************************************************************/
2013
2014} // namespace detail
2015
2016/**************************************************************************************************/
2017
2019
2020STLAB_VERSION_NAMESPACE_END()
2021} // namespace stlab
2022
2023/**************************************************************************************************/
2024
2025#if STLAB_STD_COROUTINES()
2026
2027/**************************************************************************************************/
2028
2029namespace stlab {
2030STLAB_VERSION_NAMESPACE_BEGIN()
2031
2032
2035
2036namespace detail {
2037
2038// --- Generic awaitable support for resume_on(executor, any_awaitable) ---
2039// C++ coroutine protocol: awaitable is (1) has member operator co_await, (2) has free
2040// operator co_await, or (3) is directly an awaiter (await_ready/await_suspend/await_resume).
2041template <class A, class = void>
2042struct has_member_co_await : std::false_type {};
2043template <class A>
2044struct has_member_co_await<A, std::void_t<decltype(std::declval<A>().operator co_await())>>
2045 : std::true_type {};
2046
2047template <class A, class = void>
2048struct has_free_co_await : std::false_type {};
2049template <class A>
2050struct has_free_co_await<A, std::void_t<decltype(operator co_await(std::declval<A>()))>>
2051 : std::true_type {};
2052
2053template <class A>
2054auto get_awaiter(A&& a) {
2055 if constexpr (has_member_co_await<A>::value) {
2056 return std::forward<A>(a).operator co_await();
2057 } else if constexpr (has_free_co_await<A>::value) {
2058 return operator co_await(std::forward<A>(a));
2059 } else {
2060 return std::forward<A>(a); // direct awaiter (e.g. std::suspend_always)
2061 }
2062}
2063
2064template <class A>
2065using awaiter_t = decltype(get_awaiter(std::declval<A&>()));
2066
2067template <class A>
2068using await_result_t = decltype(std::declval<awaiter_t<A>>().await_resume());
2069
2070template <class A>
2071using await_result_storage_t = void_to_monostate_t<await_result_t<A>>;
2072
2073// Fire-and-forget return type for the proxy coroutine. No handle is retained after launch;
2074// the proxy owns itself and self-destroys at final_suspend (suspend_never).
2075struct proxy_fire_and_forget {
2076 struct promise_type {
2077 proxy_fire_and_forget get_return_object() { return {}; }
2078 std::suspend_never initial_suspend() { return {}; }
2079 std::suspend_never final_suspend() noexcept { return {}; }
2080 void return_void() {}
2081 void unhandled_exception() { std::terminate(); }
2082 };
2083};
2084
2089template <class A, class Inner, class E, class F>
2090proxy_fire_and_forget resume_on_proxy_coro(E executor,
2091 F resume_cb,
2092 Inner inner,
2093 std::optional<await_result_storage_t<A>>* result_ptr,
2094 std::exception_ptr* exception_ptr) {
2095 try {
2096 if constexpr (std::is_void_v<await_result_t<A>>) {
2097 co_await std::move(inner);
2098 result_ptr->emplace(std::monostate{});
2099 } else {
2100 result_ptr->emplace(co_await std::move(inner));
2101 }
2102 } catch (...) {
2103 if (exception_ptr) *exception_ptr = std::current_exception();
2104 }
2105 executor(std::move(resume_cb));
2106}
2107
2109template <class A, class Inner, class E, class F, class WeakPtr>
2110proxy_fire_and_forget resume_on_proxy_coro_controlled(
2111 E executor,
2112 F resume_cb,
2113 Inner inner,
2114 WeakPtr weak,
2115 std::optional<await_result_storage_t<A>>* result_ptr,
2116 std::exception_ptr* exception_ptr) {
2117 // proceed with co_await; if weak_state is destroyed during suspension, the proxy is
2118 // responsible for not writing to awaiter storage and not invoking the resume callback.
2119 bool was_alive = false;
2120 try {
2121 if constexpr (std::is_void_v<await_result_t<A>>) {
2122 co_await std::move(inner);
2123 if (auto keepalive = weak.lock()) {
2124 (void)keepalive;
2125 result_ptr->emplace(std::monostate{});
2126 was_alive = true;
2127 }
2128 } else {
2129 auto tmp = co_await std::move(inner);
2130 if (auto keepalive = weak.lock()) {
2131 (void)keepalive;
2132 result_ptr->emplace(std::move(tmp));
2133 was_alive = true;
2134 }
2135 }
2136 } catch (...) {
2137 if (auto keepalive = weak.lock()) {
2138 (void)keepalive;
2139 if (exception_ptr) *exception_ptr = std::current_exception();
2140 was_alive = true;
2141 }
2142 }
2143 if (was_alive) executor(std::move(resume_cb));
2144}
2145
2146template <class... Args>
2147struct final_awaiter {
2148 bool await_ready() noexcept { return false; }
2149 void await_resume() noexcept {}
2150 void await_suspend(std::coroutine_handle<> h) noexcept { h.destroy(); }
2151};
2152
2155template <class R, class E>
2156struct resume_on_awaiter {
2157 E _executor;
2158 future<R> _input;
2159
2160 bool await_ready() const noexcept { return false; }
2161
2162 auto await_resume() { return std::move(_input).get_ready(); }
2163
2164 void await_suspend(std::coroutine_handle<> ch) {
2165 _input.on_completion(std::move(_executor), [ch]() noexcept { ch.resume(); });
2166 }
2167};
2168
2169template <class R, class E>
2170resume_on_awaiter(E, future<R>) -> resume_on_awaiter<R, std::decay_t<E>>;
2171
2176template <class R, class E, class WeakPtr, bool AllowSkipSuspend>
2177struct resume_on_awaiter_with_control {
2178 E _executor;
2179 future<R> _input;
2180 WeakPtr _weak;
2181
2182 bool await_ready() const noexcept {
2183 if constexpr (AllowSkipSuspend) {
2184 return _input.is_ready();
2185 } else {
2186 return false;
2187 }
2188 }
2189 auto await_resume() { return std::move(_input).get_ready(); }
2190 void await_suspend(std::coroutine_handle<> ch) {
2191 assert(_weak.lock() && "await_suspend: weak_state is gone");
2192 _weak.lock()->_co_handle = ch.address();
2193 _input.on_completion(std::move(_executor), [weak = std::move(_weak)]() noexcept {
2194 if (auto state = weak.lock())
2195 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2196 });
2197 }
2198};
2199
2202template <class E>
2203struct resume_on_executor_awaiter {
2204 E _executor;
2205
2206 bool await_ready() const noexcept { return false; }
2207 void await_resume() noexcept {}
2208 void await_suspend(std::coroutine_handle<> ch) {
2209 std::move(_executor)([ch]() noexcept { ch.resume(); });
2210 }
2211};
2212
2215template <class E, class WeakPtr>
2216struct resume_on_executor_awaiter_with_control {
2217 E _executor;
2218 WeakPtr _weak;
2219
2220 [[nodiscard]] bool await_ready() const noexcept { return false; }
2221 void await_resume() noexcept {}
2222 void await_suspend(std::coroutine_handle<> ch) {
2223 assert(_weak.lock() && "await_suspend: weak_state is gone");
2224 _weak.lock()->_co_handle = ch.address();
2225 std::move(_executor)([weak = std::move(_weak)]() noexcept {
2226 if (auto state = weak.lock())
2227 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2228 });
2229 }
2230};
2231
2234template <class E, class A>
2235struct resume_on_any_awaiter {
2236 E _executor;
2237 A _awaitable;
2238 std::optional<await_result_storage_t<A>> _result;
2239 std::exception_ptr _exception;
2240
2241 bool await_ready() const noexcept { return false; }
2242
2243 await_result_t<A> await_resume() {
2244 if (_exception) std::rethrow_exception(_exception);
2245 if constexpr (std::is_void_v<await_result_t<A>>) {
2246 return;
2247 } else {
2248 return std::move(*_result);
2249 }
2250 }
2251
2252 void await_suspend(std::coroutine_handle<> ch) {
2253 auto inner = get_awaiter(std::move(_awaitable));
2254 if (inner.await_ready()) {
2255 try {
2256 if constexpr (std::is_void_v<await_result_t<A>>) {
2257 inner.await_resume();
2258 _result.emplace(std::monostate{});
2259 } else {
2260 _result.emplace(inner.await_resume());
2261 }
2262 } catch (...) {
2263 _exception = std::current_exception();
2264 }
2265 std::move(_executor)([ch]() noexcept { ch.resume(); });
2266 return;
2267 }
2268 resume_on_proxy_coro<std::decay_t<A>>(
2269 std::move(_executor), [ch]() noexcept { ch.resume(); }, std::move(inner), &_result,
2270 &_exception);
2271 }
2272};
2273
2277template <class E, class A, class WeakPtr>
2278struct resume_on_any_awaiter_with_control {
2279 E _executor;
2280 A _awaitable;
2281 WeakPtr _weak;
2282 std::optional<await_result_storage_t<A>> _result;
2283 std::exception_ptr _exception;
2284
2285 bool await_ready() const noexcept { return false; }
2286
2287 await_result_t<A> await_resume() {
2288 if (_exception) std::rethrow_exception(_exception);
2289 if constexpr (std::is_void_v<await_result_t<A>>) {
2290 return;
2291 } else {
2292 return std::move(*_result);
2293 }
2294 }
2295
2296 void await_suspend(std::coroutine_handle<> ch) {
2297 assert(_weak.lock() && "await_suspend: weak_state is gone");
2298 _weak.lock()->_co_handle = ch.address();
2299 auto inner = get_awaiter(std::move(_awaitable));
2300 if (inner.await_ready()) {
2301 try {
2302 if constexpr (std::is_void_v<await_result_t<A>>) {
2303 inner.await_resume();
2304 _result.emplace(std::monostate{});
2305 } else {
2306 _result.emplace(inner.await_resume());
2307 }
2308 } catch (...) {
2309 _exception = std::current_exception();
2310 }
2311 std::move(_executor)([weak = _weak]() noexcept {
2312 if (auto state = weak.lock())
2313 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2314 });
2315 return;
2316 }
2317 resume_on_proxy_coro_controlled<std::decay_t<A>>(
2318 std::move(_executor),
2319 [weak = _weak]() noexcept {
2320 if (auto state = weak.lock())
2321 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2322 },
2323 std::move(inner), _weak, &_result, &_exception);
2324 }
2325};
2326
2330template <class A>
2331struct cancelable_requires_stlab_future_coroutine {
2332 A _awaitable;
2333};
2334
2335template <>
2336struct cancelable_requires_stlab_future_coroutine<void> {};
2337
2342template <class WeakPtr>
2343struct cancelable_unique_checkpoint_awaiter {
2344 WeakPtr _weak;
2345
2346 [[nodiscard]] bool await_ready() const noexcept { return !_weak.expired(); }
2347 void await_resume() noexcept {}
2348 void await_suspend(std::coroutine_handle<> ch) { ch.destroy(); }
2349};
2350
2351} // namespace detail
2352
2355template <class E, class R>
2356auto resume_on(E&& executor, future<R>&& f) -> detail::resume_on_awaiter<R, std::decay_t<E>> {
2357 return detail::resume_on_awaiter{std::forward<E>(executor), std::move(f)};
2358}
2359
2362template <class E, class A>
2363auto resume_on(E&& executor, A&& awaitable)
2364 -> detail::resume_on_any_awaiter<std::decay_t<E>, std::decay_t<A>> {
2365 return detail::resume_on_any_awaiter<std::decay_t<E>, std::decay_t<A>>{
2366 std::forward<E>(executor), std::forward<A>(awaitable), {}, {}};
2367}
2368
2371template <class E>
2372auto resume_on(E&& executor) -> detail::resume_on_executor_awaiter<std::decay_t<E>> {
2373 return detail::resume_on_executor_awaiter<std::decay_t<E>>{std::forward<E>(executor)};
2374}
2375
2379template <class A, std::enable_if_t<!detail::is_future_v<std::decay_t<A>>, int> = 0>
2380auto cancelable(A&& awaitable)
2381 -> detail::cancelable_requires_stlab_future_coroutine<std::decay_t<A>> {
2382 return {std::forward<A>(awaitable)};
2383}
2384
2385template <class R>
2386auto cancelable(future<R>&) // Use co_await std::move(f); it is already cancelable.
2387 -> future<R> = delete;
2388
2389template <class R>
2390auto cancelable(future<R>&&) // Use co_await std::move(f); it is already cancelable.
2391 -> future<R> = delete;
2392
2393template <class R>
2394auto cancelable(const future<R>&) // Use co_await std::move(f); it is already cancelable.
2395 -> future<R> = delete;
2396
2400[[nodiscard]] inline auto cancelable() -> detail::cancelable_requires_stlab_future_coroutine<void> {
2401 return {};
2402}
2403
2405
2406STLAB_VERSION_NAMESPACE_END()
2407} // namespace stlab
2408
2409/**************************************************************************************************/
2410// Coroutine ownership and destruction rules (implementation notes):
2411//
2412// We destroy the coroutine only while it is suspended, in final_awaiter::await_suspend
2413// (h.destroy()). Before invoking the packaged_task (in
2414// return_value/return_void/unhandled_exception), we clear _co_handle in the shared state so
2415// ~shared_task() will not try to destroy the handle when the future is later destroyed. When
2416// suspended on a future we store the handle in _co_handle so we can resume via weak_state. For
2417// non-future awaitables we clear _co_handle before suspending (give up ownership for that suspend).
2418// initial_suspend is suspend_never so we never own before the first suspend.
2419// `co_await cancelable()` with no arguments: if weak_state is expired (last future already
2420// released), await_suspend destroys the coroutine handle directly — not via _co_handle. If the weak
2421// is still valid, await_ready avoids suspension so execution continues.
2422/**************************************************************************************************/
2423
2427
2428template <class T, class... Args>
2429struct std::coroutine_traits<stlab::future<T>, Args...> { // NOLINT(cert-dcl58-cpp)
2430 struct promise_type {
2431 stlab::packaged_task<std::variant<T, std::exception_ptr>> _promise;
2432
2433 stlab::future<T> get_return_object() {
2434 auto [pro, fut] = stlab::package<T(std::variant<T, std::exception_ptr>)>(
2436 [](std::variant<T, std::exception_ptr>&& v) mutable -> T {
2437 // Use index (1 = exception) to avoid ambiguity when T is std::exception_ptr.
2438 if (auto* ep = std::get_if<1>(&v)) {
2439 std::rethrow_exception(*ep);
2440 }
2441 return std::get<0>(std::move(v));
2442 });
2443 _promise = std::move(pro);
2444 return std::move(fut);
2445 }
2446
2447 auto initial_suspend() const noexcept { return std::suspend_never{}; }
2448
2449 auto final_suspend() noexcept {
2450 assert(_promise.canceled() && "final_suspend: promise not fulfilled");
2451 return stlab::detail::final_awaiter<std::variant<T, std::exception_ptr>>{};
2452 }
2453
2454 template <class U>
2455 void return_value(U&& val) {
2456 _promise(
2457 std::variant<T, std::exception_ptr>{std::in_place_type<T>, std::forward<U>(val)});
2458 }
2459
2460 void unhandled_exception() {
2461 _promise(std::variant<T, std::exception_ptr>{std::in_place_type<std::exception_ptr>,
2462 std::current_exception()});
2463 }
2464
2465 template <class R>
2466 auto await_transform(stlab::future<R>&& f) {
2467 return stlab::detail::resume_on_awaiter_with_control<
2468 R, decltype(stlab::immediate_executor),
2469 decltype(stlab::detail::weak_state(_promise)), true>{
2470 stlab::immediate_executor, std::move(f), stlab::detail::weak_state(_promise)};
2471 }
2472 template <class R, class E>
2473 auto await_transform(stlab::detail::resume_on_awaiter<R, E> a) {
2474 return stlab::detail::resume_on_awaiter_with_control<
2475 R, E, decltype(stlab::detail::weak_state(_promise)), false>{
2476 std::move(a._executor), std::move(a._input), stlab::detail::weak_state(_promise)};
2477 }
2478 template <class E>
2479 auto await_transform(stlab::detail::resume_on_executor_awaiter<E> a) {
2480 return stlab::detail::resume_on_executor_awaiter_with_control<
2481 E, decltype(stlab::detail::weak_state(_promise))>{
2482 std::move(a._executor), stlab::detail::weak_state(_promise)};
2483 }
2484 template <class E, class A>
2485 auto await_transform(stlab::detail::resume_on_any_awaiter<E, A> a) {
2486 return stlab::detail::resume_on_any_awaiter_with_control<
2487 E, A, decltype(stlab::detail::weak_state(_promise))>{
2488 std::move(a._executor), std::move(a._awaitable),
2489 stlab::detail::weak_state(_promise), std::move(a._result), std::move(a._exception)};
2490 }
2491 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<void>) {
2492 return stlab::detail::cancelable_unique_checkpoint_awaiter<
2493 decltype(stlab::detail::weak_state(_promise))>{stlab::detail::weak_state(_promise)};
2494 }
2495 template <class A, std::enable_if_t<!std::is_void_v<A>, int> = 0>
2496 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<A> c) {
2497 return stlab::detail::resume_on_any_awaiter_with_control<
2498 decltype(stlab::immediate_executor), A,
2499 decltype(stlab::detail::weak_state(_promise))>{stlab::immediate_executor,
2500 std::move(c._awaitable),
2501 stlab::detail::weak_state(_promise),
2502 {},
2503 {}};
2504 }
2505 template <class U>
2506 U&& await_transform(U&& u) {
2507 if (auto state = stlab::detail::weak_state(_promise).lock())
2508 state->_co_handle = nullptr;
2509 return std::forward<U>(u);
2510 }
2511 };
2512};
2513
2514template <class... Args>
2515struct std::coroutine_traits<stlab::future<void>, Args...> {
2516 struct promise_type {
2517 stlab::packaged_task<std::exception_ptr> _promise;
2518
2519 stlab::future<void> get_return_object() {
2521 stlab::immediate_executor, [](const std::exception_ptr& ep) mutable {
2522 if (ep) std::rethrow_exception(ep);
2523 });
2524 _promise = std::move(pro);
2525 return std::move(fut);
2526 }
2527
2528 auto initial_suspend() const noexcept { return std::suspend_never{}; }
2529
2530 auto final_suspend() noexcept {
2531 assert(_promise.canceled() && "final_suspend: promise not fulfilled");
2532 return stlab::detail::final_awaiter<std::exception_ptr>{};
2533 }
2534
2535 void return_void() { _promise(std::exception_ptr{}); }
2536
2537 void unhandled_exception() { _promise(std::current_exception()); }
2538
2539 template <class R>
2540 auto await_transform(stlab::future<R>&& f) {
2541 return stlab::detail::resume_on_awaiter_with_control<
2542 R, decltype(stlab::immediate_executor),
2543 decltype(stlab::detail::weak_state(_promise)), true>{
2544 stlab::immediate_executor, std::move(f), stlab::detail::weak_state(_promise)};
2545 }
2546 template <class R, class E>
2547 auto await_transform(stlab::detail::resume_on_awaiter<R, E> a) {
2548 return stlab::detail::resume_on_awaiter_with_control<
2549 R, E, decltype(stlab::detail::weak_state(_promise)), false>{
2550 std::move(a._executor), std::move(a._input), stlab::detail::weak_state(_promise)};
2551 }
2552 template <class E>
2553 auto await_transform(stlab::detail::resume_on_executor_awaiter<E> a) {
2554 return stlab::detail::resume_on_executor_awaiter_with_control<
2555 E, decltype(stlab::detail::weak_state(_promise))>{
2556 std::move(a._executor), stlab::detail::weak_state(_promise)};
2557 }
2558 template <class E, class A>
2559 auto await_transform(stlab::detail::resume_on_any_awaiter<E, A> a) {
2560 return stlab::detail::resume_on_any_awaiter_with_control<
2561 E, A, decltype(stlab::detail::weak_state(_promise))>{
2562 std::move(a._executor), std::move(a._awaitable),
2563 stlab::detail::weak_state(_promise), std::move(a._result), std::move(a._exception)};
2564 }
2565 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<void>) {
2566 return stlab::detail::cancelable_unique_checkpoint_awaiter<
2567 decltype(stlab::detail::weak_state(_promise))>{stlab::detail::weak_state(_promise)};
2568 }
2569 template <class A, std::enable_if_t<!std::is_void_v<A>, int> = 0>
2570 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<A> c) {
2571 return stlab::detail::resume_on_any_awaiter_with_control<
2572 decltype(stlab::immediate_executor), A,
2573 decltype(stlab::detail::weak_state(_promise))>{stlab::immediate_executor,
2574 std::move(c._awaitable),
2575 stlab::detail::weak_state(_promise),
2576 {},
2577 {}};
2578 }
2579 template <class U>
2580 U&& await_transform(U&& u) {
2581 if (auto state = stlab::detail::weak_state(_promise).lock())
2582 state->_co_handle = nullptr;
2583 return std::forward<U>(u);
2584 }
2585 };
2586};
2587
2588/**************************************************************************************************/
2589
2590template <class R>
2591auto operator co_await(stlab::future<R>&& f) {
2592 return stlab::detail::resume_on_awaiter{stlab::immediate_executor, std::move(f)};
2593}
2594
2595// co_await on an lvalue future is deleted — use std::move(f) to make cancellation semantics
2596// explicit
2597template <class R>
2598auto operator co_await(stlab::future<R>& f) = delete;
2599
2601
2602#endif // STLAB_STD_COROUTINES()
2603
2604#endif
auto get_ready() &&
Same as get_ready() but may move the value when this is the only reference.
Definition future.hpp:1077
auto get_ready() const &
Returns the value.
Definition future.hpp:1074
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:1023
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:996
auto get_try() &&
Same as get_try() but may move the value when this is the only reference.
Definition future.hpp:1071
auto error() const &-> std::optional< std::exception_ptr >
Definition future.hpp:1081
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:1328
auto operator|(executor_task_pair< F > etp) const &
Pipe operator: same as then(etp.executor(), etp.task()).
Definition future.hpp:941
auto operator^(F &&f) &&
Pipe operator: same as recover(f). Rvalue overload; consumes *this.
Definition future.hpp:1016
void detach() const
Drops this future without requiring a value; the promise may see broken_promise.
Definition future.hpp:1036
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:1269
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:965
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:948
void on_completion(F &&f)
Invokes f immediately when this future completes (value or exception). Requires f is noexcept.
Definition future.hpp:1049
void swap(future &x) noexcept
Exchanges the shared state with x.
Definition future.hpp:890
auto operator^(F &&f) const &
Pipe operator: same as recover(f).
Definition future.hpp:990
void detach(F &&f) &&
When this future completes (value or exception), invokes f with it and does not propagate further.
Definition future.hpp:1041
auto operator|(executor_task_pair< F > etp) &&
Pipe operator: same as then(etp.executor(), etp.task()). Rvalue overload; consumes *this.
Definition future.hpp:977
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:898
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:907
friend void swap(future &x, future &y) noexcept
Exchanges the shared states of x and y.
Definition future.hpp:893
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:1068
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:1088
auto valid() const -> bool
True if this future has an associated shared state.
Definition future.hpp:902
void reset() noexcept
Releases the shared state; valid() becomes false.
Definition future.hpp:1061
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:930
auto operator|(F &&f) const &
Pipe operator: same as then(f).
Definition future.hpp:923
auto is_ready() const &-> bool
True if the result or exception is available.
Definition future.hpp:1064
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:1009
T result_type
The type of the value this future holds.
Definition future.hpp:881
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:1056
auto operator^(executor_task_pair< F > etp) const &
Pipe operator: same as recover(etp.executor(), etp.task()).
Definition future.hpp:1002
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:984
auto operator^(executor_task_pair< F > etp) &&
Pipe operator: same as recover(etp.executor(), etp.task()). Rvalue overload; consumes *this.
Definition future.hpp:1031
auto operator|(F &&f) &&
Pipe operator: same as then(f). Rvalue overload; consumes *this.
Definition future.hpp:958
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:897
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:1260
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:1194
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:1328
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:1269
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:1165
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:1146
auto valid() const -> bool
True if this future has an associated shared state.
Definition future.hpp:1141
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:1181
T result_type
The type of the value this future holds.
Definition future.hpp:1120
void reset() noexcept
Releases the shared state; valid() becomes false.
Definition future.hpp:1233
auto is_ready() const &-> bool
True if the result or exception is available.
Definition future.hpp:1236
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:1137
auto get_try() &&
Same as get_try() but may move the value when this is the only reference.
Definition future.hpp:1243
auto operator^(F &&f) &&
Pipe operator: same as recover(f).
Definition future.hpp:1188
auto get_ready() const &
Returns the value.
Definition future.hpp:1246
void detach(F &&f) &&
When this future completes (value or exception), invokes f with it and does not propagate further.
Definition future.hpp:1211
void swap(future &x) noexcept
Exchanges the shared state with x.
Definition future.hpp:1129
auto operator^(executor_task_pair< F > etp) &&
Pipe operator: same as recover(etp.executor(), etp.task()).
Definition future.hpp:1201
friend void swap(future &x, future &y) noexcept
Exchanges the shared states of x and y.
Definition future.hpp:1132
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:1228
void detach() const
Drops this future without requiring a value; the promise may see broken_promise.
Definition future.hpp:1206
auto operator|(executor_task_pair< F > etp) &&
Pipe operator: same as then(etp.executor(), etp.task()).
Definition future.hpp:1174
auto operator|(F &&f) &&
Pipe operator: same as then(f).
Definition future.hpp:1158
auto get_ready() &&
Same as get_ready() but may move the value when this is the only reference.
Definition future.hpp:1249
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:1240
auto error() const &-> std::optional< std::exception_ptr >
Definition future.hpp:1253
void on_completion(F &&f)
Invokes f immediately when this future completes (value or exception). This consumes the continuation...
Definition future.hpp:1220
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:1136
Exception thrown when a future-related contract is violated (e.g. broken_promise, no_state).
Definition future.hpp:225
auto what() const noexcept -> const char *override
Human-readable message for the stored error code.
Definition future.hpp:233
auto code() const noexcept -> const future_error_codes &
The error code that caused this exception.
Definition future.hpp:230
One-shot asynchronous result: holds a value or exception produced by a promise or packaged_task.
Definition future.hpp:300
Invocable that completes a future when called with arguments of type Args...; created by package().
Definition future.hpp:782
auto canceled() const -> bool
True if the associated future was released.
Definition future.hpp:827
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:1269
void set_exception(const std::exception_ptr &error) noexcept
Completes the future with error without invoking the callable.
Definition future.hpp:830
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:1328
void operator()(A &&... args) noexcept
Invokes the packaged callable with args and completes the associated future. Clears _co_handle before...
Definition future.hpp:818
Executor type aliases and scheduling helpers.
Reference unwrapping and related functional helpers.
std::function< void(stlab::task< void() noexcept >)> executor_t
Type-erased executor: accepts a void() noexcept task.
Definition executor_base.hpp:44
constexpr bool is_monostate_v
True if T is std::monostate.
Definition future.hpp:136
auto when_all(const E &executor, F f, future< Ts >... args)
Returns a future that completes when all input futures are ready; f receives their values.
Definition future.hpp:1550
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:1328
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:1937
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:161
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:115
future_error_codes
Error codes for future_error.
Definition future.hpp:184
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:1269
auto when_any(E &&executor, F &&f, future< T > &&arg, future< Ts > &&... args)
Returns a future that completes when any of the given futures is ready; f receives the value and the ...
Definition future.hpp:1616
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:140
auto monostate_to_void(T &&a)
Converts std::monostate to void (no return); forwards other types unchanged.
Definition future.hpp:150
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:171
typename void_to_monostate< T >::type void_to_monostate_t
Alias for void_to_monostate<T>::type.
Definition future.hpp:132
@ broken_promise
Promise was destroyed without setting a value or exception.
Definition future.hpp:185
@ no_state
Operation required a valid shared state.
Definition future.hpp:186
constexpr auto immediate_executor
Invokes work inline on the calling thread (synchronous executor).
Definition immediate_executor.hpp:52
typename noexcept_deducer< task_, F >::type task
task_ with noexcept deduced from the function type F (e.g. void() vs void() noexcept).
Definition task.hpp:324
auto make_weak_ptr(const std::shared_ptr< T > &x)
Returns a std::weak_ptr<T> sharing ownership with x.
Definition memory.hpp:35
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
Synchronous inline executor.
Small memory-related utilities (make_weak_ptr).
Definition reverse.hpp:28
Executor plus callable, produced by executor & f (used by futures and channels).
Definition executor_base.hpp:80
Wraps an executor_t for use with operator&.
Definition executor_base.hpp:74
Helper to implement when_any for result type T.
Definition future.hpp:1569
Maps void to std::monostate for uniform future result storage; other types unchanged.
Definition future.hpp:126
Move-only callable wrapper for executor scheduling (task<Signature>).
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.).