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