Skip to content

Commit 90862cd

Browse files
burblebeetkoeppe
authored andcommitted
P3481R5 std::execution::bulk() issues
1 parent 4448006 commit 90862cd

File tree

1 file changed

+185
-22
lines changed

1 file changed

+185
-22
lines changed

source/exec.tex

Lines changed: 185 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,8 @@
629629
struct @\libglobal{let_error_t}@ { @\unspec@ };
630630
struct @\libglobal{let_stopped_t}@ { @\unspec@ };
631631
struct @\libglobal{bulk_t}@ { @\unspec@ };
632+
struct @\libglobal{bulk_chunked_t}@ { @\unspec@ };
633+
struct @\libglobal{bulk_unchunked_t}@ { @\unspec@ };
632634
struct @\libglobal{split_t}@ { @\unspec@ };
633635
struct @\libglobal{when_all_t}@ { @\unspec@ };
634636
struct @\libglobal{when_all_with_variant_t}@ { @\unspec@ };
@@ -649,6 +651,8 @@
649651
inline constexpr let_error_t @\libglobal{let_error}@{};
650652
inline constexpr let_stopped_t @\libglobal{let_stopped}@{};
651653
inline constexpr bulk_t @\libglobal{bulk}@{};
654+
inline constexpr bulk_chunked_t @\libglobal{bulk_chunked}@{};
655+
inline constexpr bulk_unchunked_t @\libglobal{bulk_unchunked}@{};
652656
inline constexpr split_t @\libglobal{split}@{};
653657
inline constexpr when_all_t @\libglobal{when_all}@{};
654658
inline constexpr when_all_with_variant_t @\libglobal{when_all_with_variant}@{};
@@ -3654,52 +3658,133 @@
36543658
propagates the other completion operations sent by \tcode{sndr}.
36553659
\end{itemize}
36563660

3657-
\rSec3[exec.bulk]{\tcode{execution::bulk}}
3661+
\rSec3[exec.bulk]{\tcode{execution::bulk}, \tcode{execution::bulk_chunked}, and \tcode{execution::bulk_unchunked}}
36583662

36593663
\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.
36613666

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))>}.
36653677
If
36663678
\begin{itemize}
36673679
\item
36683680
\tcode{decltype((sndr))} does not satisfy \libconcept{sender}, or
36693681
\item
3682+
\tcode{is_execution_policy_v<Policy>} is \tcode{false}, or
3683+
\item
36703684
\tcode{Shape} does not satisfy \libconcept{integral}, or
36713685
\item
3672-
\tcode{decltype((f))} does not satisfy \exposconcept{movable-value},
3686+
\tcode{Func} does not model \libconcept{copy_constructible},
36733687
\end{itemize}
3674-
\tcode{bulk(sndr, shape, f)} is ill-formed.
3688+
\tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)} is ill-formed.
36753689

36763690
\pnum
36773691
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:
36793694

36803695
\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))
36823700
\end{codeblock}
36833701
except that \tcode{sndr} is evaluated only once.
36843702

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+
36853731
\pnum
36863732
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:
36883734
\begin{codeblock}
36893735
namespace std::execution {
36903736
template<>
3691-
struct @\exposid{impls-for}@<bulk_t> : @\exposid{default-impls}@ {
3737+
struct @\exposid{impls-for}@<bulk_chunked_t> : @\exposid{default-impls}@ {
36923738
static constexpr auto @\exposid{complete}@ = @\seebelow@;
36933739
};
36943740
}
36953741
\end{codeblock}
36963742

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+
36973769
\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}}
36993783
is initialized with a callable object equivalent to the following lambda:
37003784
\begin{codeblock}
37013785
[]<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@ {
37033788
if constexpr (@\libconcept{same_as}@<Tag, set_value_t>) {
37043789
auto& [shape, f] = state;
37053790
constexpr bool nothrow = noexcept(f(auto(shape), args...));
@@ -3715,32 +3800,110 @@
37153800
}
37163801
\end{codeblock}
37173802

3718-
\pnum
3803+
\par
37193804
The expression in the \grammarterm{requires-clause} of the lambda above
37203805
is \tcode{true} if and only
37213806
if \tcode{Tag} denotes a type other than \tcode{set_value_t} or
37223807
if the expression \tcode{f(auto(shape), args...)} is well-formed.
37233808

37243809
\pnum
37253810
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
37273813
an object equal to such, and
37283814
let the subexpression \tcode{rcvr} denote a receiver
37293815
such that the expression \tcode{connect(out_sndr, rcvr)} is well-formed.
37303816
The expression \tcode{connect(out_sndr, rcvr)} has undefined behavior
37313817
unless it creates an asynchronous operation\iref{exec.async.ops} that,
3732-
when started,
3818+
when started:
3819+
37333820
\begin{itemize}
37343821
\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+
37403881
\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}.
37423897
\end{itemize}
37433898

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+
37443907
\rSec3[exec.split]{\tcode{execution::split}}
37453908

37463909
\pnum

0 commit comments

Comments
 (0)