stlab 2.4.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
1370template <class F, class Args>
1371struct when_all_shared {
1372 // decay
1373 Args _args;
1374 std::mutex _guard;
1375 std::array<future<void>, std::tuple_size_v<Args>> _holds;
1376 std::size_t _remaining{std::tuple_size_v<Args>};
1377 std::exception_ptr _exception;
1378 packaged_task<> _f;
1379
1380 // require f is sink.
1381 template <std::size_t index, class FF>
1382 auto done(FF&& f) -> std::enable_if_t<!std::is_lvalue_reference_v<FF>> {
1383 auto run{false};
1384 {
1385 std::unique_lock<std::mutex> lock{_guard};
1386 if (!_exception) {
1387 assign_ready_future<FF>::assign(std::get<index>(_args), std::move(f));
1388 if (--_remaining == 0) run = true;
1389 }
1390 }
1391 if (run) _f();
1392 }
1393
1394 void failure(const std::exception_ptr& error) {
1395 auto run{false};
1396 {
1397 std::unique_lock<std::mutex> lock{_guard};
1398 if (!_exception) {
1399 for (auto& h : _holds)
1400 h.reset();
1401 _exception = error;
1402 run = true;
1403 }
1404 }
1405 if (run) _f();
1406 }
1407};
1408
1409template <size_t S, class R>
1410struct when_any_shared {
1411 using result_type = R;
1412 // decay
1413 std::optional<R> _arg;
1414 std::mutex _guard;
1415 std::array<future<void>, S> _holds;
1416 std::size_t _remaining{S};
1417 std::exception_ptr _exception;
1418 std::size_t _index = std::numeric_limits<std::size_t>::max();
1419 packaged_task<> _f;
1420
1421 void failure(const std::exception_ptr& error) {
1422 auto run{false};
1423 {
1424 std::unique_lock<std::mutex> lock{_guard};
1425 if (--_remaining == 0) {
1426 _exception = error;
1427 run = true;
1428 }
1429 }
1430 if (run) _f();
1431 }
1432
1433 template <size_t index, class FF>
1434 void done(FF&& f) {
1435 auto run{false};
1436 {
1437 std::unique_lock<std::mutex> lock{_guard};
1438 if (_index == std::numeric_limits<std::size_t>::max()) {
1439 assert(f.is_ready());
1440 _arg = std::forward<FF>(f).get_ready();
1441 _index = index;
1442 run = true;
1443 }
1444 }
1445 if (run) _f();
1446 }
1447
1448 template <class F>
1449 auto apply(F& f) {
1450 return f(std::move(*_arg), _index);
1451 }
1452};
1453
1454template <size_t S>
1455struct when_any_shared<S, void> {
1456 using result_type = void;
1457 // decay
1458 std::mutex _guard;
1459 std::array<future<void>, S> _holds;
1460 std::size_t _remaining{S};
1461 std::exception_ptr _exception;
1462 std::size_t _index = std::numeric_limits<std::size_t>::max();
1463 packaged_task<> _f;
1464
1465 void failure(const std::exception_ptr& error) {
1466 auto run{false};
1467 std::unique_lock<std::mutex> lock{_guard};
1468 {
1469 if (--_remaining == 0) {
1470 _exception = error;
1471 run = true;
1472 }
1473 }
1474 if (run) _f();
1475 }
1476
1477 template <size_t index, class FF>
1478 void done(FF&&) {
1479 auto run{false};
1480 {
1481 std::unique_lock<std::mutex> lock{_guard};
1482 if (_index == std::numeric_limits<std::size_t>::max()) {
1483 _index = index;
1484 run = true;
1485 }
1486 }
1487 if (run) _f();
1488 }
1489
1490 template <class F>
1491 auto apply(F& f) {
1492 return f(_index);
1493 }
1494};
1495
1496inline void rethrow_if_false(bool x, const std::exception_ptr& p) {
1497 if (!x) std::rethrow_exception(p);
1498}
1499
1500template <class F, class Args, class P, std::size_t... I>
1501auto apply_when_all_args_(F& f, Args& args, P& p, std::index_sequence<I...>) {
1502 (void)std::initializer_list<int>{
1503 (rethrow_if_false(static_cast<bool>(std::get<I>(args)), p->_exception), 0)...};
1504 return apply_optional_indexed<
1505 index_sequence_transform_t<std::make_index_sequence<std::tuple_size_v<Args>>,
1506 remove_placeholder<Args>::template function>>(f, args);
1507}
1508
1509template <class F, class P>
1510auto apply_when_all_args(F& f, P& p) {
1511 return apply_when_all_args_(f, p->_args, p,
1512 std::make_index_sequence<std::tuple_size_v<decltype(p->_args)>>());
1513}
1514
1515template <class F, class P>
1516auto apply_when_any_arg(F& f, P& p) {
1517 if (p->_exception) {
1518 std::rethrow_exception(p->_exception);
1519 }
1520
1521 return p->apply(f);
1522}
1523
1524template <std::size_t i, class E, class P, class T>
1525void attach_when_arg_(E&& executor, std::shared_ptr<P>& shared, T a) {
1526 auto holds =
1527 std::move(a).recover(std::forward<E>(executor), [_w = std::weak_ptr<P>(shared)](auto&& x) {
1528 auto p = _w.lock();
1529 if (!p) return;
1530
1531 if (auto ex = x.exception()) {
1532 p->failure(std::move(ex));
1533 } else {
1534 p->template done<i>(std::move(x));
1535 }
1536 });
1537 std::unique_lock<std::mutex> lock{shared->_guard};
1538 shared->_holds[i] = std::move(holds);
1539}
1540
1541template <class E, class P, class... Ts, std::size_t... I>
1542void attach_when_args_(std::index_sequence<I...>,
1543 const E& executor,
1544 std::shared_ptr<P>& p,
1545 Ts... a) {
1546 (void)std::initializer_list<int>{(attach_when_arg_<I>(executor, p, std::move(a)), 0)...};
1547}
1548
1549template <class E, class P, class... Ts>
1550void attach_when_args(E&& executor, std::shared_ptr<P>& p, Ts... a) {
1551 attach_when_args_(std::make_index_sequence<sizeof...(Ts)>(), std::forward<E>(executor), p,
1552 std::move(a)...);
1553}
1554
1555} // namespace detail
1556
1557/**************************************************************************************************/
1558
1565template <class E, class F, class... Ts>
1566auto when_all(const E& executor, F f, future<Ts>... args) {
1567 using vt_t = voidless_tuple<Ts...>;
1568 using opt_t = optional_placeholder_tuple<Ts...>;
1569 using result_t = decltype(apply_ignore_placeholders(std::declval<F>(), std::declval<vt_t>()));
1570
1571 auto shared = std::make_shared<detail::when_all_shared<F, opt_t>>();
1572 auto p = package<result_t()>(
1573 executor, [_f = std::move(f), _p = shared] { return detail::apply_when_all_args(_f, _p); });
1574 shared->_f = std::move(p.first);
1575
1576 detail::attach_when_args(executor, shared, std::move(args)...);
1577
1578 return std::move(p.second);
1579}
1580
1581/**************************************************************************************************/
1582
1584template <class T>
1586 template <class E, class F, class... Ts>
1587 static auto make(const E& executor, F f, future<T> arg, future<Ts>... args) {
1588 using result_t = detail::result_t<F, T, size_t>;
1589
1590 auto shared = std::make_shared<detail::when_any_shared<sizeof...(Ts) + 1, T>>();
1591 auto p = package<result_t()>(executor, [_f = std::move(f), _p = shared]() mutable {
1592 return detail::apply_when_any_arg(_f, _p);
1593 });
1594 shared->_f = std::move(p.first);
1595
1596 detail::attach_when_args(executor, shared, std::move(arg), std::move(args)...);
1597
1598 return std::move(p.second);
1599 }
1600};
1601
1602/**************************************************************************************************/
1603
1605template <>
1606struct make_when_any<void> {
1607 template <class E, class F, class... Ts>
1608 static auto make(E&& executor, F&& f, future<Ts>... args) {
1609 using result_t = detail::result_t<F, size_t>;
1610
1611 auto shared = std::make_shared<detail::when_any_shared<sizeof...(Ts), void>>();
1612 auto p = package<result_t()>(executor, [_f = std::forward<F>(f), _p = shared]() mutable {
1613 return detail::apply_when_any_arg(_f, _p);
1614 });
1615 shared->_f = std::move(p.first);
1616
1617 detail::attach_when_args(std::forward<E>(executor), shared, std::move(args)...);
1618
1619 return std::move(p.second);
1620 }
1621};
1622
1623/**************************************************************************************************/
1624
1633template <class E, class F, class T, class... Ts>
1634auto when_any(E&& executor, F&& f, future<T>&& arg, future<Ts>&&... args) {
1635 return make_when_any<T>::make(std::forward<E>(executor), std::forward<F>(f), std::move(arg),
1636 std::move(args)...);
1637}
1638
1639/**************************************************************************************************/
1640
1641namespace detail {
1642template <class T>
1643struct value_storer {
1644 template <class C, class F>
1645 static void store(C& context, F&& f, std::size_t index) {
1646 context._results = std::forward<F>(f).get_ready();
1647 context._index = index;
1648 }
1649};
1650
1651template <class T>
1652struct value_storer<std::vector<T>> {
1653 template <class C, class F>
1654 static void store(C& context, F&& f, std::size_t index) {
1655 context._results[index] = std::forward<F>(f).get_ready();
1656 }
1657};
1658
1659template <bool Indexed, class R>
1660struct result_creator;
1661
1662template <>
1663struct result_creator<true, void> {
1664 template <class C>
1665 static auto go(C& context) {
1666 return context._f(context._index);
1667 }
1668};
1669
1670template <>
1671struct result_creator<false, void> {
1672 template <class C>
1673 static auto go(C& context) {
1674 return context._f();
1675 }
1676};
1677
1678template <class R>
1679struct result_creator<true, R> {
1680 template <class C>
1681 static auto go(C& context) {
1682 return context._f(std::move(context._results), context._index);
1683 }
1684};
1685
1686template <class R>
1687struct result_creator<false, R> {
1688 template <class C>
1689 static auto go(C& context) {
1690 return context._f(std::move(context._results));
1691 }
1692};
1693
1694template <class F, bool Indexed, class R>
1695struct context_result {
1696 using result_type = R;
1697
1698 R _results;
1699 std::exception_ptr _exception;
1700 std::size_t _index{0};
1701 F _f;
1702
1703 context_result(F f, std::size_t s) : _f(std::move(f)) { init(_results, s); }
1704
1705 template <class T>
1706 void init(std::vector<T>& v, std::size_t s) {
1707 v.resize(s);
1708 }
1709
1710 template <class T>
1711 void init(T&, std::size_t) {}
1712
1713 template <class FF>
1714 void apply(FF&& f, std::size_t index) {
1715 value_storer<R>::store(*this, std::forward<FF>(f), index);
1716 }
1717
1718 void apply(const std::exception_ptr& error, std::size_t) { _exception = error; }
1719
1720 auto operator()() { return result_creator<Indexed, R>::go(*this); }
1721};
1722
1723template <class F, bool Indexed>
1724struct context_result<F, Indexed, void> {
1725 std::exception_ptr _exception;
1726 std::size_t _index{0};
1727 F _f;
1728
1729 context_result(F f, std::size_t) : _f(std::move(f)) {}
1730
1731 template <class FF>
1732 void apply(FF&&, std::size_t index) {
1733 _index = index;
1734 }
1735
1736 void apply(const std::exception_ptr& error, std::size_t) { _exception = error; }
1737
1738 auto operator()() { return result_creator<Indexed, void>::go(*this); }
1739};
1740
1741/**************************************************************************************************/
1742
1743/*
1744 * This specialization is used for cases when only one ready future is enough to move forward.
1745 * In case of when_any, the first successful future triggers the continuation. All others are
1746 * cancelled. In case of when_all, after the first error, this future cannot be fulfilled
1747 * anymore and so we cancel the all the others.
1748 */
1749struct single_trigger {
1750 template <class C, class F>
1751 static auto go(C& context, F&& f, size_t index) -> bool {
1752 auto run{false};
1753 {
1754 std::unique_lock<std::mutex> lock{context._guard};
1755 if (!context._single_event) {
1756 for (auto i = 0u; i < context._holds.size(); ++i) {
1757 if (i != index) context._holds[i].reset();
1758 }
1759 context._single_event = true;
1760 context.apply(std::forward<F>(f), index);
1761 run = true;
1762 }
1763 }
1764 return run;
1765 }
1766};
1767
1768/*
1769 * This specialization is used for cases when all futures must be fulfilled before the
1770 * continuation is triggered. In case of when_any it means, that the error case handling is
1771 * started, because all futures failed. In case of when_all it means, that after all futures
1772 * were fulfilled, the continuation is started.
1773 */
1774struct all_trigger {
1775 template <class C, class F>
1776 static auto go(C& context, F&& f, size_t index) -> bool {
1777 auto run{false};
1778 {
1779 std::unique_lock<std::mutex> lock{context._guard};
1780 context.apply(std::forward<F>(f), index);
1781 if (--context._remaining == 0) run = true;
1782 }
1783 return run;
1784 }
1785
1786 template <class C>
1787 static auto go(C& context, const std::exception_ptr& error, size_t index) -> bool {
1788 auto run{false};
1789 {
1790 std::unique_lock<std::mutex> lock{context._guard};
1791 if (--context._remaining == 0) {
1792 context.apply(error, index);
1793 run = true;
1794 }
1795 }
1796 return run;
1797 }
1798};
1799
1800template <class CR, class F, class ResultCollector, class FailureCollector>
1801struct common_context : CR {
1802 std::mutex _guard;
1803 std::size_t _remaining;
1804 bool _single_event{false};
1805 std::vector<future<void>> _holds;
1806 packaged_task<> _f;
1807
1808 common_context(F f, size_t s) : CR(std::move(f), s), _remaining(s), _holds(_remaining) {}
1809
1810 auto execute() {
1811 if (this->_exception) {
1812 std::rethrow_exception(this->_exception);
1813 }
1814 return CR::operator()();
1815 }
1816
1817 void failure(const std::exception_ptr& error, size_t index) {
1818 if (FailureCollector::go(*this, error, index)) _f();
1819 }
1820
1821 template <class FF>
1822 void done(FF&& f, size_t index) {
1823 if (ResultCollector::go(*this, std::forward<FF>(f), index)) _f();
1824 }
1825};
1826
1827/**************************************************************************************************/
1828
1829template <class C, class E, class T>
1830void attach_tasks(size_t index, E&& executor, const std::shared_ptr<C>& context, T&& a) {
1831 auto hold = std::forward<T>(a).recover(
1832 std::forward<E>(executor), [_context = make_weak_ptr(context), _i = index](auto&& x) {
1833 auto p = _context.lock();
1834 if (!p) return;
1835 if (auto ex = x.exception()) {
1836 p->failure(std::move(ex), _i);
1837 } else {
1838 p->done(std::forward<decltype(x)>(x), _i);
1839 }
1840 });
1841
1842 std::unique_lock<std::mutex> guard(context->_guard);
1843 context->_holds[index] = std::move(hold);
1844}
1845
1846template <class R, class T, class C, class Enabled = void>
1847struct create_range_of_futures;
1848
1849template <class R, class T, class C>
1850struct create_range_of_futures<R, T, C, enable_if_copyable<T>> {
1851 template <class E, class F, class I>
1852 static auto do_it(const E& executor, F&& f, I first, I last) {
1853 assert(first != last);
1854
1855 auto context = std::make_shared<C>(std::forward<F>(f), std::distance(first, last));
1856 auto p = package<R()>(executor, [_c = context]() mutable { return _c->execute(); });
1857
1858 context->_f = std::move(p.first);
1859
1860 size_t index(0);
1861 for (; first != last; ++first) {
1862 attach_tasks(index++, executor, context, *first);
1863 }
1864
1865 return std::move(p.second);
1866 }
1867};
1868
1869template <class R, class T, class C>
1870struct create_range_of_futures<R, T, C, enable_if_not_copyable<T>> {
1871 template <class E, class F, class I>
1872 static auto do_it(const E& executor, F&& f, I first, I last) {
1873 assert(first != last);
1874
1875 auto context = std::make_shared<C>(std::forward<F>(f), std::distance(first, last));
1876 auto p = package<R()>(executor, [_c = context] { return _c->execute(); });
1877
1878 context->_f = std::move(p.first);
1879
1880 size_t index(0);
1881 for (; first != last; ++first) {
1882 attach_tasks(index++, executor, context, std::move(*first));
1883 }
1884
1885 return std::move(p.second);
1886 }
1887};
1888
1889/**************************************************************************************************/
1890
1891} // namespace detail
1892
1893/**************************************************************************************************/
1894
1902template <class E, class F, class I>
1903auto when_all(const E& executor, F f, std::pair<I, I> range) {
1904 using param_t = typename std::iterator_traits<I>::value_type::result_type;
1905 using result_t = typename detail::result_of_when_all_t<F, param_t>::result_type;
1906 using context_result_t =
1907 std::conditional_t<std::is_same_v<void, param_t>, void, std::vector<param_t>>;
1908 using context_t = detail::common_context<detail::context_result<F, false, context_result_t>, F,
1909 detail::all_trigger, detail::single_trigger>;
1910
1911 if (range.first == range.second) {
1912 auto p = package<result_t()>(
1913 executor, detail::context_result<F, false, context_result_t>(std::move(f), 0));
1914 executor(std::move(p.first));
1915 return std::move(p.second);
1916 }
1917
1918 return detail::create_range_of_futures<result_t, param_t, context_t>::do_it(
1919 executor, std::move(f), range.first, range.second);
1920}
1921
1922/**************************************************************************************************/
1923
1931template <class E, class F, class I>
1932auto when_any(const E& executor, F&& f, std::pair<I, I> range) {
1933 using param_t = typename std::iterator_traits<I>::value_type::result_type;
1934 using result_t = std::decay_t<typename detail::result_of_when_any_t<F, param_t>::result_type>;
1935 using context_result_t = std::conditional_t<std::is_same_v<void, param_t>, void, param_t>;
1936 using context_t = detail::common_context<detail::context_result<F, true, context_result_t>, F,
1937 detail::single_trigger, detail::all_trigger>;
1938
1939 if (range.first == range.second) {
1941 }
1942
1943 return detail::create_range_of_futures<result_t, param_t, context_t>::do_it(
1944 std::move(executor), std::forward<F>(f), range.first, range.second);
1945}
1946
1947/**************************************************************************************************/
1948
1954template <class E, class F, class... Args>
1955auto async(const E& executor, F&& f, Args&&... args)
1956 -> detail::reduced_t<detail::result_t<std::decay_t<F>, std::decay_t<Args>...>> {
1957 using result_type = detail::result_t<std::decay_t<F>, std::decay_t<Args>...>;
1958
1959 auto [pro, fut] = package<result_type()>(
1960 executor,
1961 [f = std::forward<F>(f), args = std::make_tuple(std::forward<Args>(args)...)]() mutable
1962 -> result_type { return std::apply(std::move(f), std::move(args)); });
1963
1964 executor(std::move(pro));
1965
1966 return std::move(fut);
1967}
1968
1969/**************************************************************************************************/
1970
1971namespace detail {
1972
1973/**************************************************************************************************/
1974
1975template <class T>
1976struct value_<T, enable_if_copyable<void_to_monostate_t<T>>> {
1977 template <class C>
1978 static void proceed(C& sb) {
1979 typename C::then_t then;
1980 {
1981 std::unique_lock<std::mutex> lock(sb._mutex);
1982 sb._ready = true;
1983 then = std::move(sb._then);
1984 }
1985 for (auto& e : then)
1986 e.first(std::move(e.second));
1987 }
1988
1989 template <class R, class A>
1990 static void set(shared_base<R>& sb, A&& a) {
1991 sb._result = std::forward<A>(a);
1992 proceed(sb);
1993 }
1994};
1995
1996template <class T>
1997struct value_<T, enable_if_not_copyable<void_to_monostate_t<T>>> {
1998 template <class C>
1999 static void proceed(C& sb) {
2000 typename C::then_t then;
2001 {
2002 std::unique_lock<std::mutex> lock(sb._mutex);
2003 sb._ready = true;
2004 then = std::move(sb._then);
2005 }
2006 if (then.first) then.first(std::move(then.second));
2007 }
2008
2009 template <class R, class A>
2010 static void set(shared_base<R>& sb, A&& a) {
2011 sb._result = std::forward<A>(a);
2012 proceed(sb);
2013 }
2014};
2015
2016/**************************************************************************************************/
2017
2018template <class T>
2019template <class A>
2020void shared_base<T, enable_if_copyable<void_to_monostate_t<T>>>::set_value(A&& a) {
2021 value_<T>::set(*this, std::forward<A>(a));
2022}
2023
2024template <class T>
2025template <class A>
2026void shared_base<T, enable_if_not_copyable<void_to_monostate_t<T>>>::set_value(A&& a) {
2027 value_<T>::set(*this, std::forward<A>(a));
2028}
2029
2030/**************************************************************************************************/
2031
2032} // namespace detail
2033
2034/**************************************************************************************************/
2035
2037
2038STLAB_VERSION_NAMESPACE_END()
2039} // namespace stlab
2040
2041/**************************************************************************************************/
2042
2043#if STLAB_STD_COROUTINES()
2044
2045/**************************************************************************************************/
2046
2047namespace stlab {
2048STLAB_VERSION_NAMESPACE_BEGIN()
2049
2050
2053
2054namespace detail {
2055
2056// --- Generic awaitable support for resume_on(executor, any_awaitable) ---
2057// C++ coroutine protocol: awaitable is (1) has member operator co_await, (2) has free
2058// operator co_await, or (3) is directly an awaiter (await_ready/await_suspend/await_resume).
2059template <class A, class = void>
2060struct has_member_co_await : std::false_type {};
2061template <class A>
2062struct has_member_co_await<A, std::void_t<decltype(std::declval<A>().operator co_await())>>
2063 : std::true_type {};
2064
2065template <class A, class = void>
2066struct has_free_co_await : std::false_type {};
2067template <class A>
2068struct has_free_co_await<A, std::void_t<decltype(operator co_await(std::declval<A>()))>>
2069 : std::true_type {};
2070
2071template <class A>
2072auto get_awaiter(A&& a) {
2073 if constexpr (has_member_co_await<A>::value) {
2074 return std::forward<A>(a).operator co_await();
2075 } else if constexpr (has_free_co_await<A>::value) {
2076 return operator co_await(std::forward<A>(a));
2077 } else {
2078 return std::forward<A>(a); // direct awaiter (e.g. std::suspend_always)
2079 }
2080}
2081
2082template <class A>
2083using awaiter_t = decltype(get_awaiter(std::declval<A&>()));
2084
2085template <class A>
2086using await_result_t = decltype(std::declval<awaiter_t<A>>().await_resume());
2087
2088template <class A>
2089using await_result_storage_t = void_to_monostate_t<await_result_t<A>>;
2090
2091// Fire-and-forget return type for the proxy coroutine. No handle is retained after launch;
2092// the proxy owns itself and self-destroys at final_suspend (suspend_never).
2093struct proxy_fire_and_forget {
2094 struct promise_type {
2095 proxy_fire_and_forget get_return_object() { return {}; }
2096 std::suspend_never initial_suspend() { return {}; }
2097 std::suspend_never final_suspend() noexcept { return {}; }
2098 void return_void() {}
2099 void unhandled_exception() { std::terminate(); }
2100 };
2101};
2102
2108template <class A, class Inner, class E, class F>
2109proxy_fire_and_forget resume_on_proxy_coro(E executor,
2110 F resume_cb,
2111 Inner inner,
2112 std::optional<await_result_storage_t<A>>* result_ptr,
2113 std::exception_ptr* exception_ptr) {
2114 try {
2115 if constexpr (std::is_void_v<await_result_t<A>>) {
2116 co_await std::move(inner);
2117 result_ptr->emplace(std::monostate{});
2118 } else {
2119 result_ptr->emplace(co_await std::move(inner));
2120 }
2121 } catch (...) {
2122 if (exception_ptr) *exception_ptr = std::current_exception();
2123 }
2124 executor(std::move(resume_cb));
2125}
2126
2129template <class A, class Inner, class E, class F, class WeakPtr>
2130proxy_fire_and_forget resume_on_proxy_coro_controlled(
2131 E executor,
2132 F resume_cb,
2133 Inner inner,
2134 WeakPtr weak,
2135 std::optional<await_result_storage_t<A>>* result_ptr,
2136 std::exception_ptr* exception_ptr) {
2137 // proceed with co_await; if weak_state is destroyed during suspension, the proxy is
2138 // responsible for not writing to awaiter storage and not invoking the resume callback.
2139 bool was_alive = false;
2140 try {
2141 if constexpr (std::is_void_v<await_result_t<A>>) {
2142 co_await std::move(inner);
2143 if (auto keepalive = weak.lock()) {
2144 (void)keepalive;
2145 result_ptr->emplace(std::monostate{});
2146 was_alive = true;
2147 }
2148 } else {
2149 auto tmp = co_await std::move(inner);
2150 if (auto keepalive = weak.lock()) {
2151 (void)keepalive;
2152 result_ptr->emplace(std::move(tmp));
2153 was_alive = true;
2154 }
2155 }
2156 } catch (...) {
2157 if (auto keepalive = weak.lock()) {
2158 (void)keepalive;
2159 if (exception_ptr) *exception_ptr = std::current_exception();
2160 was_alive = true;
2161 }
2162 }
2163 if (was_alive) executor(std::move(resume_cb));
2164}
2165
2166template <class... Args>
2167struct final_awaiter {
2168 bool await_ready() noexcept { return false; }
2169 void await_resume() noexcept {}
2170 void await_suspend(std::coroutine_handle<> h) noexcept { h.destroy(); }
2171};
2172
2175template <class R, class E>
2176struct resume_on_awaiter {
2177 E _executor;
2178 future<R> _input;
2179
2180 bool await_ready() const noexcept { return false; }
2181
2182 auto await_resume() { return std::move(_input).get_ready(); }
2183
2184 void await_suspend(std::coroutine_handle<> ch) {
2185 _input.on_completion(std::move(_executor), [ch]() noexcept { ch.resume(); });
2186 }
2187};
2188
2189template <class R, class E>
2190resume_on_awaiter(E, future<R>) -> resume_on_awaiter<R, std::decay_t<E>>;
2191
2196template <class R, class E, class WeakPtr, bool AllowSkipSuspend>
2197struct resume_on_awaiter_with_control {
2198 E _executor;
2199 future<R> _input;
2200 WeakPtr _weak;
2201
2202 bool await_ready() const noexcept {
2203 if constexpr (AllowSkipSuspend) {
2204 return _input.is_ready();
2205 } else {
2206 return false;
2207 }
2208 }
2209 auto await_resume() { return std::move(_input).get_ready(); }
2210 void await_suspend(std::coroutine_handle<> ch) {
2211 assert(_weak.lock() && "await_suspend: weak_state is gone");
2212 _weak.lock()->_co_handle = ch.address();
2213 _input.on_completion(std::move(_executor), [weak = std::move(_weak)]() noexcept {
2214 if (auto state = weak.lock())
2215 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2216 });
2217 }
2218};
2219
2222template <class E>
2223struct resume_on_executor_awaiter {
2224 E _executor;
2225
2226 bool await_ready() const noexcept { return false; }
2227 void await_resume() noexcept {}
2228 void await_suspend(std::coroutine_handle<> ch) {
2229 std::move(_executor)([ch]() noexcept { ch.resume(); });
2230 }
2231};
2232
2235template <class E, class WeakPtr>
2236struct resume_on_executor_awaiter_with_control {
2237 E _executor;
2238 WeakPtr _weak;
2239
2240 [[nodiscard]] bool await_ready() const noexcept { return false; }
2241 void await_resume() noexcept {}
2242 void await_suspend(std::coroutine_handle<> ch) {
2243 assert(_weak.lock() && "await_suspend: weak_state is gone");
2244 _weak.lock()->_co_handle = ch.address();
2245 std::move(_executor)([weak = std::move(_weak)]() noexcept {
2246 if (auto state = weak.lock())
2247 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2248 });
2249 }
2250};
2251
2254template <class E, class A>
2255struct resume_on_any_awaiter {
2256 E _executor;
2257 A _awaitable;
2258 std::optional<await_result_storage_t<A>> _result;
2259 std::exception_ptr _exception;
2260
2261 bool await_ready() const noexcept { return false; }
2262
2263 await_result_t<A> await_resume() {
2264 if (_exception) std::rethrow_exception(_exception);
2265 if constexpr (std::is_void_v<await_result_t<A>>) {
2266 return;
2267 } else {
2268 return std::move(*_result);
2269 }
2270 }
2271
2272 void await_suspend(std::coroutine_handle<> ch) {
2273 auto inner = get_awaiter(std::move(_awaitable));
2274 if (inner.await_ready()) {
2275 try {
2276 if constexpr (std::is_void_v<await_result_t<A>>) {
2277 inner.await_resume();
2278 _result.emplace(std::monostate{});
2279 } else {
2280 _result.emplace(inner.await_resume());
2281 }
2282 } catch (...) {
2283 _exception = std::current_exception();
2284 }
2285 std::move(_executor)([ch]() noexcept { ch.resume(); });
2286 return;
2287 }
2288 resume_on_proxy_coro<std::decay_t<A>>(
2289 std::move(_executor), [ch]() noexcept { ch.resume(); }, std::move(inner), &_result,
2290 &_exception);
2291 }
2292};
2293
2297template <class E, class A, class WeakPtr>
2298struct resume_on_any_awaiter_with_control {
2299 E _executor;
2300 A _awaitable;
2301 WeakPtr _weak;
2302 std::optional<await_result_storage_t<A>> _result;
2303 std::exception_ptr _exception;
2304
2305 bool await_ready() const noexcept { return false; }
2306
2307 await_result_t<A> await_resume() {
2308 if (_exception) std::rethrow_exception(_exception);
2309 if constexpr (std::is_void_v<await_result_t<A>>) {
2310 return;
2311 } else {
2312 return std::move(*_result);
2313 }
2314 }
2315
2316 void await_suspend(std::coroutine_handle<> ch) {
2317 assert(_weak.lock() && "await_suspend: weak_state is gone");
2318 _weak.lock()->_co_handle = ch.address();
2319 auto inner = get_awaiter(std::move(_awaitable));
2320 if (inner.await_ready()) {
2321 try {
2322 if constexpr (std::is_void_v<await_result_t<A>>) {
2323 inner.await_resume();
2324 _result.emplace(std::monostate{});
2325 } else {
2326 _result.emplace(inner.await_resume());
2327 }
2328 } catch (...) {
2329 _exception = std::current_exception();
2330 }
2331 std::move(_executor)([weak = _weak]() noexcept {
2332 if (auto state = weak.lock())
2333 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2334 });
2335 return;
2336 }
2337 resume_on_proxy_coro_controlled<std::decay_t<A>>(
2338 std::move(_executor),
2339 [weak = _weak]() noexcept {
2340 if (auto state = weak.lock())
2341 std::coroutine_handle<>::from_address(state->_co_handle).resume();
2342 },
2343 std::move(inner), _weak, &_result, &_exception);
2344 }
2345};
2346
2351template <class A>
2352struct cancelable_requires_stlab_future_coroutine {
2353 A _awaitable;
2354};
2355
2356template <>
2357struct cancelable_requires_stlab_future_coroutine<void> {};
2358
2363template <class WeakPtr>
2364struct cancelable_unique_checkpoint_awaiter {
2365 WeakPtr _weak;
2366
2367 [[nodiscard]] bool await_ready() const noexcept { return !_weak.expired(); }
2368 void await_resume() noexcept {}
2369 void await_suspend(std::coroutine_handle<> ch) { ch.destroy(); }
2370};
2371
2372} // namespace detail
2373
2376template <class E, class R>
2377auto resume_on(E&& executor, future<R>&& f) -> detail::resume_on_awaiter<R, std::decay_t<E>> {
2378 return detail::resume_on_awaiter{std::forward<E>(executor), std::move(f)};
2379}
2380
2383template <class E, class A>
2384auto resume_on(E&& executor, A&& awaitable)
2385 -> detail::resume_on_any_awaiter<std::decay_t<E>, std::decay_t<A>> {
2386 return detail::resume_on_any_awaiter<std::decay_t<E>, std::decay_t<A>>{
2387 std::forward<E>(executor), std::forward<A>(awaitable), {}, {}};
2388}
2389
2392template <class E>
2393auto resume_on(E&& executor) -> detail::resume_on_executor_awaiter<std::decay_t<E>> {
2394 return detail::resume_on_executor_awaiter<std::decay_t<E>>{std::forward<E>(executor)};
2395}
2396
2400template <class A, std::enable_if_t<!detail::is_future_v<std::decay_t<A>>, int> = 0>
2401auto cancelable(A&& awaitable)
2402 -> detail::cancelable_requires_stlab_future_coroutine<std::decay_t<A>> {
2403 return {std::forward<A>(awaitable)};
2404}
2405
2406template <class R>
2407auto cancelable(future<R>&) // Use co_await std::move(f); it is already cancelable.
2408 -> future<R> = delete;
2409
2410template <class R>
2411auto cancelable(future<R>&&) // Use co_await std::move(f); it is already cancelable.
2412 -> future<R> = delete;
2413
2414template <class R>
2415auto cancelable(const future<R>&) // Use co_await std::move(f); it is already cancelable.
2416 -> future<R> = delete;
2417
2421[[nodiscard]] inline auto cancelable() -> detail::cancelable_requires_stlab_future_coroutine<void> {
2422 return {};
2423}
2424
2426
2427STLAB_VERSION_NAMESPACE_END()
2428} // namespace stlab
2429
2430/**************************************************************************************************/
2431// Coroutine ownership and destruction rules (implementation notes):
2432//
2433// We destroy the coroutine only while it is suspended, in final_awaiter::await_suspend
2434// (h.destroy()). Before invoking the packaged_task (in
2435// return_value/return_void/unhandled_exception), we clear _co_handle in the shared state so
2436// ~shared_task() will not try to destroy the handle when the future is later destroyed. When
2437// suspended on a future we store the handle in _co_handle so we can resume via weak_state. For
2438// non-future awaitables we clear _co_handle before suspending (give up ownership for that suspend).
2439// initial_suspend is suspend_never so we never own before the first suspend.
2440// `co_await cancelable()` with no arguments: if weak_state is expired (last future already
2441// released), await_suspend destroys the coroutine handle directly — not via _co_handle. If the weak
2442// is still valid, await_ready avoids suspension so execution continues.
2443/**************************************************************************************************/
2444
2448
2449template <class T, class... Args>
2450struct std::coroutine_traits<stlab::future<T>, Args...> { // NOLINT(cert-dcl58-cpp)
2451 struct promise_type {
2452 stlab::packaged_task<std::variant<T, std::exception_ptr>> _promise;
2453
2454 stlab::future<T> get_return_object() {
2455 auto [pro, fut] = stlab::package<T(std::variant<T, std::exception_ptr>)>(
2456 stlab::immediate_executor,
2457 [](std::variant<T, std::exception_ptr>&& v) mutable -> T {
2458 // Use index (1 = exception) to avoid ambiguity when T is std::exception_ptr.
2459 if (auto* ep = std::get_if<1>(&v)) {
2460 std::rethrow_exception(*ep);
2461 }
2462 return std::get<0>(std::move(v));
2463 });
2464 _promise = std::move(pro);
2465 return std::move(fut);
2466 }
2467
2468 auto initial_suspend() const noexcept { return std::suspend_never{}; }
2469
2470 auto final_suspend() noexcept {
2471 assert(_promise.canceled() && "final_suspend: promise not fulfilled");
2472 return stlab::detail::final_awaiter<std::variant<T, std::exception_ptr>>{};
2473 }
2474
2475 template <class U>
2476 void return_value(U&& val) {
2477 _promise(
2478 std::variant<T, std::exception_ptr>{std::in_place_index<0>, std::forward<U>(val)});
2479 }
2480
2481 void unhandled_exception() {
2482 _promise(std::variant<T, std::exception_ptr>{std::in_place_index<1>,
2483 std::current_exception()});
2484 }
2485
2486 template <class R>
2487 auto await_transform(stlab::future<R>&& f) {
2488 return stlab::detail::resume_on_awaiter_with_control<
2489 R, decltype(stlab::immediate_executor),
2490 decltype(stlab::detail::weak_state(_promise)), true>{
2491 stlab::immediate_executor, std::move(f), stlab::detail::weak_state(_promise)};
2492 }
2493 template <class R, class E>
2494 auto await_transform(stlab::detail::resume_on_awaiter<R, E> a) {
2495 return stlab::detail::resume_on_awaiter_with_control<
2496 R, E, decltype(stlab::detail::weak_state(_promise)), false>{
2497 std::move(a._executor), std::move(a._input), stlab::detail::weak_state(_promise)};
2498 }
2499 template <class E>
2500 auto await_transform(stlab::detail::resume_on_executor_awaiter<E> a) {
2501 return stlab::detail::resume_on_executor_awaiter_with_control<
2502 E, decltype(stlab::detail::weak_state(_promise))>{
2503 std::move(a._executor), stlab::detail::weak_state(_promise)};
2504 }
2505 template <class E, class A>
2506 auto await_transform(stlab::detail::resume_on_any_awaiter<E, A> a) {
2507 return stlab::detail::resume_on_any_awaiter_with_control<
2508 E, A, decltype(stlab::detail::weak_state(_promise))>{
2509 std::move(a._executor), std::move(a._awaitable),
2510 stlab::detail::weak_state(_promise), std::move(a._result), std::move(a._exception)};
2511 }
2512 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<void>) {
2513 return stlab::detail::cancelable_unique_checkpoint_awaiter<
2514 decltype(stlab::detail::weak_state(_promise))>{stlab::detail::weak_state(_promise)};
2515 }
2516 template <class A, std::enable_if_t<!std::is_void_v<A>, int> = 0>
2517 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<A> c) {
2518 return stlab::detail::resume_on_any_awaiter_with_control<
2519 decltype(stlab::immediate_executor), A,
2520 decltype(stlab::detail::weak_state(_promise))>{stlab::immediate_executor,
2521 std::move(c._awaitable),
2522 stlab::detail::weak_state(_promise),
2523 {},
2524 {}};
2525 }
2526 template <class U>
2527 U&& await_transform(U&& u) {
2528 if (auto state = stlab::detail::weak_state(_promise).lock())
2529 state->_co_handle = nullptr;
2530 return std::forward<U>(u);
2531 }
2532 };
2533};
2534
2535template <class... Args>
2536struct std::coroutine_traits<stlab::future<void>, Args...> {
2537 struct promise_type {
2538 stlab::packaged_task<std::exception_ptr> _promise;
2539
2540 stlab::future<void> get_return_object() {
2542 stlab::immediate_executor, [](const std::exception_ptr& ep) mutable {
2543 if (ep) std::rethrow_exception(ep);
2544 });
2545 _promise = std::move(pro);
2546 return std::move(fut);
2547 }
2548
2549 auto initial_suspend() const noexcept { return std::suspend_never{}; }
2550
2551 auto final_suspend() noexcept {
2552 assert(_promise.canceled() && "final_suspend: promise not fulfilled");
2553 return stlab::detail::final_awaiter<std::exception_ptr>{};
2554 }
2555
2556 void return_void() { _promise(std::exception_ptr{}); }
2557
2558 void unhandled_exception() { _promise(std::current_exception()); }
2559
2560 template <class R>
2561 auto await_transform(stlab::future<R>&& f) {
2562 return stlab::detail::resume_on_awaiter_with_control<
2563 R, decltype(stlab::immediate_executor),
2564 decltype(stlab::detail::weak_state(_promise)), true>{
2565 stlab::immediate_executor, std::move(f), stlab::detail::weak_state(_promise)};
2566 }
2567 template <class R, class E>
2568 auto await_transform(stlab::detail::resume_on_awaiter<R, E> a) {
2569 return stlab::detail::resume_on_awaiter_with_control<
2570 R, E, decltype(stlab::detail::weak_state(_promise)), false>{
2571 std::move(a._executor), std::move(a._input), stlab::detail::weak_state(_promise)};
2572 }
2573 template <class E>
2574 auto await_transform(stlab::detail::resume_on_executor_awaiter<E> a) {
2575 return stlab::detail::resume_on_executor_awaiter_with_control<
2576 E, decltype(stlab::detail::weak_state(_promise))>{
2577 std::move(a._executor), stlab::detail::weak_state(_promise)};
2578 }
2579 template <class E, class A>
2580 auto await_transform(stlab::detail::resume_on_any_awaiter<E, A> a) {
2581 return stlab::detail::resume_on_any_awaiter_with_control<
2582 E, A, decltype(stlab::detail::weak_state(_promise))>{
2583 std::move(a._executor), std::move(a._awaitable),
2584 stlab::detail::weak_state(_promise), std::move(a._result), std::move(a._exception)};
2585 }
2586 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<void>) {
2587 return stlab::detail::cancelable_unique_checkpoint_awaiter<
2588 decltype(stlab::detail::weak_state(_promise))>{stlab::detail::weak_state(_promise)};
2589 }
2590 template <class A, std::enable_if_t<!std::is_void_v<A>, int> = 0>
2591 auto await_transform(stlab::detail::cancelable_requires_stlab_future_coroutine<A> c) {
2592 return stlab::detail::resume_on_any_awaiter_with_control<
2593 decltype(stlab::immediate_executor), A,
2594 decltype(stlab::detail::weak_state(_promise))>{stlab::immediate_executor,
2595 std::move(c._awaitable),
2596 stlab::detail::weak_state(_promise),
2597 {},
2598 {}};
2599 }
2600 template <class U>
2601 U&& await_transform(U&& u) {
2602 if (auto state = stlab::detail::weak_state(_promise).lock())
2603 state->_co_handle = nullptr;
2604 return std::forward<U>(u);
2605 }
2606 };
2607};
2608
2609/**************************************************************************************************/
2610
2611template <class R>
2612auto operator co_await(stlab::future<R>&& f) {
2613 return stlab::detail::resume_on_awaiter{stlab::immediate_executor, std::move(f)};
2614}
2615
2616// co_await on an lvalue future is deleted — use std::move(f) to make cancellation semantics
2617// explicit
2618template <class R>
2619auto operator co_await(stlab::future<R>& f) = delete;
2620
2622
2623#endif // STLAB_STD_COROUTINES()
2624
2625#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 that completes when all input futures are ready; f receives their values.
Definition future.hpp:1566
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:1955
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:1634
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
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
Small memory-related utilities (make_weak_ptr).
Definition reverse.hpp:28
Helper to implement when_any for result type T.
Definition future.hpp:1585
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.).