Skip to content

Commit 63348df

Browse files
burblebeetkoeppe
authored andcommitted
P3481R5 std::execution::bulk() issues
1 parent e0eccf1 commit 63348df

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
@@ -631,6 +631,8 @@
631631
struct @\libglobal{let_error_t}@ { @\unspec@ };
632632
struct @\libglobal{let_stopped_t}@ { @\unspec@ };
633633
struct @\libglobal{bulk_t}@ { @\unspec@ };
634+
struct @\libglobal{bulk_chunked_t}@ { @\unspec@ };
635+
struct @\libglobal{bulk_unchunked_t}@ { @\unspec@ };
634636
struct @\libglobal{split_t}@ { @\unspec@ };
635637
struct @\libglobal{when_all_t}@ { @\unspec@ };
636638
struct @\libglobal{when_all_with_variant_t}@ { @\unspec@ };
@@ -651,6 +653,8 @@
651653
inline constexpr let_error_t @\libglobal{let_error}@{};
652654
inline constexpr let_stopped_t @\libglobal{let_stopped}@{};
653655
inline constexpr bulk_t @\libglobal{bulk}@{};
656+
inline constexpr bulk_chunked_t @\libglobal{bulk_chunked}@{};
657+
inline constexpr bulk_unchunked_t @\libglobal{bulk_unchunked}@{};
654658
inline constexpr split_t @\libglobal{split}@{};
655659
inline constexpr when_all_t @\libglobal{when_all}@{};
656660
inline constexpr when_all_with_variant_t @\libglobal{when_all_with_variant}@{};
@@ -3677,52 +3681,133 @@
36773681
propagates the other completion operations sent by \tcode{sndr}.
36783682
\end{itemize}
36793683

3680-
\rSec3[exec.bulk]{\tcode{execution::bulk}}
3684+
\rSec3[exec.bulk]{\tcode{execution::bulk}, \tcode{execution::bulk_chunked}, and \tcode{execution::bulk_unchunked}}
36813685

36823686
\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.
36843689

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))>}.
36883700
If
36893701
\begin{itemize}
36903702
\item
36913703
\tcode{decltype((sndr))} does not satisfy \libconcept{sender}, or
36923704
\item
3705+
\tcode{is_execution_policy_v<Policy>} is \tcode{false}, or
3706+
\item
36933707
\tcode{Shape} does not satisfy \libconcept{integral}, or
36943708
\item
3695-
\tcode{decltype((f))} does not satisfy \exposconcept{movable-value},
3709+
\tcode{Func} does not model \libconcept{copy_constructible},
36963710
\end{itemize}
3697-
\tcode{bulk(sndr, shape, f)} is ill-formed.
3711+
\tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)} is ill-formed.
36983712

36993713
\pnum
37003714
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:
37023717

37033718
\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))
37053723
\end{codeblock}
37063724
except that \tcode{sndr} is evaluated only once.
37073725

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+
37083754
\pnum
37093755
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:
37113757
\begin{codeblock}
37123758
namespace std::execution {
37133759
template<>
3714-
struct @\exposid{impls-for}@<bulk_t> : @\exposid{default-impls}@ {
3760+
struct @\exposid{impls-for}@<bulk_chunked_t> : @\exposid{default-impls}@ {
37153761
static constexpr auto @\exposid{complete}@ = @\seebelow@;
37163762
};
37173763
}
37183764
\end{codeblock}
37193765

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+
37203792
\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}}
37223806
is initialized with a callable object equivalent to the following lambda:
37233807
\begin{codeblock}
37243808
[]<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@ {
37263811
if constexpr (@\libconcept{same_as}@<Tag, set_value_t>) {
37273812
auto& [shape, f] = state;
37283813
constexpr bool nothrow = noexcept(f(auto(shape), args...));
@@ -3738,32 +3823,110 @@
37383823
}
37393824
\end{codeblock}
37403825

3741-
\pnum
3826+
\par
37423827
The expression in the \grammarterm{requires-clause} of the lambda above
37433828
is \tcode{true} if and only
37443829
if \tcode{Tag} denotes a type other than \tcode{set_value_t} or
37453830
if the expression \tcode{f(auto(shape), args...)} is well-formed.
37463831

37473832
\pnum
37483833
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
37503836
an object equal to such, and
37513837
let the subexpression \tcode{rcvr} denote a receiver
37523838
such that the expression \tcode{connect(out_sndr, rcvr)} is well-formed.
37533839
The expression \tcode{connect(out_sndr, rcvr)} has undefined behavior
37543840
unless it creates an asynchronous operation\iref{exec.async.ops} that,
3755-
when started,
3841+
when started:
3842+
37563843
\begin{itemize}
37573844
\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+
37633904
\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}.
37653920
\end{itemize}
37663921

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

37693932
\pnum

0 commit comments

Comments
 (0)