Skip to content

Commit 4956880

Browse files
jensmaurertkoeppe
authored andcommitted
P3927R2 task_scheduler support for parallel bulk execution
Fixes NB US 238-368 (C++26 CD). Editorial note: * [exec.task.scheduler] Change "Initialize sch_ with" to "Initializes sch_ with:", and put the (long) expression in a code block.
1 parent ca635f1 commit 4956880

1 file changed

Lines changed: 186 additions & 63 deletions

File tree

source/exec.tex

Lines changed: 186 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7447,33 +7447,31 @@
74477447
\begin{codeblock}
74487448
namespace std::execution {
74497449
class @\libglobal{task_scheduler}@ {
7450-
class @\exposidnc{ts-sender}@; // \expos
7450+
class @\exposid{ts-domain}@; // \expos
74517451

7452-
template<@\libconcept{receiver}@ R>
7453-
class @\exposidnc{state}@; // \expos
7452+
template<@\libconcept{scheduler}@ Sch>
7453+
class @\exposid{backend-for}@; // \expos
74547454

74557455
public:
74567456
using scheduler_concept = scheduler_t;
74577457

74587458
template<class Sch, class Allocator = allocator<void>>
7459-
requires (!@\libconcept{same_as}@<task_scheduler, remove_cvref_t<Sch>>)
7460-
&& @\libconcept{scheduler}@<Sch>
7459+
requires (!@\libconcept{same_as}@<task_scheduler, remove_cvref_t<Sch>>) && @\libconcept{scheduler}@<Sch>
74617460
explicit task_scheduler(Sch&& sch, Allocator alloc = {});
74627461

74637462
task_scheduler(const task_scheduler&) = default;
74647463
task_scheduler& operator=(const task_scheduler&) = default;
74657464

7466-
@\exposid{ts-sender}@ schedule();
7465+
@\seebelow@ schedule();
74677466

7468-
friend bool operator==(const task_scheduler& lhs, const task_scheduler& rhs)
7469-
noexcept;
7467+
friend bool operator==(const task_scheduler& lhs, const task_scheduler& rhs) noexcept;
74707468
template<class Sch>
7471-
requires (!@\libconcept{same_as}@<task_scheduler, Sch>)
7472-
&& @\libconcept{scheduler}@<Sch>
7469+
requires (!@\libconcept{same_as}@<task_scheduler, Sch>) && @\libconcept{scheduler}@<Sch>
74737470
friend bool operator==(const task_scheduler& lhs, const Sch& rhs) noexcept;
74747471

74757472
private:
7476-
shared_ptr<void> @\exposidnc{sch_}@; // \expos
7473+
// see \ref{exec.sysctxrepl.psb}
7474+
shared_ptr<system_context_replaceability::parallel_scheduler_backend> @\exposidnc{sch_}@; // \expos
74777475
};
74787476
}
74797477
\end{codeblock}
@@ -7484,6 +7482,10 @@
74847482
Given an object \tcode{s} of type \tcode{task_scheduler}, let
74857483
\tcode{\exposid{SCHED}(s)} be the object
74867484
pointed to by the pointer owned by \tcode{s.\exposid{sch_}}.
7485+
The expression \tcode{get_forward_progress_guarantee(s)} is equivalent to
7486+
\tcode{get_forward_progress_guarantee(\exposid{SCHED}(s))}.
7487+
The expression \tcode{get_completion_domain<set_value_t>(s)} is equivalent to
7488+
\tcode{task_scheduler::\exposid{ts-domain}()}.
74877489

74887490
\indexlibraryctor{task_scheduler}
74897491
\begin{itemdecl}
@@ -7498,8 +7500,10 @@
74987500

74997501
\pnum
75007502
\effects
7501-
Initialize \exposid{sch_} with
7502-
\tcode{allocate_shared<remove_cvref_t<Sch>>(alloc,\brk{} std::forward<Sch>\brk{}(sch))}.
7503+
Initializes \exposid{sch_} with:
7504+
\begin{codeblock}
7505+
allocate_shared<@\exposid{backend-for}@<remove_cvref_t<Sch>>>(alloc, std::forward<Sch>(sch))
7506+
\end{codeblock}
75037507

75047508
\pnum
75057509
\recommended
@@ -7508,22 +7512,10 @@
75087512

75097513
\pnum
75107514
\remarks
7511-
Any allocations performed by construction of \exposid{ts-sender} or
7512-
\exposid{state} objects resulting from calls on \tcode{*this} are
7515+
Any allocations performed by calls on \tcode{*this} are
75137516
performed using a copy of \tcode{alloc}.
75147517
\end{itemdescr}
75157518

7516-
\indexlibrarymember{scheduler}{task_scheduler}%
7517-
\begin{itemdecl}
7518-
@\exposid{ts-sender}@ schedule();
7519-
\end{itemdecl}
7520-
\begin{itemdescr}
7521-
\pnum
7522-
\effects
7523-
Returns an object of type \exposid{ts-sender} containing a sender
7524-
initialized with \tcode{sched\-ule(\brk{}\exposid{SCHED}\brk{}(*this))}.
7525-
\end{itemdescr}
7526-
75277519
\indexlibrarymember{operator==}{task_scheduler}%
75287520
\begin{itemdecl}
75297521
bool operator==(const task_scheduler& lhs, const task_scheduler& rhs) noexcept;
@@ -7549,73 +7541,204 @@
75497541
\end{itemdescr}
75507542

75517543
\pnum
7544+
For an lvalue \tcode{r} of a type derived from \tcode{receiver_proxy},
7545+
let \tcode{\exposid{WRAP-RCVR}(r)} be an object of a type
7546+
that models \libconcept{receiver} and
7547+
whose completion handlers result in
7548+
invoking the corresponding completion handlers of \tcode{r}.
75527549
\begin{codeblock}
75537550
namespace std::execution {
7554-
class task_scheduler::@\exposidnc{ts-sender}@ { // \expos
7551+
template<@\libconcept{scheduler}@ Sch>
7552+
class task_scheduler::@\exposid{backend-for}@
7553+
: public system_context_replaceability::parallel_scheduler_backend { // \expos
75557554
public:
7556-
using sender_concept = sender_t;
7555+
explicit @\exposid{backend-for}@(Sch sch) : @\exposid{sched_}@(std::move(sch)) {}
75577556

7558-
template<@\libconcept{receiver}@ Rcvr>
7559-
@\exposid{state}@<Rcvr> connect(Rcvr&& rcvr) &&;
7557+
void schedule(receiver_proxy& r, span<byte> s) noexcept override;
7558+
void schedule_bulk_chunked(size_t shape, bulk_item_receiver_proxy& r,
7559+
span<byte> s) noexcept override;
7560+
void schedule_bulk_unchunked(size_t shape, bulk_item_receiver_proxy& r,
7561+
span<byte> s) noexcept override;
7562+
7563+
private:
7564+
Sch @\exposid{sched_}@;
75607565
};
75617566
}
75627567
\end{codeblock}
7563-
\exposid{ts-sender} is an exposition-only class that models
7564-
\libconcept{sender}\iref{exec.snd} and for which
7565-
\tcode{completion_signatures_of_t<\exposid{ts-sender}, E>} denotes
7566-
\tcode{completion_signatures<set_value_t()>}
7567-
if \tcode{unstoppable_token<stop_token_of_t<E>>} is \tcode{true}, and
7568-
otherwise \tcode{completion_signatures<set_value_t(), set_stopped_t()>}.
75697568

75707569
\pnum
7571-
Let \tcode{\placeholder{sch}} be an object of type \tcode{task_scheduler}
7572-
and let \tcode{sndr} be an object of type \exposid{ts-sender} obtained
7573-
from \tcode{schedule(\placeholder{sch})}.
7574-
Then \tcode{get_completion_scheduler<set_value_t>(get_env(sndr)) == \placeholder{sch}}
7575-
is \tcode{true}.
7576-
The object \tcode{\exposid{SENDER}(sndr)} is the sender object contained by
7577-
\tcode{sndr} or an object move constructed from it.
7570+
Let \tcode{env} be a pack of subexpressions, and
7571+
let \exposid{just-sndr-like} be a sender
7572+
whose only value completion signature is \tcode{set_value_t()} and
7573+
for which the expression
7574+
\tcode{get_completion_scheduler<set_value_t>(get_env(\exposid{just-sndr-like)}, env...)}
7575+
is expression-equivalent to
7576+
\tcode{get_completion_scheduler<set_value_t>(\exposid{sched_}, env...)}.
75787577

7579-
\indexlibrarymember{connect}{task_scheduler::\exposid{ts-sender}}%
75807578
\begin{itemdecl}
7581-
template<@\libconcept{receiver}@ Rcvr>
7582-
@\exposid{state}@<Rcvr> connect(Rcvr&& rcvr) &&;
7579+
void schedule(receiver_proxy& r, span<byte> s) noexcept override;
7580+
\end{itemdecl}
7581+
7582+
\begin{itemdescr}
7583+
\pnum
7584+
\effects
7585+
Constructs an operation state \tcode{os}
7586+
with \tcode{connect(schedule(\exposid{sched_}), \exposid{WRAP-RCVR}(r))} and
7587+
calls \tcode{start(os)}.
7588+
\end{itemdescr}
7589+
7590+
\begin{itemdecl}
7591+
void schedule_bulk_chunked(size_t shape, bulk_item_receiver_proxy& r,
7592+
span<byte> s) noexcept override;
75837593
\end{itemdecl}
7594+
75847595
\begin{itemdescr}
75857596
\pnum
75867597
\effects
7587-
Let \tcode{\placeholder{r}} be an object of a type that models \libconcept{receiver}
7588-
and whose completion handlers result in invoking the corresponding
7589-
completion handlers of \tcode{rcvr} or copy thereof.
7590-
Returns an object of type \tcode{\exposid{state}<Rcvr>} containing
7591-
an operation state object initialized with \tcode{connect(\exposid{SENDER}(*this),
7592-
std::move(\placeholder{r}))}.
7598+
Let \tcode{chunk_size} be an integer less than or equal to \tcode{shape},
7599+
let \tcode{num_chunks} be \tcode{(shape + chunk_size - 1) / chunk_size}, and
7600+
let \tcode{fn} be a function object such that
7601+
for an integer \tcode{i},
7602+
\tcode{fn(i)} calls \tcode{r.execute(i * chunk_size, m)},
7603+
where \tcode{m} is the lesser of \tcode{(i + 1) * chunk_size} and \tcode{shape}.
7604+
Constructs an operation state \tcode{os} as if with
7605+
\begin{codeblock}
7606+
connect(bulk(@\exposid{just-sndr-like}@, par, num_chunks, fn), @\exposid{WRAP-RCVR}@(r))
7607+
\end{codeblock}
7608+
and calls \tcode{start(os)}.
75937609
\end{itemdescr}
75947610

7611+
\begin{itemdecl}
7612+
void schedule_bulk_unchunked(size_t shape, bulk_item_receiver_proxy& r,
7613+
span<byte> s) noexcept override;
7614+
\end{itemdecl}
7615+
7616+
\begin{itemdescr}
75957617
\pnum
7618+
\effects
7619+
Let \tcode{fn} be a function object such that
7620+
for an integer \tcode{i},
7621+
\tcode{fn(i)} is equivalent to \tcode{r.execute(i, i + 1)}.
7622+
Constructs an operation state \tcode{os} as if with
7623+
\begin{codeblock}
7624+
connect(bulk(@\exposid{just-sndr-like}@, par, shape, fn), @\exposid{WRAP-RCVR}@(r))
7625+
\end{codeblock}
7626+
and calls \tcode{start(os)}.
7627+
\end{itemdescr}
7628+
7629+
\begin{itemdecl}
7630+
@\seebelow@ schedule();
7631+
\end{itemdecl}
7632+
7633+
\begin{itemdescr}
7634+
\pnum
7635+
\returns
7636+
A prvalue \exposid{ts-sndr} whose type models \libconcept{sender} such that:
7637+
\begin{itemize}
7638+
\item
7639+
\tcode{get_completion_scheduler<set_value_t>(get_env(\exposid{ts-sndr}))}
7640+
is equal to \tcode{*this.}
7641+
\item
7642+
\tcode{get_completion_domain<set_value_t>(get_env(\exposid{ts-sndr}))}
7643+
is expression-equivalent to \tcode{\exposid{ts-do\-main}()}.
7644+
\item
7645+
If a receiver \tcode{rcvr} is connected to \exposid{ts-sndr} and
7646+
the resulting operation state is started,
7647+
calls \tcode{\exposid{sch_}->schedule(r, s)}, where
7648+
\begin{itemize}
7649+
\item
7650+
\tcode{r} is a proxy for \tcode{rcvr} with base
7651+
\tcode{system_context_replaceability::receiver_proxy}\iref{exec.par.scheduler}
7652+
and
7653+
\item
7654+
\tcode{s} is a preallocated backend storage for \tcode{r}.
7655+
\end{itemize}
7656+
\item
7657+
For any type \tcode{E},
7658+
\tcode{completion_signatures_of_t<decltype(\exposid{ts-sndr}), E>} denotes
7659+
\tcode{completion_signatures<set_value_t()>} if
7660+
\tcode{\libconcept{unstoppable_token}<stop_token_of_t<E>>} is \tcode{true}, and
7661+
otherwise \tcode{completion_signatures<set_value_t(), set_stopped_t()>}.
7662+
\end{itemize}
7663+
\end{itemdescr}
7664+
75967665
\begin{codeblock}
75977666
namespace std::execution {
7598-
template<@\libconcept{receiver}@ R>
7599-
class task_scheduler::@\exposidnc{state}@ { // \expos
7667+
class task_scheduler::@\exposid{ts-domain}@ : public default_domain { // \expos
76007668
public:
7601-
using operation_state_concept = operation_state_t;
7602-
7603-
void start() & noexcept;
7669+
template<class BulkSndr, class Env>
7670+
static constexpr auto transform_sender(set_value_t, BulkSndr&& bulk_sndr, const Env& env)
7671+
noexcept(@\seebelow@);
76047672
};
76057673
}
76067674
\end{codeblock}
7607-
\exposid{state} is an exposition-only class template whose
7608-
specializations model \libconcept{operation_state}\iref{exec.opstate}.
76097675

7610-
\indexlibrarymember{start}{task_scheduler::\exposid{state}}%
76117676
\begin{itemdecl}
7612-
void start() & noexcept;
7677+
template<class BulkSndr, class Env>
7678+
static constexpr auto transform_sender(BulkSndr&& bulk_sndr, const Env& env)
7679+
noexcept(is_nothrow_constructible_v<decay_t<BulkSndr>, BulkSndr>);
76137680
\end{itemdecl}
7681+
76147682
\begin{itemdescr}
7683+
\pnum
7684+
\constraints
7685+
\begin{itemize}
7686+
\item
7687+
\tcode{\libconcept{sender_in}<BulkSndr, Env>} is \tcode{true},
7688+
\item
7689+
\tcode{auto(std::forward<BulkSndr>(bulk_sndr))} is well-formed, and
7690+
\item
7691+
either
7692+
\begin{itemize}
7693+
\item
7694+
\tcode{\exposconcept{sender-for}<BulkSndr, bulk_chunked_t>} or
7695+
\item
7696+
\tcode{\exposconcept{sender-for}<BulkSndr, bulk_un\-chunked_t>}
7697+
\end{itemize}
7698+
is \tcode{true}.
7699+
\end{itemize}
7700+
76157701
\pnum
76167702
\effects
7617-
Equivalent to \tcode{start(st)} where \tcode{st} is the operation
7618-
state object contained by \tcode{*this}.
7703+
Equivalent to:
7704+
\begin{codeblock}
7705+
auto& [_, data, child] = bulk_sndr;
7706+
auto& [_, shape, fn] = data;
7707+
auto sch = @\exposidnc{call-with-default}@(get_completion_scheduler<set_value_t>,
7708+
@\exposidnc{not-a-scheduler}@(), get_env(child), @\exposidnc{FWD-ENV}@(env));
7709+
return @$e$@;
7710+
\end{codeblock}
7711+
where $e$ is \tcode{\exposid{not-a-sender}()}
7712+
if the type of \tcode{sch} is not \tcode{task_scheduler};
7713+
otherwise, it is a prvalue whose type models \libconcept{sender} such that,
7714+
if it is connected to a receiver \tcode{rcvr} and
7715+
the resulting operation state is started,
7716+
\tcode{child} is connected to an unspecified receiver \tcode{R} and started.
7717+
The expression \tcode{get_env(R)}
7718+
is expression-equivalent to \tcode{\exposid{FWD-ENV}(get_env(\exposid{rcvr-copy}))},
7719+
where \exposid{rcvr-copy} is an lvalue subexpression
7720+
designating an object decay-copied from \tcode{rcvr}.
7721+
7722+
If \tcode{child} completes with an error or a stopped completion,
7723+
the completion operation is forwarded unchanged to \tcode{rcvr}.
7724+
Otherwise, let \tcode{args} be a pack of lvalue subexpressions
7725+
designating objects decay-copied from the value result datums.
7726+
Then:
7727+
\begin{itemize}
7728+
\item
7729+
If \tcode{bulk_sndr} was the result of the evaluation of
7730+
an expression equivalent to \tcode{bulk_chunked(child, policy, shape, fn)} or
7731+
a copy of such,
7732+
then \tcode{\exposid{sch_}->schedule_bulk_chunked(shape, r, s)} is called
7733+
where \tcode{r} is a bulk chunked proxy\iref{exec.par.scheduler}
7734+
for \tcode{rcvr} with callable \tcode{fn} and arguments \tcode{args}, and
7735+
\tcode{s} is a preallocated backend storage for \tcode{r}.
7736+
\item
7737+
Otherwise, calls \tcode{\exposid{sch_}->schedule_bulk_unchunked(shape, r, s)}
7738+
where \tcode{r} is a bulk unchunked proxy for \tcode{rcvr}
7739+
with callable \tcode{fn} and arguments \tcode{args}, and
7740+
\tcode{s} is a preallocated backend storage for \tcode{r}.
7741+
\end{itemize}
76197742
\end{itemdescr}
76207743

76217744
\rSec2[exec.task]{\tcode{execution::task}}

0 commit comments

Comments
 (0)