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