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