9#ifndef STLAB_CONCURRENCY_FUTURE_HPP
10#define STLAB_CONCURRENCY_FUTURE_HPP
12#include <stlab/config.hpp>
21#include <initializer_list>
29#if STLAB_STD_COROUTINES()
97STLAB_VERSION_NAMESPACE_BEGIN()
114template <class F, class... 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{};
120 return std::forward<F>(f)(std::forward<Args>(args)...);
127 using type = std::conditional_t<std::is_void_v<T>, std::monostate, T>;
142 return o.has_value();
154 return std::forward<T>(a);
165 return std::forward_as_tuple(std::forward<T>(a));
170template <
class F,
class... Args>
173 [&](
auto&&... args) {
return std::forward<F>(f)(std::forward<decltype(args)>(args)...); },
175 [&](
auto&&... args) {
178 std::forward_as_tuple(std::forward<Args>(args)...)));
197 return "broken promise";
210#if STLAB_CPP_VERSION_AT_LEAST(17)
211template <
class F,
class... Args>
212using result_t = std::invoke_result_t<F, Args...>;
214template <
class F,
class... Args>
215using result_t = std::result_of_t<F(Args...)>;
225class future_error :
public std::logic_error {
233 [[nodiscard]]
auto what() const noexcept -> const
char*
override {
234 return detail::Future_error_map(_code);
250template <
class R,
class... Args>
251struct result_of_<R(Args...)> {
256using result_of_t_ =
typename result_of_<F>::type;
258template <
class F,
class T>
259struct result_of_when_all_t;
262struct result_of_when_all_t<F, void> {
263 using result_type = detail::result_t<F>;
266template <
class F,
class T>
267struct result_of_when_all_t {
268 using result_type = detail::result_t<F, const std::vector<T>&>;
271template <
class F,
class T>
272struct result_of_when_any_t;
275struct result_of_when_any_t<F, void> {
276 using result_type = detail::result_t<F, size_t>;
279template <
class F,
class R>
280struct result_of_when_any_t {
281 using result_type = detail::result_t<F, R, size_t>;
285auto unique_usage(
const std::shared_ptr<T>& p) ->
bool {
286 return p.use_count() == 1;
299template <
class,
class =
void>
309struct packaged_task_from_signature;
311template <
class R,
class... Args>
312struct packaged_task_from_signature<R(Args...)> {
317using packaged_task_from_signature_t =
typename packaged_task_from_signature<T>::type;
322struct reduced_signature;
324template <
class R,
class... Args>
325struct reduced_signature<R(Args...)> {
326 using type = R(Args...);
329template <
class R,
class... Args>
330struct reduced_signature<
future<R>(Args...)> {
331 using type = R(Args...);
335using reduced_signature_t =
typename reduced_signature<T>::type;
340inline constexpr bool is_future_v =
false;
343inline constexpr bool is_future_v<future<T>> =
true;
346using reduced_t = std::conditional_t<is_future_v<T>, T,
future<T>>;
349using reduced_result_t = reduced_t<result_of_t_<Sig>>;
353template <
class T,
class =
void>
362template <
class Sig,
class E,
class F>
364 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
372template <
class,
class>
374template <
class,
class =
void>
379template <
class... Args>
381 void* _co_handle{
nullptr};
383 virtual ~shared_task() {
384#if STLAB_STD_COROUTINES()
385 if (_co_handle) std::coroutine_handle<>::from_address(_co_handle).destroy();
389 virtual void operator()(Args...) = 0;
390 virtual void set_exception(
const std::exception_ptr&)
noexcept = 0;
397 : std::enable_shared_from_this<shared_base<T>> {
398 using then_t = std::vector<std::pair<
executor_t,
task<void() noexcept>>>;
403 std::optional<type> _result;
404 std::exception_ptr _exception;
406 std::atomic_bool _ready{
false};
412 auto recover(future<T>&& p, F&& f) {
413 return recover(std::move(p), _executor, std::forward<F>(f));
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>>;
421 executor, [_f = std::forward<F>(f), _p = std::move(p)]()
mutable {
422 return std::move(_f)(std::move(_p));
427 std::unique_lock<std::mutex> lock(_mutex);
429 if (!ready) _then.emplace_back(std::move(executor), std::move(pro));
432 if (ready) executor(std::move(pro));
434 return std::move(fut);
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));
445 std::unique_lock<std::mutex> lock(_mutex);
450 if (ready) std::move(pro)();
454 std::unique_lock<std::mutex> lock(_mutex);
456 _then.emplace_back([](
auto&&) {}, [_p = this->shared_from_this()]()
noexcept {});
460 void _on_completion(F&& f) {
461 task<void() noexcept> t(std::forward<F>(f));
463 std::unique_lock<std::mutex> lock(_mutex);
472 template <
class E,
class F>
473 void _on_completion(E executor, F&& f) {
474 task<void() noexcept> t(std::forward<F>(f));
476 std::unique_lock<std::mutex> lock(_mutex);
478 _then.emplace_back(std::move(executor), std::move(t));
482 executor(std::move(t));
485 void _set_exception(
const std::exception_ptr& error)
noexcept {
489 std::unique_lock<std::mutex> lock(_mutex);
490 then = std::move(_then);
494 for (
auto& e : then) {
495 e.first(std::move(e.second));
500 void set_value(A&& a);
502 auto is_ready() const& ->
bool {
return _ready; }
505 auto get_ready() ->
const type& {
506 assert(is_ready() &&
"FATAL (sean.parent) : get_ready() called but not ready!");
508 if (_exception) std::rethrow_exception(_exception);
512 auto get_ready_r(
bool unique) -> type {
513 if (!unique)
return get_ready();
515 assert(is_ready() &&
"FATAL (sean.parent) : get_ready() called but not ready!");
517 if (_exception) std::rethrow_exception(_exception);
518 return std::move(*_result);
521 auto get_try() -> std::optional<type> {
524 std::unique_lock<std::mutex> lock(_mutex);
528 if (_exception) std::rethrow_exception(_exception);
534 auto get_try_r(
bool unique) -> std::optional<type> {
535 if (!unique)
return get_try();
539 std::unique_lock<std::mutex> lock(_mutex);
543 if (_exception) std::rethrow_exception(_exception);
544 return std::move(_result);
554 : std::enable_shared_from_this<shared_base<T>> {
558 std::optional<T> _result;
559 std::exception_ptr _exception;
561 std::atomic_bool _ready{
false};
567 auto recover(future<T>&& p, F&& f) {
568 return recover(std::move(p), _executor, std::forward<F>(f));
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>>;
581 executor, [_f = std::forward<F>(f), _p = std::move(p)]()
mutable {
582 return std::move(_f)(std::move(_p));
587 std::unique_lock<std::mutex> lock(_mutex);
590 assert(!_then.second &&
"recover: _then slot already occupied");
591 _then = {std::move(executor), std::move(pro)};
594 if (ready) executor(std::move(pro));
596 return std::move(fut);
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));
607 std::unique_lock<std::mutex> lock(_mutex);
612 if (ready) std::move(pro)();
616 std::unique_lock<std::mutex> lock(_mutex);
617 if (!_ready) _then = then_t([](
auto&&) {}, [_p = this->shared_from_this()]()
noexcept {});
621 void _on_completion(F&& f) {
622 task<void() noexcept> t(std::forward<F>(f));
624 std::unique_lock<std::mutex> lock(_mutex);
626 assert(!_then.second &&
"on_completion: _then slot already occupied");
634 template <
class E,
class F>
635 void _on_completion(E executor, F&& f) {
636 task<void() noexcept> t(std::forward<F>(f));
638 std::unique_lock<std::mutex> lock(_mutex);
640 assert(!_then.second &&
"on_completion: _then slot already occupied");
641 _then = {std::move(executor), std::move(t)};
645 executor(std::move(t));
648 void _set_exception(
const std::exception_ptr& error)
noexcept {
652 std::unique_lock<std::mutex> lock(_mutex);
653 if (_then.second) then = std::move(_then);
657 if (then.second) then.first(std::move(then.second));
661 void set_value(A&& a);
663 auto is_ready() const ->
bool {
667 auto get_ready() ->
const T& {
return get_ready_r(
true); }
669 auto get_ready_r(
bool) -> T {
670 assert(is_ready() &&
"FATAL (sean.parent) : get_ready() called but not ready!");
672 if (_exception) std::rethrow_exception(_exception);
673 return std::move(*_result);
676 auto get_try() -> std::optional<T> {
return get_try_r(
true); }
678 auto get_try_r(
bool) -> std::optional<T> {
681 std::unique_lock<std::mutex> lock(_mutex);
685 if (_exception) std::rethrow_exception(_exception);
686 return std::move(_result);
699 std::weak_ptr<shared_base<R>> _p;
702 explicit promise(
const std::shared_ptr<shared_base<R>>& p) : _p{p} {}
705 if (
auto p = _p.lock()) {
711 promise(promise&&) noexcept = default;
712 auto operator=(promise&&) noexcept -> promise& = default;
714 promise(const promise&) = delete;
715 auto operator=(const promise&) -> promise& = delete;
719 void set_value(type&& value) && noexcept {
720 if (
auto p = _p.lock()) {
722 p->set_value(std::move(value));
727 auto set_value() &&
noexcept { set_value(std::monostate{}); }
730 void set_exception(
const std::exception_ptr& error) &&
noexcept {
731 if (
auto p = _p.lock()) {
733 p->_set_exception(error);
738 [[nodiscard]]
auto canceled() const ->
bool {
return _p.expired(); }
743promise(std::shared_ptr<shared_base<R>>) -> promise<R>;
745template <
class F,
class R,
class... Args>
746struct shared<F, R(Args...)> final : shared_base<R>, shared_task<Args...> {
751 void operator()(Args... args)
noexcept override {
752 std::move (*_f)(promise{this->shared_from_this()}, std::move(args)...);
758 void set_exception(
const std::exception_ptr& error)
noexcept override {
759 shared_base<R>::_set_exception(error);
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));
769template <
class... Args>
770[[nodiscard]]
auto weak_state(
const packaged_task<Args...>& p)
771 -> std::weak_ptr<shared_task<Args...>>;
781template <
class... Args>
783 using ptr_t = std::weak_ptr<detail::shared_task<Args...>>;
786 explicit packaged_task(ptr_t&& p) : _p(std::move(p)) {}
788 template <
class Sig,
class E,
class F>
790 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
792 template <
class T,
class E>
795 template <
class... Brgs>
796 friend auto detail::weak_state(
const packaged_task<Brgs...>& p)
797 -> std::weak_ptr<detail::shared_task<Brgs...>>;
800 packaged_task() =
default;
802 if (
auto p = _p.lock()) {
808 packaged_task(
const packaged_task&) =
delete;
809 auto operator=(
const packaged_task&) -> packaged_task& =
delete;
811 packaged_task(packaged_task&&)
noexcept =
default;
812 auto operator=(packaged_task&& x)
noexcept -> packaged_task& =
default;
817 template <
class... A>
819 if (
auto p = _p.lock()) {
820 p->_co_handle =
nullptr;
822 (*p)(std::forward<A>(args)...);
827 [[nodiscard]]
auto canceled() const ->
bool {
return _p.expired(); }
831 if (
auto p = _p.lock()) {
832 p->_co_handle =
nullptr;
841template <
class... Args>
842[[nodiscard]]
auto weak_state(
const packaged_task<Args...>& p)
843 -> std::weak_ptr<detail::shared_task<Args...>> {
861 using ptr_t = std::shared_ptr<detail::shared_base<T>>;
864 explicit future(ptr_t p) : _p(std::move(p)) {}
866 template <
class Sig,
class E,
class F>
868 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
870 template <
class U,
class E>
874 friend struct detail::shared_base<type>;
875 template <
class,
class>
876 friend struct detail::value_;
884 future(
const future&) =
default;
885 future(future&&) noexcept = default;
886 auto operator=(const future&) -> future& = default;
887 auto operator=(future&&) noexcept -> future& = default;
890 void swap(future& x) noexcept { std::swap(_p, x._p); }
893 inline friend void swap(future& x, future& y)
noexcept { x.swap(y); }
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); }
902 [[nodiscard]]
auto valid() const ->
bool {
return static_cast<bool>(_p); }
908 return recover([_f = std::forward<F>(f)](future<result_type>&& p)
mutable {
924 return then(std::forward<F>(f));
929 template <
class E,
class F>
932 std::forward<E>(
executor), [_f = std::forward<F>(f)](future<result_type>&& p)
mutable {
942 return then(std::move(etp)._executor, std::move(etp)._f);
949 return std::move(*this).recover([_f = std::forward<F>(f)](future<result_type>&& p)
mutable {
950 return invoke_remove_monostate_arguments(
952 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
959 return std::move(*this).then(std::forward<F>(f));
964 template <
class E,
class 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(
970 invoke_void_to_monostate_result([&] { return std::move(p).get_ready(); }));
978 return std::move(*this).then(std::move(etp)._executor, std::move(etp)._f);
985 return _p->recover(
copy(*
this), std::forward<F>(f));
991 return recover(std::forward<F>(f));
995 template <
class E,
class F>
997 return _p->recover(
copy(*
this), std::forward<E>(
executor), std::forward<F>(f));
1003 return recover(std::move(etp)._executor, std::move(etp)._f);
1010 auto& self = *_p.get();
1011 return self.recover(std::move(*
this), std::forward<F>(f));
1017 return std::move(*this).recover(std::forward<F>(f));
1022 template <
class E,
class F>
1024 auto& self = *_p.get();
1025 return self.recover(std::move(*
this), std::forward<E>(
executor), std::forward<F>(f));
1032 return std::move(*this).recover(std::move(etp)._executor, std::move(etp)._f);
1042 auto& self = *_p.get();
1043 self._detach(std::move(*
this), std::forward<F>(f));
1050 _p->_on_completion(std::forward<F>(f));
1055 template <
class E,
class F>
1057 _p->_on_completion(std::forward<E>(
executor), std::forward<F>(f));
1064 [[nodiscard]]
auto is_ready() const& ->
bool {
return _p && _p->is_ready(); }
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;
1088 [[nodiscard]]
auto exception() const& -> std::exception_ptr {
1090 return _p->_exception;
1100 using ptr_t = std::shared_ptr<detail::shared_base<T>>;
1103 explicit future(ptr_t p) : _p(std::move(p)) {}
1105 template <
class Sig,
class E,
class F>
1107 -> std::pair<detail::packaged_task_from_signature_t<Sig>, detail::reduced_result_t<Sig>>;
1109 template <
class U,
class E>
1113 friend struct detail::shared_base<T>;
1114 template <
class,
class>
1115 friend struct detail::value_;
1123 future(
const future&) =
delete;
1124 future(future&&) noexcept = default;
1125 auto operator=(const future&) -> future& = delete;
1126 auto operator=(future&&) noexcept -> future& = default;
1129 void swap(future& x) noexcept { std::swap(_p, x._p); }
1132 inline friend void swap(future& x, future& y)
noexcept { x.swap(y); }
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); }
1141 [[nodiscard]]
auto valid() const ->
bool {
return static_cast<bool>(_p); }
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());
1159 return std::move(*this).then(std::forward<F>(f));
1164 template <
class E,
class 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());
1175 return std::move(*this).then(std::move(etp)._executor, std::move(etp)._f);
1182 auto& self = *_p.get();
1183 return self.recover(std::move(*
this), std::forward<F>(f));
1189 return std::move(*this).recover(std::forward<F>(f));
1193 template <
class E,
class F>
1195 auto& self = *_p.get();
1196 return self.recover(std::move(*
this), std::forward<E>(
executor), std::forward<F>(f));
1202 return std::move(*this).recover(std::move(etp)._executor, std::move(etp)._f);
1212 auto& self = *_p.get();
1213 self._detach(std::move(*
this), std::forward<F>(f));
1221 _p->_on_completion(std::forward<F>(f));
1227 template <
class E,
class F>
1229 _p->_on_completion(std::forward<E>(
executor), std::forward<F>(f));
1236 [[nodiscard]]
auto is_ready() const& ->
bool {
return _p && _p->is_ready(); }
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;
1260 [[nodiscard]]
auto exception() const& -> std::exception_ptr {
1262 return _p->_exception;
1268template <
class Sig,
class E,
class 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!");
1275 using result_t = detail::result_of_t_<Sig>;
1277 if constexpr (detail::is_future_v<result_t>) {
1278 auto p = detail::make_shared_state<detail::reduced_signature_t<Sig>>(
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");
1284 auto r = std::move(*_f)(std::forward<decltype(args)>(args)...);
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));
1293 [&] {
return std::move(f).get_ready(); }));
1302 std::move(promise).set_exception(std::current_exception());
1306 return {detail::packaged_task_from_signature_t<Sig>{p}, result_t{p}};
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");
1314 std::move(*_f), std::forward<
decltype(args)>(args)...);
1315 std::move(promise).set_value(std::move(tmp));
1317 std::move(promise).set_exception(std::current_exception());
1321 return {detail::packaged_task_from_signature_t<Sig>{p},
future<result_t>{p}};
1327template <
class T,
class E>
1329 auto p = std::make_shared<detail::shared_base<typename detail::reduced_t<T>::result_type>>(
1334 return detail::reduced_t<T>{p};
1342struct assign_ready_future {
1344 static void assign(T& x, F&& f) {
1345 x = std::move(*(std::move(f).get_try()));
1350struct assign_ready_future<future<void>> {
1352 static void assign(T& x,
const future<void>&) {
1353 x = std::move(
typename T::value_type());
1357template <
class F,
class Args>
1358struct when_all_shared {
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;
1368 template <std::
size_t index,
class FF>
1369 auto done(FF&& f) -> std::enable_if_t<!std::is_lvalue_reference_v<FF>> {
1372 std::unique_lock<std::mutex> lock{_guard};
1374 assign_ready_future<FF>::assign(std::get<index>(_args), std::move(f));
1375 if (--_remaining == 0) run =
true;
1381 void failure(
const std::exception_ptr& error) {
1384 std::unique_lock<std::mutex> lock{_guard};
1386 for (
auto& h : _holds)
1396template <
size_t S,
class R>
1397struct when_any_shared {
1398 using result_type = R;
1400 std::optional<R> _arg;
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();
1408 void failure(
const std::exception_ptr& error) {
1411 std::unique_lock<std::mutex> lock{_guard};
1412 if (--_remaining == 0) {
1420 template <
size_t index,
class FF>
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());
1436 return f(std::move(*_arg), _index);
1441struct when_any_shared<S, void> {
1442 using result_type = void;
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();
1451 void failure(
const std::exception_ptr& error) {
1453 std::unique_lock<std::mutex> lock{_guard};
1455 if (--_remaining == 0) {
1463 template <
size_t index,
class FF>
1467 std::unique_lock<std::mutex> lock{_guard};
1468 if (_index == std::numeric_limits<std::size_t>::max()) {
1482inline void rethrow_if_false(
bool x,
const std::exception_ptr& p) {
1483 if (!x) std::rethrow_exception(p);
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);
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)>>());
1501template <
class F,
class P>
1502auto apply_when_any_arg(F& f, P& p) {
1503 if (p->_exception) {
1504 std::rethrow_exception(p->_exception);
1510template <std::
size_t i,
class E,
class P,
class T>
1511void attach_when_arg_(E&& executor, std::shared_ptr<P>& shared, T a) {
1513 std::move(a).recover(std::forward<E>(executor), [_w = std::weak_ptr<P>(shared)](
auto&& x) {
1517 if (
auto ex = x.exception()) {
1518 p->failure(std::move(ex));
1520 p->template done<i>(std::move(x));
1523 std::unique_lock<std::mutex> lock{shared->_guard};
1524 shared->_holds[i] = std::move(holds);
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)...};
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,
1549template <
class E,
class F,
class... Ts>
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>()));
1555 auto shared = std::make_shared<detail::when_all_shared<F, opt_t>>();
1557 executor, [_f = std::move(f), _p = shared] {
return detail::apply_when_all_args(_f, _p); });
1558 shared->_f = std::move(p.first);
1560 detail::attach_when_args(
executor, shared, std::move(args)...);
1562 return std::move(p.second);
1570 template <
class E,
class F,
class... Ts>
1572 using result_t = detail::result_t<F, T, size_t>;
1574 auto shared = std::make_shared<detail::when_any_shared<
sizeof...(Ts) + 1, T>>();
1576 return detail::apply_when_any_arg(_f, _p);
1578 shared->_f = std::move(p.first);
1580 detail::attach_when_args(
executor, shared, std::move(arg), std::move(args)...);
1582 return std::move(p.second);
1591 template <
class E,
class F,
class... Ts>
1593 using result_t = detail::result_t<F, size_t>;
1595 auto shared = std::make_shared<detail::when_any_shared<
sizeof...(Ts),
void>>();
1597 return detail::apply_when_any_arg(_f, _p);
1599 shared->_f = std::move(p.first);
1601 detail::attach_when_args(std::forward<E>(
executor), shared, std::move(args)...);
1603 return std::move(p.second);
1615template <
class E,
class F,
class T,
class... Ts>
1617 return make_when_any<T>::make(std::forward<E>(
executor), std::forward<F>(f), std::move(arg),
1618 std::move(args)...);
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;
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());
1641template <
bool Indexed,
class R>
1642struct result_creator;
1645struct result_creator<true, void> {
1647 static auto go(C& context) {
1648 return context._f(context._index);
1653struct result_creator<false, void> {
1655 static auto go(C& context) {
1656 return context._f();
1661struct result_creator<true, R> {
1663 static auto go(C& context) {
1664 return context._f(std::move(context._results), context._index);
1669struct result_creator<false, R> {
1671 static auto go(C& context) {
1672 return context._f(std::move(context._results));
1676template <
class F,
bool Indexed,
class R>
1677struct context_result {
1678 using result_type = R;
1681 std::exception_ptr _exception;
1682 std::size_t _index{0};
1685 context_result(F f, std::size_t s) : _f(std::
move(f)) { init(_results, s); }
1688 void init(std::vector<T>& v, std::size_t s) {
1693 void init(T&, std::size_t) {}
1696 void apply(FF&& f, std::size_t index) {
1697 value_storer<R>::store(*
this, std::forward<FF>(f), index);
1700 void apply(
const std::exception_ptr& error, std::size_t) { _exception = error; }
1702 auto operator()() {
return result_creator<Indexed, R>::go(*
this); }
1705template <
class F,
bool Indexed>
1706struct context_result<F, Indexed, void> {
1707 std::exception_ptr _exception;
1708 std::size_t _index{0};
1711 context_result(F f, std::size_t) : _f(std::
move(f)) {}
1714 void apply(FF&&, std::size_t index) {
1718 void apply(
const std::exception_ptr& error, std::size_t) { _exception = error; }
1720 auto operator()() {
return result_creator<Indexed, void>::go(*
this); }
1731struct single_trigger {
1732 template <
class C,
class F>
1733 static auto go(C& context, F&& f,
size_t index) ->
bool {
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();
1741 context._single_event =
true;
1742 context.apply(std::forward<F>(f), index);
1757 template <
class C,
class F>
1758 static auto go(C& context, F&& f,
size_t index) ->
bool {
1761 std::unique_lock<std::mutex> lock{context._guard};
1762 context.apply(std::forward<F>(f), index);
1763 if (--context._remaining == 0) run =
true;
1769 static auto go(C& context,
const std::exception_ptr& error,
size_t index) ->
bool {
1772 std::unique_lock<std::mutex> lock{context._guard};
1773 if (--context._remaining == 0) {
1774 context.apply(error, index);
1782template <
class CR,
class F,
class ResultCollector,
class FailureCollector>
1783struct common_context : CR {
1785 std::size_t _remaining;
1786 bool _single_event{
false};
1787 std::vector<future<void>> _holds;
1790 common_context(F f,
size_t s) : CR(std::
move(f), s), _remaining(s), _holds(_remaining) {}
1793 if (this->_exception) {
1794 std::rethrow_exception(this->_exception);
1796 return CR::operator()();
1799 void failure(
const std::exception_ptr& error,
size_t index) {
1800 if (FailureCollector::go(*
this, error, index)) _f();
1804 void done(FF&& f,
size_t index) {
1805 if (ResultCollector::go(*
this, std::forward<FF>(f), index)) _f();
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();
1817 if (
auto ex = x.exception()) {
1818 p->failure(std::move(ex), _i);
1820 p->done(std::move(x), _i);
1824 std::unique_lock<std::mutex> guard(context->_guard);
1825 context->_holds[index] = std::move(hold);
1828template <
class R,
class T,
class C,
class Enabled =
void>
1829struct create_range_of_futures;
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);
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(); });
1840 context->_f = std::move(p.first);
1843 for (; first != last; ++first) {
1844 attach_tasks(index++, executor, context, *first);
1847 return std::move(p.second);
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);
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(); });
1860 context->_f = std::move(p.first);
1863 for (; first != last; ++first) {
1864 attach_tasks(index++, executor, context, std::move(*first));
1867 return std::move(p.second);
1884template <
class E,
class F,
class I>
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>;
1893 if (range.first == range.second) {
1895 executor, detail::context_result<F, false, context_result_t>(std::move(f), 0));
1897 return std::move(p.second);
1900 return detail::create_range_of_futures<result_t, param_t, context_t>::do_it(
1901 executor, std::move(f), range.first, range.second);
1913template <
class E,
class F,
class I>
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>;
1921 if (range.first == range.second) {
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);
1936template <
class E,
class F,
class... 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>...>;
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)); });
1948 return std::move(fut);
1958struct value_<T, enable_if_copyable<void_to_monostate_t<T>>> {
1960 static void proceed(C& sb) {
1961 typename C::then_t then;
1963 std::unique_lock<std::mutex> lock(sb._mutex);
1965 then = std::move(sb._then);
1967 for (
auto& e : then)
1968 e.first(std::move(e.second));
1971 template <
class R,
class A>
1972 static void set(shared_base<R>& sb, A&& a) {
1973 sb._result = std::forward<A>(a);
1981 static void proceed(C& sb) {
1982 typename C::then_t then;
1984 std::unique_lock<std::mutex> lock(sb._mutex);
1986 then = std::move(sb._then);
1988 if (then.first) then.first(std::move(then.second));
1991 template <
class R,
class A>
1992 static void set(shared_base<R>& sb, A&& a) {
1993 sb._result = std::forward<A>(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));
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));
2020STLAB_VERSION_NAMESPACE_END()
2025#if STLAB_STD_COROUTINES()
2030STLAB_VERSION_NAMESPACE_BEGIN()
2041template <
class A,
class =
void>
2042struct has_member_co_await : std::false_type {};
2044struct has_member_co_await<A, std::void_t<decltype(std::declval<A>().operator co_await())>>
2045 : std::true_type {};
2047template <
class A,
class =
void>
2048struct has_free_co_await : std::false_type {};
2050struct has_free_co_await<A, std::void_t<decltype(operator co_await(std::declval<A>()))>>
2051 : std::true_type {};
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));
2060 return std::forward<A>(a);
2065using awaiter_t =
decltype(get_awaiter(std::declval<A&>()));
2068using await_result_t =
decltype(std::declval<awaiter_t<A>>().await_resume());
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(); }
2089template <
class A,
class Inner,
class E,
class F>
2090proxy_fire_and_forget resume_on_proxy_coro(E
executor,
2093 std::optional<await_result_storage_t<A>>* result_ptr,
2094 std::exception_ptr* exception_ptr) {
2096 if constexpr (std::is_void_v<await_result_t<A>>) {
2097 co_await std::move(inner);
2098 result_ptr->emplace(std::monostate{});
2100 result_ptr->emplace(
co_await std::move(inner));
2103 if (exception_ptr) *exception_ptr = std::current_exception();
2109template <
class A,
class Inner,
class E,
class F,
class WeakPtr>
2110proxy_fire_and_forget resume_on_proxy_coro_controlled(
2115 std::optional<await_result_storage_t<A>>* result_ptr,
2116 std::exception_ptr* exception_ptr) {
2119 bool was_alive =
false;
2121 if constexpr (std::is_void_v<await_result_t<A>>) {
2122 co_await std::move(inner);
2123 if (
auto keepalive = weak.lock()) {
2125 result_ptr->emplace(std::monostate{});
2129 auto tmp =
co_await std::move(inner);
2130 if (
auto keepalive = weak.lock()) {
2132 result_ptr->emplace(std::move(tmp));
2137 if (
auto keepalive = weak.lock()) {
2139 if (exception_ptr) *exception_ptr = std::current_exception();
2143 if (was_alive)
executor(std::move(resume_cb));
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(); }
2155template <
class R,
class E>
2156struct resume_on_awaiter {
2160 bool await_ready() const noexcept {
return false; }
2162 auto await_resume() {
return std::move(_input).get_ready(); }
2164 void await_suspend(std::coroutine_handle<> ch) {
2165 _input.on_completion(std::move(_executor), [ch]()
noexcept { ch.resume(); });
2169template <
class R,
class E>
2170resume_on_awaiter(E,
future<R>) -> resume_on_awaiter<R, std::decay_t<E>>;
2176template <
class R,
class E,
class WeakPtr,
bool AllowSkipSuspend>
2177struct resume_on_awaiter_with_control {
2182 bool await_ready() const noexcept {
2183 if constexpr (AllowSkipSuspend) {
2184 return _input.is_ready();
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();
2203struct resume_on_executor_awaiter {
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(); });
2215template <
class E,
class WeakPtr>
2216struct resume_on_executor_awaiter_with_control {
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();
2234template <
class E,
class A>
2235struct resume_on_any_awaiter {
2238 std::optional<await_result_storage_t<A>> _result;
2239 std::exception_ptr _exception;
2241 bool await_ready() const noexcept {
return false; }
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>>) {
2248 return std::move(*_result);
2252 void await_suspend(std::coroutine_handle<> ch) {
2253 auto inner = get_awaiter(std::move(_awaitable));
2254 if (inner.await_ready()) {
2256 if constexpr (std::is_void_v<await_result_t<A>>) {
2257 inner.await_resume();
2258 _result.emplace(std::monostate{});
2260 _result.emplace(inner.await_resume());
2263 _exception = std::current_exception();
2265 std::move(_executor)([ch]()
noexcept { ch.resume(); });
2268 resume_on_proxy_coro<std::decay_t<A>>(
2269 std::move(_executor), [ch]()
noexcept { ch.resume(); }, std::move(inner), &_result,
2277template <
class E,
class A,
class WeakPtr>
2278struct resume_on_any_awaiter_with_control {
2282 std::optional<await_result_storage_t<A>> _result;
2283 std::exception_ptr _exception;
2285 bool await_ready() const noexcept {
return false; }
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>>) {
2292 return std::move(*_result);
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()) {
2302 if constexpr (std::is_void_v<await_result_t<A>>) {
2303 inner.await_resume();
2304 _result.emplace(std::monostate{});
2306 _result.emplace(inner.await_resume());
2309 _exception = std::current_exception();
2311 std::move(_executor)([weak = _weak]()
noexcept {
2312 if (
auto state = weak.lock())
2313 std::coroutine_handle<>::from_address(state->_co_handle).resume();
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();
2323 std::move(inner), _weak, &_result, &_exception);
2331struct cancelable_requires_stlab_future_coroutine {
2336struct cancelable_requires_stlab_future_coroutine<void> {};
2342template <
class WeakPtr>
2343struct cancelable_unique_checkpoint_awaiter {
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(); }
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)};
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), {}, {}};
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)};
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)};
2400[[nodiscard]]
inline auto cancelable() -> detail::cancelable_requires_stlab_future_coroutine<void> {
2406STLAB_VERSION_NAMESPACE_END()
2428template <
class T,
class... Args>
2429struct std::coroutine_traits<
stlab::future<T>, Args...> {
2430 struct promise_type {
2431 stlab::packaged_task<std::variant<T, std::exception_ptr>> _promise;
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 {
2438 if (
auto* ep = std::get_if<1>(&v)) {
2439 std::rethrow_exception(*ep);
2441 return std::get<0>(std::move(v));
2443 _promise = std::move(pro);
2444 return std::move(fut);
2447 auto initial_suspend() const noexcept {
return std::suspend_never{}; }
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>>{};
2455 void return_value(U&& val) {
2457 std::variant<T, std::exception_ptr>{std::in_place_type<T>, std::forward<U>(val)});
2460 void unhandled_exception() {
2461 _promise(std::variant<T, std::exception_ptr>{std::in_place_type<std::exception_ptr>,
2462 std::current_exception()});
2466 auto await_transform(stlab::future<R>&& f) {
2467 return stlab::detail::resume_on_awaiter_with_control<
2469 decltype(stlab::detail::weak_state(_promise)),
true>{
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)};
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)};
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)};
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)};
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<
2500 std::move(c._awaitable),
2501 stlab::detail::weak_state(_promise),
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);
2514template <
class... Args>
2515struct std::coroutine_traits<
stlab::future<void>, Args...> {
2516 struct promise_type {
2517 stlab::packaged_task<std::exception_ptr> _promise;
2519 stlab::future<void> get_return_object() {
2522 if (ep) std::rethrow_exception(ep);
2524 _promise = std::move(pro);
2525 return std::move(fut);
2528 auto initial_suspend() const noexcept {
return std::suspend_never{}; }
2530 auto final_suspend() noexcept {
2531 assert(_promise.canceled() &&
"final_suspend: promise not fulfilled");
2532 return stlab::detail::final_awaiter<std::exception_ptr>{};
2535 void return_void() { _promise(std::exception_ptr{}); }
2537 void unhandled_exception() { _promise(std::current_exception()); }
2540 auto await_transform(stlab::future<R>&& f) {
2541 return stlab::detail::resume_on_awaiter_with_control<
2543 decltype(stlab::detail::weak_state(_promise)),
true>{
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)};
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)};
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)};
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)};
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<
2574 std::move(c._awaitable),
2575 stlab::detail::weak_state(_promise),
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);
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
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
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.).