|
629 | 629 | struct @\libglobal{let_error_t}@ { @\unspec@ };
|
630 | 630 | struct @\libglobal{let_stopped_t}@ { @\unspec@ };
|
631 | 631 | struct @\libglobal{bulk_t}@ { @\unspec@ };
|
| 632 | + struct @\libglobal{bulk_chunked_t}@ { @\unspec@ }; |
| 633 | + struct @\libglobal{bulk_unchunked_t}@ { @\unspec@ }; |
632 | 634 | struct @\libglobal{split_t}@ { @\unspec@ };
|
633 | 635 | struct @\libglobal{when_all_t}@ { @\unspec@ };
|
634 | 636 | struct @\libglobal{when_all_with_variant_t}@ { @\unspec@ };
|
|
647 | 649 | inline constexpr let_error_t @\libglobal{let_error}@{};
|
648 | 650 | inline constexpr let_stopped_t @\libglobal{let_stopped}@{};
|
649 | 651 | inline constexpr bulk_t @\libglobal{bulk}@{};
|
| 652 | + inline constexpr bulk_chunked_t @\libglobal{bulk_chunked}@{}; |
| 653 | + inline constexpr bulk_unchunked_t @\libglobal{bulk_unchunked}@{}; |
650 | 654 | inline constexpr split_t @\libglobal{split}@{};
|
651 | 655 | inline constexpr when_all_t @\libglobal{when_all}@{};
|
652 | 656 | inline constexpr when_all_with_variant_t @\libglobal{when_all_with_variant}@{};
|
|
3608 | 3612 | propagates the other completion operations sent by \tcode{sndr}.
|
3609 | 3613 | \end{itemize}
|
3610 | 3614 |
|
3611 |
| -\rSec3[exec.bulk]{\tcode{execution::bulk}} |
| 3615 | +\rSec3[exec.bulk]{\tcode{execution::bulk}, \tcode{execution::bulk_chunked}, and \tcode{execution::bulk_unchunked}} |
3612 | 3616 |
|
3613 | 3617 | \pnum
|
3614 |
| -\tcode{bulk} runs a task repeatedly for every index in an index space. |
| 3618 | +\tcode{bulk}, \tcode{bulk_chunked}, and \tcode{bulk_unchunked} |
| 3619 | +run a task repeatedly for every index in an index space. |
3615 | 3620 |
|
3616 |
| -The name \tcode{bulk} denotes a pipeable sender adaptor object. |
3617 |
| -For subexpressions \tcode{sndr}, \tcode{shape}, and \tcode{f}, |
3618 |
| -let \tcode{Shape} be \tcode{decltype(auto(shape))}. |
| 3621 | +\pnum |
| 3622 | +The names \tcode{bulk}, \tcode{bulk_chunked}, and \tcode{bulk_unchunked} |
| 3623 | +denote pipeable sender adaptor objects. |
| 3624 | +Let \placeholder{bulk-algo} be either |
| 3625 | +\tcode{bulk}, \tcode{bulk_chunked}, or \tcode{bulk_unchunked}. |
| 3626 | +For subexpressions \tcode{sndr}, \tcode{policy}, \tcode{shape}, and \tcode{f}, |
| 3627 | +let |
| 3628 | +\tcode{Policy} be \tcode{remove_cvref_t<decltype(policy)>}, |
| 3629 | +\tcode{Shape} be \tcode{decltype(auto(shape))}, and |
| 3630 | +\tcode{Func} be \tcode{decay_t<decltype((f))>}. |
3619 | 3631 | If
|
3620 | 3632 | \begin{itemize}
|
3621 | 3633 | \item
|
3622 | 3634 | \tcode{decltype((sndr))} does not satisfy \libconcept{sender}, or
|
3623 | 3635 | \item
|
| 3636 | +\tcode{is_execution_policy_v<Policy>} is \tcode{false}, or |
| 3637 | +\item |
3624 | 3638 | \tcode{Shape} does not satisfy \libconcept{integral}, or
|
3625 | 3639 | \item
|
3626 |
| -\tcode{decltype((f))} does not satisfy \exposconcept{movable-value}, |
| 3640 | +\tcode{Func} does not model \libconcept{copy_constructible}, |
3627 | 3641 | \end{itemize}
|
3628 |
| -\tcode{bulk(sndr, shape, f)} is ill-formed. |
| 3642 | +\tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)} is ill-formed. |
3629 | 3643 |
|
3630 | 3644 | \pnum
|
3631 | 3645 | Otherwise,
|
3632 |
| -the expression \tcode{bulk(sndr, shape, f)} is expression-equivalent to: |
| 3646 | +the expression \tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)} |
| 3647 | +is expression-equivalent to: |
3633 | 3648 |
|
3634 | 3649 | \begin{codeblock}
|
3635 |
| -transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(bulk, @\exposid{product-type}@{shape, f}, sndr)) |
| 3650 | +transform_sender(@\exposid{get-domain-early}@(sndr), |
| 3651 | + @\exposid{make-sender}@(@\placeholder{bulk-algo}@, |
| 3652 | + @\exposid{product-type}@<@\seebelow@, Shape, Func>{policy, shape, f}, |
| 3653 | + sndr)) |
3636 | 3654 | \end{codeblock}
|
3637 | 3655 | except that \tcode{sndr} is evaluated only once.
|
3638 | 3656 |
|
| 3657 | +\par |
| 3658 | +The first template argument of \exposid{product-type} is \tcode{Policy} |
| 3659 | +if \tcode{Policy} models \libconcept{copy_constructible}, and |
| 3660 | +\tcode{const Policy\&} otherwise. |
| 3661 | + |
| 3662 | +\pnum |
| 3663 | +Let \tcode{sndr} and \tcode{env} be subexpressions such that |
| 3664 | +\tcode{Sndr} is \tcode{decltype((sndr))}. |
| 3665 | +If \tcode{\exposconcept{sender-for}<Sndr, bulk_t>} is \tcode{false}, then |
| 3666 | +the expression \tcode{bulk.transform_sender(sndr, env)} is ill-formed; |
| 3667 | +otherwise, it is equivalent to: |
| 3668 | +\begin{codeblock} |
| 3669 | +auto [_, data, child] = sndr; |
| 3670 | +auto& [policy, shape, f] = data; |
| 3671 | +auto new_f = [func=std::move(f)](Shape begin, Shape end, auto&&... vs) |
| 3672 | + noexcept(noexcept(f(begin, vs...))) { |
| 3673 | + while (begin != end) func(begin++, vs...); |
| 3674 | +} |
| 3675 | +return bulk_chunked(std::move(child), policy, shape, std::move(new_f)); |
| 3676 | +\end{codeblock} |
| 3677 | + |
| 3678 | +\begin{note} |
| 3679 | +This causes the \tcode{bulk(sndr, policy, shape, f)} sender to be |
| 3680 | +expressed in terms of \tcode{bulk_chunked(sndr, policy, shape, f)} when |
| 3681 | +it is connected to a receiver whose |
| 3682 | +execution domain does not customize \tcode{bulk}. |
| 3683 | +\end{note} |
| 3684 | + |
3639 | 3685 | \pnum
|
3640 | 3686 | The exposition-only class template \exposid{impls-for}\iref{exec.snd.general}
|
3641 |
| -is specialized for \tcode{bulk_t} as follows: |
| 3687 | +is specialized for \tcode{bulk_chunked_t} as follows: |
3642 | 3688 | \begin{codeblock}
|
3643 | 3689 | namespace std::execution {
|
3644 | 3690 | template<>
|
3645 |
| - struct @\exposid{impls-for}@<bulk_t> : @\exposid{default-impls}@ { |
| 3691 | + struct @\exposid{impls-for}@<bulk_chunked_t> : @\exposid{default-impls}@ { |
3646 | 3692 | static constexpr auto @\exposid{complete}@ = @\seebelow@;
|
3647 | 3693 | };
|
3648 | 3694 | }
|
3649 | 3695 | \end{codeblock}
|
3650 | 3696 |
|
| 3697 | +\par |
| 3698 | +The member \tcode{\exposid{impls-for}<bulk_chunked_t>::\exposid{complete}} |
| 3699 | +is initialized with a callable object equivalent to the following lambda: |
| 3700 | +\begin{codeblock} |
| 3701 | +[]<class Index, class State, class Rcvr, class Tag, class... Args> |
| 3702 | + (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept |
| 3703 | + -> void requires @\seebelow@ { |
| 3704 | + if constexpr (@\libconcept{same_as}@<Tag, set_value_t>) { |
| 3705 | + auto& [policy, shape, f] = state; |
| 3706 | + constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...)); |
| 3707 | + @\exposid{TRY-EVAL}@(rcvr, [&]() noexcept(nothrow) { |
| 3708 | + f(static_cast<decltype(auto(shape))>(0), auto(shape), args...); |
| 3709 | + Tag()(std::move(rcvr), std::forward<Args>(args)...); |
| 3710 | + }()); |
| 3711 | + } else { |
| 3712 | + Tag()(std::move(rcvr), std::forward<Args>(args)...); |
| 3713 | + } |
| 3714 | + } |
| 3715 | +\end{codeblock} |
| 3716 | + |
| 3717 | +\par |
| 3718 | +The expression in the \grammarterm{requires-clause} of the lambda above is |
| 3719 | +\tcode{true} if and only |
| 3720 | +if \tcode{Tag} denotes a type other than \tcode{set_value_t} or |
| 3721 | +if the expression \tcode{f(auto(shape), auto(shape), args...)} is well-formed. |
| 3722 | + |
3651 | 3723 | \pnum
|
3652 |
| -The member \tcode{\exposid{impls-for}<bulk_t>::\exposid{complete}} |
| 3724 | +The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} |
| 3725 | +is specialized for \tcode{bulk_unchunked_t} as follows: |
| 3726 | +\begin{codeblock} |
| 3727 | +namespace std::execution { |
| 3728 | + template<> |
| 3729 | + struct @\exposid{impls-for}@<bulk_unchunked_t> : @\exposid{default-impls}@ { |
| 3730 | + static constexpr auto @\exposid{complete}@ = @\seebelow@; |
| 3731 | + }; |
| 3732 | +} |
| 3733 | +\end{codeblock} |
| 3734 | + |
| 3735 | +\par |
| 3736 | +The member \tcode{\exposid{impls-for}<bulk_unchunked_t>::\exposid{complete}} |
3653 | 3737 | is initialized with a callable object equivalent to the following lambda:
|
3654 | 3738 | \begin{codeblock}
|
3655 | 3739 | []<class Index, class State, class Rcvr, class Tag, class... Args>
|
3656 |
| - (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void requires @\seebelow@ { |
| 3740 | + (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept |
| 3741 | + -> void requires @\seebelow@ { |
3657 | 3742 | if constexpr (@\libconcept{same_as}@<Tag, set_value_t>) {
|
3658 | 3743 | auto& [shape, f] = state;
|
3659 | 3744 | constexpr bool nothrow = noexcept(f(auto(shape), args...));
|
|
3669 | 3754 | }
|
3670 | 3755 | \end{codeblock}
|
3671 | 3756 |
|
3672 |
| -\pnum |
| 3757 | +\par |
3673 | 3758 | The expression in the \grammarterm{requires-clause} of the lambda above
|
3674 | 3759 | is \tcode{true} if and only
|
3675 | 3760 | if \tcode{Tag} denotes a type other than \tcode{set_value_t} or
|
3676 | 3761 | if the expression \tcode{f(auto(shape), args...)} is well-formed.
|
3677 | 3762 |
|
3678 | 3763 | \pnum
|
3679 | 3764 | Let the subexpression \tcode{out_sndr} denote
|
3680 |
| -the result of the invocation \tcode{bulk(sndr, shape, f)} or |
| 3765 | +the result of the invocation |
| 3766 | +\tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)} or |
3681 | 3767 | an object equal to such, and
|
3682 | 3768 | let the subexpression \tcode{rcvr} denote a receiver
|
3683 | 3769 | such that the expression \tcode{connect(out_sndr, rcvr)} is well-formed.
|
3684 | 3770 | The expression \tcode{connect(out_sndr, rcvr)} has undefined behavior
|
3685 | 3771 | unless it creates an asynchronous operation\iref{exec.async.ops} that,
|
3686 |
| -when started, |
| 3772 | +when started: |
| 3773 | + |
3687 | 3774 | \begin{itemize}
|
3688 | 3775 | \item
|
3689 |
| -on a value completion operation, |
3690 |
| -invokes \tcode{f(i, args...)} |
3691 |
| -for every \tcode{i} of type \tcode{Shape} in \range{\tcode{0}}{\tcode{shape}}, |
3692 |
| -where \tcode{args} is a pack of lvalue subexpressions |
3693 |
| -referring to the value completion result datums of the input sender, and |
| 3776 | +If \tcode{sndr} has a successful completion, where |
| 3777 | +\tcode{args} is a pack of lvalue subexpressions |
| 3778 | +referring to the value completion result datums of \tcode{sndr}, or |
| 3779 | +decayed copies of those values if they model copy_constructible, then: |
| 3780 | + |
| 3781 | + \begin{itemize} |
| 3782 | + \item |
| 3783 | + If \tcode{out_sndr} also completes successfully, then: |
| 3784 | + |
| 3785 | + \begin{itemize} |
| 3786 | + \item |
| 3787 | + for \tcode{bulk}, |
| 3788 | + invokes \tcode{f($i$, args...)} for every $i$ of type \tcode{Shape} |
| 3789 | + from \tcode{0} to \tcode{shape}; |
| 3790 | + |
| 3791 | + \item |
| 3792 | + for \tcode{bulk_unchunked}, |
| 3793 | + invokes \tcode{f($i$, args...)} for every $i$ of type \tcode{Shape} |
| 3794 | + from \tcode{0} to \tcode{shape}; |
| 3795 | + |
| 3796 | + \recommended |
| 3797 | + The underlying scheduler should execute each iteration |
| 3798 | + on a distinct execution agent. |
| 3799 | + |
| 3800 | + \item |
| 3801 | + for \tcode{bulk_chunked}, |
| 3802 | + invokes \tcode{f($b$, $e$, args...)} zero or more times |
| 3803 | + with pairs of $b$ and $e$ of type \tcode{Shape} |
| 3804 | + in range \range{\tcode{0}}{\tcode{shape}}, |
| 3805 | + such that $b < e$ and |
| 3806 | + for every $i$ of type \tcode{Shape} from \tcode{0} to \tcode{shape}, |
| 3807 | + there is exactly one invocation with a pair $b$ and $e$, |
| 3808 | + such that $i$ is in the range \range{$b$}{$e$}. |
| 3809 | + \end{itemize} |
| 3810 | + |
| 3811 | + \item |
| 3812 | + If \tcode{out_sndr} completes with \tcode{set_error(rcvr, eptr)}, then |
| 3813 | + the asynchronous operation may invoke a subset of |
| 3814 | + the invocations of \tcode{f} |
| 3815 | + before the error completion handler is called, and |
| 3816 | + \tcode{eptr} is an \tcode{exception_ptr} containing either: |
| 3817 | + \begin{itemize} |
| 3818 | + \item |
| 3819 | + an exception thrown by an invocation of \tcode{f}, or |
| 3820 | + \item |
| 3821 | + a \tcode{bad_alloc} exception if |
| 3822 | + the implementation fails to allocate required resources, or |
| 3823 | + \item |
| 3824 | + an exception derived from \tcode{runtime_error}. |
| 3825 | + \end{itemize} |
| 3826 | + |
| 3827 | + \item |
| 3828 | + If \tcode{out_sndr} completes with \tcode{set_stopped(rcvr)}, then |
| 3829 | + the asynchronous operation may invoke a subset of |
| 3830 | + the invocations of \tcode{f} |
| 3831 | + before the stopped completion handler. |
| 3832 | + \end{itemize} |
| 3833 | + |
3694 | 3834 | \item
|
3695 |
| -propagates all completion operations sent by \tcode{sndr}. |
| 3835 | +If \tcode{sndr} does not complete with \tcode{set_value}, then |
| 3836 | +the completion is forwarded to \tcode{recv}. |
| 3837 | + |
| 3838 | +\item |
| 3839 | +For \placeholder{bulk-algo}, |
| 3840 | +the parameter \tcode{policy} describes |
| 3841 | +the manner in which |
| 3842 | +the execution of the asynchronous operations corresponding to these algorithms |
| 3843 | +may be parallelized and |
| 3844 | +the manner in which |
| 3845 | +%%FIXME: Should this be "apply to f"? |
| 3846 | +they apply \tcode{f}. |
| 3847 | +Permissions and requirements |
| 3848 | +on parallel algorithm element access functions\iref{algorithms.parallel.exec} |
| 3849 | +apply to \tcode{f}. |
3696 | 3850 | \end{itemize}
|
3697 | 3851 |
|
| 3852 | +\pnum |
| 3853 | +\begin{note} |
| 3854 | +The asynchronous operation corresponding to |
| 3855 | +\tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)} |
| 3856 | +can complete with \tcode{set_stopped} if cancellation is requested or |
| 3857 | +ignore cancellation requests. |
| 3858 | +\end{note} |
| 3859 | + |
3698 | 3860 | \rSec3[exec.split]{\tcode{execution::split}}
|
3699 | 3861 |
|
3700 | 3862 | \pnum
|
|
0 commit comments