Skip to content

Commit a6ab2f3

Browse files
committed
P3481R5 std::execution::bulk() issues
1 parent 691ab30 commit a6ab2f3

File tree

1 file changed

+184
-22
lines changed

1 file changed

+184
-22
lines changed

source/exec.tex

Lines changed: 184 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@ };
@@ -647,6 +649,8 @@
647649
inline constexpr let_error_t @\libglobal{let_error}@{};
648650
inline constexpr let_stopped_t @\libglobal{let_stopped}@{};
649651
inline constexpr bulk_t @\libglobal{bulk}@{};
652+
inline constexpr bulk_chunked_t @\libglobal{bulk_chunked}@{};
653+
inline constexpr bulk_unchunked_t @\libglobal{bulk_unchunked}@{};
650654
inline constexpr split_t @\libglobal{split}@{};
651655
inline constexpr when_all_t @\libglobal{when_all}@{};
652656
inline constexpr when_all_with_variant_t @\libglobal{when_all_with_variant}@{};
@@ -3608,52 +3612,133 @@
36083612
propagates the other completion operations sent by \tcode{sndr}.
36093613
\end{itemize}
36103614

3611-
\rSec3[exec.bulk]{\tcode{execution::bulk}}
3615+
\rSec3[exec.bulk]{\tcode{execution::bulk}, \tcode{execution::bulk_chunked}, and \tcode{execution::bulk_unchunked}}
36123616

36133617
\pnum
3614-
\tcode{bulk} runs a task repeatedly for every index in an index space.
3618+
\tcode{bulk}, \tcode{bulk_chunked}, and \tcode{bulk_unchunked}
3619+
run a task repeatedly for every index in an index space.
36153620

3616-
The name \tcode{bulk} denotes a pipeable sender adaptor object.
3617-
For subexpressions \tcode{sndr}, \tcode{shape}, and \tcode{f},
3618-
let \tcode{Shape} be \tcode{decltype(auto(shape))}.
3621+
\pnum
3622+
The names \tcode{bulk}, \tcode{bulk_chunked}, and \tcode{bulk_unchunked}
3623+
denote pipeable sender adaptor objects.
3624+
Let \placeholder{bulk-algo} be either
3625+
\tcode{bulk}, \tcode{bulk_chunked}, or \tcode{bulk_unchunked}.
3626+
For subexpressions \tcode{sndr}, \tcode{policy}, \tcode{shape}, and \tcode{f},
3627+
let
3628+
\tcode{Policy} be \tcode{remove_cvref_t<decltype(policy)>},
3629+
\tcode{Shape} be \tcode{decltype(auto(shape))}, and
3630+
\tcode{Func} be \tcode{decay_t<decltype((f))>}.
36193631
If
36203632
\begin{itemize}
36213633
\item
36223634
\tcode{decltype((sndr))} does not satisfy \libconcept{sender}, or
36233635
\item
3636+
\tcode{is_execution_policy_v<Policy>} is \tcode{false}, or
3637+
\item
36243638
\tcode{Shape} does not satisfy \libconcept{integral}, or
36253639
\item
3626-
\tcode{decltype((f))} does not satisfy \exposconcept{movable-value},
3640+
\tcode{Func} does not model \libconcept{copy_constructible},
36273641
\end{itemize}
3628-
\tcode{bulk(sndr, shape, f)} is ill-formed.
3642+
\tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)} is ill-formed.
36293643

36303644
\pnum
36313645
Otherwise,
3632-
the expression \tcode{bulk(sndr, shape, f)} is expression-equivalent to:
3646+
the expression \tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)}
3647+
is expression-equivalent to:
36333648

36343649
\begin{codeblock}
3635-
transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(bulk, @\exposid{product-type}@{shape, f}, sndr))
3650+
transform_sender(@\exposid{get-domain-early}@(sndr),
3651+
@\exposid{make-sender}@(@\placeholder{bulk-algo}@,
3652+
@\exposid{product-type}@<@\seebelow@, Shape, Func>{policy, shape, f},
3653+
sndr))
36363654
\end{codeblock}
36373655
except that \tcode{sndr} is evaluated only once.
36383656

3657+
\par
3658+
The first template argument of \exposid{product-type} is \tcode{Policy}
3659+
if \tcode{Policy} models \libconcept{copy_constructible}, and
3660+
\tcode{const Policy\&} otherwise.
3661+
3662+
\pnum
3663+
Let \tcode{sndr} and \tcode{env} be subexpressions such that
3664+
\tcode{Sndr} is \tcode{decltype((sndr))}.
3665+
If \tcode{\exposconcept{sender-for}<Sndr, bulk_t>} is \tcode{false}, then
3666+
the expression \tcode{bulk.transform_sender(sndr, env)} is ill-formed;
3667+
otherwise, it is equivalent to:
3668+
\begin{codeblock}
3669+
auto [_, data, child] = sndr;
3670+
auto& [policy, shape, f] = data;
3671+
auto new_f = [func=std::move(f)](Shape begin, Shape end, auto&&... vs)
3672+
noexcept(noexcept(f(begin, vs...))) {
3673+
while (begin != end) func(begin++, vs...);
3674+
}
3675+
return bulk_chunked(std::move(child), policy, shape, std::move(new_f));
3676+
\end{codeblock}
3677+
3678+
\begin{note}
3679+
This causes the \tcode{bulk(sndr, policy, shape, f)} sender to be
3680+
expressed in terms of \tcode{bulk_chunked(sndr, policy, shape, f)} when
3681+
it is connected to a receiver whose
3682+
execution domain does not customize \tcode{bulk}.
3683+
\end{note}
3684+
36393685
\pnum
36403686
The exposition-only class template \exposid{impls-for}\iref{exec.snd.general}
3641-
is specialized for \tcode{bulk_t} as follows:
3687+
is specialized for \tcode{bulk_chunked_t} as follows:
36423688
\begin{codeblock}
36433689
namespace std::execution {
36443690
template<>
3645-
struct @\exposid{impls-for}@<bulk_t> : @\exposid{default-impls}@ {
3691+
struct @\exposid{impls-for}@<bulk_chunked_t> : @\exposid{default-impls}@ {
36463692
static constexpr auto @\exposid{complete}@ = @\seebelow@;
36473693
};
36483694
}
36493695
\end{codeblock}
36503696

3697+
\par
3698+
The member \tcode{\exposid{impls-for}<bulk_chunked_t>::\exposid{complete}}
3699+
is initialized with a callable object equivalent to the following lambda:
3700+
\begin{codeblock}
3701+
[]<class Index, class State, class Rcvr, class Tag, class... Args>
3702+
(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
3703+
-> void requires @\seebelow@ {
3704+
if constexpr (@\libconcept{same_as}@<Tag, set_value_t>) {
3705+
auto& [policy, shape, f] = state;
3706+
constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...));
3707+
@\exposid{TRY-EVAL}@(rcvr, [&]() noexcept(nothrow) {
3708+
f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
3709+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
3710+
}());
3711+
} else {
3712+
Tag()(std::move(rcvr), std::forward<Args>(args)...);
3713+
}
3714+
}
3715+
\end{codeblock}
3716+
3717+
\par
3718+
The expression in the \grammarterm{requires-clause} of the lambda above is
3719+
\tcode{true} if and only
3720+
if \tcode{Tag} denotes a type other than \tcode{set_value_t} or
3721+
if the expression \tcode{f(auto(shape), auto(shape), args...)} is well-formed.
3722+
36513723
\pnum
3652-
The member \tcode{\exposid{impls-for}<bulk_t>::\exposid{complete}}
3724+
The exposition-only class template \exposid{impls-for}\iref{exec.snd.general}
3725+
is specialized for \tcode{bulk_unchunked_t} as follows:
3726+
\begin{codeblock}
3727+
namespace std::execution {
3728+
template<>
3729+
struct @\exposid{impls-for}@<bulk_unchunked_t> : @\exposid{default-impls}@ {
3730+
static constexpr auto @\exposid{complete}@ = @\seebelow@;
3731+
};
3732+
}
3733+
\end{codeblock}
3734+
3735+
\par
3736+
The member \tcode{\exposid{impls-for}<bulk_unchunked_t>::\exposid{complete}}
36533737
is initialized with a callable object equivalent to the following lambda:
36543738
\begin{codeblock}
36553739
[]<class Index, class State, class Rcvr, class Tag, class... Args>
3656-
(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void requires @\seebelow@ {
3740+
(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
3741+
-> void requires @\seebelow@ {
36573742
if constexpr (@\libconcept{same_as}@<Tag, set_value_t>) {
36583743
auto& [shape, f] = state;
36593744
constexpr bool nothrow = noexcept(f(auto(shape), args...));
@@ -3669,32 +3754,109 @@
36693754
}
36703755
\end{codeblock}
36713756

3672-
\pnum
3757+
\par
36733758
The expression in the \grammarterm{requires-clause} of the lambda above
36743759
is \tcode{true} if and only
36753760
if \tcode{Tag} denotes a type other than \tcode{set_value_t} or
36763761
if the expression \tcode{f(auto(shape), args...)} is well-formed.
36773762

36783763
\pnum
36793764
Let the subexpression \tcode{out_sndr} denote
3680-
the result of the invocation \tcode{bulk(sndr, shape, f)} or
3765+
the result of the invocation
3766+
\tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)} or
36813767
an object equal to such, and
36823768
let the subexpression \tcode{rcvr} denote a receiver
36833769
such that the expression \tcode{connect(out_sndr, rcvr)} is well-formed.
36843770
The expression \tcode{connect(out_sndr, rcvr)} has undefined behavior
36853771
unless it creates an asynchronous operation\iref{exec.async.ops} that,
3686-
when started,
3772+
when started:
3773+
36873774
\begin{itemize}
36883775
\item
3689-
on a value completion operation,
3690-
invokes \tcode{f(i, args...)}
3691-
for every \tcode{i} of type \tcode{Shape} in \range{\tcode{0}}{\tcode{shape}},
3692-
where \tcode{args} is a pack of lvalue subexpressions
3693-
referring to the value completion result datums of the input sender, and
3776+
If \tcode{sndr} has a successful completion, where
3777+
\tcode{args} is a pack of lvalue subexpressions
3778+
referring to the value completion result datums of \tcode{sndr}, or
3779+
decayed copies of those values if they model copy_constructible, then:
3780+
3781+
\begin{itemize}
3782+
\item
3783+
If \tcode{out_sndr} also completes successfully, then:
3784+
3785+
\begin{itemize}
3786+
\item
3787+
for \tcode{bulk},
3788+
invokes \tcode{f($i$, args...)} for every $i$ of type \tcode{Shape}
3789+
from \tcode{0} to \tcode{shape};
3790+
3791+
\item
3792+
for \tcode{bulk_unchunked},
3793+
invokes \tcode{f($i$, args...)} for every $i$ of type \tcode{Shape}
3794+
from \tcode{0} to \tcode{shape};
3795+
3796+
\recommended
3797+
The underlying scheduler should execute each iteration
3798+
on a distinct execution agent.
3799+
3800+
\item
3801+
for \tcode{bulk_chunked},
3802+
invokes \tcode{f($b$, $e$, args...)} zero or more times
3803+
with pairs of $b$ and $e$ of type \tcode{Shape}
3804+
in range \range{\tcode{0}}{\tcode{shape}},
3805+
such that $b < e$ and
3806+
for every $i$ of type \tcode{Shape} from \tcode{0} to \tcode{shape},
3807+
there is exactly one invocation with a pair $b$ and $e$,
3808+
such that $i$ is in the range \range{$b$}{$e$}.
3809+
\end{itemize}
3810+
3811+
\item
3812+
If \tcode{out_sndr} completes with \tcode{set_error(rcvr, eptr)}, then
3813+
the asynchronous operation may invoke a subset of
3814+
the invocations of \tcode{f}
3815+
before the error completion handler is called, and
3816+
\tcode{eptr} is an \tcode{exception_ptr} containing either:
3817+
\begin{itemize}
3818+
\item
3819+
an exception thrown by an invocation of \tcode{f}, or
3820+
\item
3821+
a \tcode{bad_alloc} exception if
3822+
the implementation fails to allocate required resources, or
3823+
\item
3824+
an exception derived from \tcode{runtime_error}.
3825+
\end{itemize}
3826+
3827+
\item
3828+
If \tcode{out_sndr} completes with \tcode{set_stopped(rcvr)}, then
3829+
the asynchronous operation may invoke a subset of
3830+
the invocations of \tcode{f}
3831+
before the stopped completion handler.
3832+
\end{itemize}
3833+
36943834
\item
3695-
propagates all completion operations sent by \tcode{sndr}.
3835+
If \tcode{sndr} does not complete with \tcode{set_value}, then
3836+
the completion is forwarded to \tcode{recv}.
3837+
3838+
\item
3839+
For \placeholder{bulk-algo},
3840+
the parameter \tcode{policy} describes
3841+
the manner in which
3842+
the execution of the asynchronous operations corresponding to these algorithms
3843+
may be parallelized and
3844+
the manner in which
3845+
%%FIXME: Should this be "apply to f"?
3846+
they apply \tcode{f}.
3847+
Permissions and requirements
3848+
on parallel algorithm element access functions\iref{algorithms.parallel.exec}
3849+
apply to \tcode{f}.
36963850
\end{itemize}
36973851

3852+
\pnum
3853+
\begin{note}
3854+
The asynchronous operation corresponding to
3855+
\tcode{\placeholder{bulk-algo}(sndr, policy, shape, f)}
3856+
can complete with \tcode{set_stopped} if cancellation is requested or
3857+
ignore cancellation requests.
3858+
\end{note}
3859+
36983860
\rSec3[exec.split]{\tcode{execution::split}}
36993861

37003862
\pnum

0 commit comments

Comments
 (0)