Skip to content

P3682R0 Remove std::execution::split #7978

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
373 changes: 0 additions & 373 deletions source/exec.tex
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,6 @@
struct @\libglobal{let_error_t}@ { @\unspec@ };
struct @\libglobal{let_stopped_t}@ { @\unspec@ };
struct @\libglobal{bulk_t}@ { @\unspec@ };
struct @\libglobal{split_t}@ { @\unspec@ };
struct @\libglobal{when_all_t}@ { @\unspec@ };
struct @\libglobal{when_all_with_variant_t}@ { @\unspec@ };
struct @\libglobal{into_variant_t}@ { @\unspec@ };
Expand All @@ -647,7 +646,6 @@
inline constexpr let_error_t @\libglobal{let_error}@{};
inline constexpr let_stopped_t @\libglobal{let_stopped}@{};
inline constexpr bulk_t @\libglobal{bulk}@{};
inline constexpr split_t @\libglobal{split}@{};
inline constexpr when_all_t @\libglobal{when_all}@{};
inline constexpr when_all_with_variant_t @\libglobal{when_all_with_variant}@{};
inline constexpr into_variant_t @\libglobal{into_variant}@{};
Expand Down Expand Up @@ -3695,377 +3693,6 @@
propagates all completion operations sent by \tcode{sndr}.
\end{itemize}

\rSec3[exec.split]{\tcode{execution::split}}

\pnum
\tcode{split} adapts an arbitrary sender
into a sender that can be connected multiple times.

\pnum
Let \exposid{split-env} be the type of an environment
such that, given an instance \tcode{env},
the expression \tcode{get_stop_token(env)} is well-formed and
has type \tcode{inplace_stop_token.}

\pnum
The name \tcode{split} denotes a pipeable sender adaptor object.
For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))}.
If \tcode{\libconcept{sender_in}<Sndr, \exposid{split-env}>} is \tcode{false},
\tcode{split(sndr)} is ill-formed.

\pnum
Otherwise, the expression \tcode{split(sndr)} is expression-equivalent to:
\begin{codeblock}
transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(split, {}, sndr))
\end{codeblock}
except that \tcode{sndr} is evaluated only once.
\begin{note}
The default implementation of \tcode{transform_sender}
will have the effect of connecting the sender to a receiver.
It will return a sender with a different tag type.
\end{note}

\pnum
Let \exposid{local-state} denote the following exposition-only class template:

\begin{codeblock}
namespace std::execution {
struct @\exposid{local-state-base}@ { // \expos
virtual ~@\exposid{local-state-base}@() = default;
virtual void @\exposid{notify}@() noexcept = 0; // \expos
};

template<class Sndr, class Rcvr>
struct @\exposid{local-state}@ : @\exposid{local-state-base}@ { // \expos
using @\exposid{on-stop-callback}@ = // \expos
stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, @\exposid{on-stop-request}@>;

@\exposid{local-state}@(Sndr&& sndr, Rcvr& rcvr) noexcept;
~@\exposid{local-state}@();

void @\exposid{notify}@() noexcept override;

private:
optional<@\exposid{on-stop-callback}@> @\exposid{on_stop}@; // \expos
@\exposid{shared-state}@<Sndr>* @\exposid{sh_state}@; // \expos
Rcvr* @\exposid{rcvr}@; // \expos
};
}
\end{codeblock}

\begin{itemdecl}
@\exposid{local-state}@(Sndr&& sndr, Rcvr& rcvr) noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\effects
Equivalent to:
\begin{codeblock}
auto& [_, data, _] = sndr;
this->@\exposid{sh_state}@ = data.sh_state.get();
this->@\exposid{sh_state}@->@\exposid{inc-ref}@();
this->@\exposid{rcvr}@ = addressof(rcvr);
\end{codeblock}
\end{itemdescr}

\begin{itemdecl}
~@\exposid{local-state}@();
\end{itemdecl}

\begin{itemdescr}
\pnum
\effects
Equivalent to:
\begin{codeblock}
sh_state->@\exposid{dec-ref}@();
\end{codeblock}
\end{itemdescr}

\begin{itemdecl}
void @\exposid{notify}@() noexcept override;
\end{itemdecl}

\begin{itemdescr}
\pnum
\effects
Equivalent to:
\begin{codeblock}
@\exposid{on_stop}@.reset();
visit(
[this](const auto& tupl) noexcept -> void {
apply(
[this](auto tag, const auto&... args) noexcept -> void {
tag(std::move(*@\exposid{rcvr}@), args...);
},
tupl);
},
@\exposid{sh_state}@->result);
\end{codeblock}
\end{itemdescr}

\pnum
Let \exposid{split-receiver} denote
the following exposition-only class template:
\begin{codeblock}
namespace std::execution {
template<class Sndr>
struct @\exposid{split-receiver}@ { // \expos
using receiver_concept = receiver_t;

template<class Tag, class... Args>
void @\exposid{complete}@(Tag, Args&&... args) noexcept { // \expos
using tuple_t = @\exposid{decayed-tuple}@<Tag, Args...>;
try {
@\exposid{sh_state}@->result.template emplace<tuple_t>(Tag(), std::forward<Args>(args)...);
} catch (...) {
using tuple_t = tuple<set_error_t, exception_ptr>;
@\exposid{sh_state}@->result.template emplace<tuple_t>(set_error, current_exception());
}
@\exposid{sh_state}@->notify();
}

template<class... Args>
void set_value(Args&&... args) && noexcept {
@\exposid{complete}@(execution::set_value, std::forward<Args>(args)...);
}

template<class Error>
void set_error(Error&& err) && noexcept {
@\exposid{complete}@(execution::set_error, std::forward<Error>(err));
}

void set_stopped() && noexcept {
@\exposid{complete}@(execution::set_stopped);
}

struct @\exposid{env}@ { // \expos
@\exposid{shared-state}@<Sndr>* @\exposid{sh-state}@; // \expos

inplace_stop_token query(get_stop_token_t) const noexcept {
return @\exposid{sh-state}@->stop_src.get_token();
}
};

@\exposid{env}@ get_env() const noexcept {
return @\exposid{env}@{@\exposid{sh_state}@};
}

@\exposid{shared-state}@<Sndr>* @\exposid{sh_state}@; // \expos
};
}
\end{codeblock}

\pnum
Let \exposid{shared-state} denote the following exposition-only class template:
\begin{codeblock}
namespace std::execution {
template<class Sndr>
struct @\exposid{shared-state}@ {
using @\exposid{variant-type}@ = @\seebelow@; // \expos
using @\exposid{state-list-type}@ = @\seebelow@; // \expos

explicit @\exposid{shared-state}@(Sndr&& sndr);

void @\exposid{start-op}@() noexcept; // \expos
void @\exposid{notify}@() noexcept; // \expos
void @\exposid{inc-ref}@() noexcept; // \expos
void @\exposid{dec-ref}@() noexcept; // \expos

inplace_stop_source @\exposid{stop_src}@{}; // \expos
@\exposid{variant-type}@ @\exposid{result}@{}; // \expos
@\exposid{state-list-type}@ @\exposid{waiting_states}@; // \expos
atomic<bool> @\exposid{completed}@{false}; // \expos
atomic<size_t> @\exposid{ref_count}@{1}; // \expos
connect_result_t<Sndr, @\exposid{split-receiver}@<Sndr>> @\exposid{op_state}@; // \expos
};
}
\end{codeblock}

\pnum
Let \tcode{Sigs} be a pack of the arguments
to the \tcode{completion_signatures} specialization
named by \tcode{completion_signatures_of_t<Sndr>}.
For type \tcode{Tag} and pack \tcode{Args},
let \exposid{as-tuple} be an alias template
such that \tcode{\exposid{as-tuple}<Tag(Args...)>} denotes
the type \tcode{\exposid{decayed-tuple}<Tag, Args...>}.
Then \exposid{variant-type} denotes the type
\begin{codeblock}
variant<tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, @\exposid{as-tuple}@<Sigs>...>
\end{codeblock}
but with duplicate types removed.

\pnum
Let \exposid{state-list-type} be a type
that stores a list of pointers to \exposid{local-state-base} objects and
that permits atomic insertion.

\begin{itemdecl}
explicit @\exposid{shared-state}@(Sndr&& sndr);
\end{itemdecl}

\begin{itemdescr}
\pnum
\effects
Initializes \exposid{op_state} with the result of
\tcode{connect(std::forward<Sndr>(sndr), \exposid{split-re\-ceiver}\{this\})}.

\pnum
\ensures
\exposid{waiting_states} is empty, and \exposid{completed} is \tcode{false}.
\end{itemdescr}

\begin{itemdecl}
void @\exposid{start-op}@() noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\effects
Evaluates \tcode{\exposid{inc-ref}()}.
If \tcode{stop_src.stop_requested()} is \tcode{true},
evaluates \tcode{\exposid{notify}()};
otherwise, evaluates \tcode{start(\exposid{op_state})}.
\end{itemdescr}

\begin{itemdecl}
void @\exposid{notify}@() noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\effects
Atomically does the following:
\begin{itemize}
\item
Sets \tcode{completed} to \tcode{true}, and
\item
Exchanges \tcode{waiting_states} with an empty list,
storing the old value in a local \tcode{prior_states}.
\end{itemize}
Then, for each pointer \tcode{p} in \tcode{prior_states},
evaluates \tcode{p->\exposid{notify}()}.
Finally, evaluates \tcode{\exposid{dec-ref}()}.
\end{itemdescr}

\begin{itemdecl}
void @\exposid{inc-ref}@() noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\effects
Increments \exposid{ref_count}.
\end{itemdescr}

\begin{itemdecl}
void @\exposid{dec-ref}@() noexcept;
\end{itemdecl}

\begin{itemdescr}
\pnum
\effects
Decrements \exposid{ref_count}.
If the new value of \exposid{ref_count} is \tcode{0},
calls \tcode{delete this}.

\pnum
\sync
If an evaluation of \tcode{\exposid{dec-ref}()} does not
decrement the \tcode{ref_count} to \tcode{0} then
synchronizes with the evaluation of \tcode{dec-ref()}
that decrements \tcode{ref_count} to \tcode{0}.
\end{itemdescr}

\pnum
Let \exposid{split-impl-tag} be an empty exposition-only class type.
Given an expression \tcode{sndr},
the expression \tcode{split.transform_sender(sndr)} is equivalent to:
\begin{codeblock}
auto&& [tag, _, child] = sndr;
auto* sh_state = new @\exposid{shared-state}@{std::forward_like<decltype((sndr))>(child)};
return @\exposid{make-sender}@(@\exposid{split-impl-tag}@(), @\exposid{shared-wrapper}@{sh_state, tag});
\end{codeblock}
where \exposid{shared-wrapper} is an exposition-only class
that manages the reference count of the \exposid{shared-state} object
pointed to by sh_state.
\exposid{shared-wrapper} models \libconcept{copyable}
with move operations nulling out the moved-from object,
copy operations incrementing the reference count
by calling \tcode{sh_state->\exposid{inc-ref}()}, and
assignment operations performing a copy-and-swap operation.
The destructor has no effect if sh_state is null;
otherwise, it decrements the reference count
by evaluating \tcode{sh_state->\exposid{dec-ref}()}.

\pnum
The exposition-only class template \exposid{impls-for}\iref{exec.snd.general}
is specialized for \exposid{split-impl-tag} as follows:
\begin{codeblock}
namespace std::execution {
template<>
struct @\exposid{impls-for}@<@\exposid{split-impl-tag}@> : @\exposid{default-impls}@ {
static constexpr auto @\exposid{get-state}@ = @\seebelow@;
static constexpr auto @\exposid{start}@ = @\seebelow@;
};
}
\end{codeblock}

\pnum
The member
\tcode{\exposid{impls-for}<\exposid{split-impl-tag}>::\exposid{get-state}}
is initialized with a callable object equivalent to
the following lambda expression:
\begin{codeblock}
[]<class Sndr>(Sndr&& sndr, auto& rcvr) noexcept {
return @\exposid{local-state}@{std::forward<Sndr>(sndr), rcvr};
}
\end{codeblock}

\pnum
The member
\tcode{\exposid{impls-for}<\exposid{split-impl-tag}>::\exposid{start}}
is initialized with a callable object
that has a function call operator equivalent to the following:
\begin{codeblock}
template<class Sndr, class Rcvr>
void operator()(@\exposid{local-state}@<Sndr, Rcvr>& state, Rcvr& rcvr) const noexcept;
\end{codeblock}

\effects
If \tcode{state.\exposid{sh_state}->\exposid{completed}} is \tcode{true},
evaluates \tcode{state.\exposid{notify}()} and returns.
Otherwise, does the following in order:
\begin{itemize}
\item
Evaluates
\begin{codeblock}
state.@\exposid{on_stop}@.emplace(
get_stop_token(get_env(rcvr)),
@\exposid{on-stop-request}@{state.@\exposid{sh_state}@->@\exposid{stop_src}@});
\end{codeblock}
\item
Then atomically does the following:
\begin{itemize}
\item
Reads the value \tcode{c} of
\tcode{state.\exposid{sh_state}->\exposid{completed}}, and
\item
Inserts \tcode{addressof(state)} into
\tcode{state.\exposid{sh_state}->\exposid{waiting_states}}
if \tcode{c} is \tcode{false}.
\end{itemize}
\item
If \tcode{c} is \tcode{true},
calls \tcode{state.\exposid{notify}()} and returns.
\item
Otherwise,
if \tcode{addressof(state)} is the first item added to
\tcode{state.\exposid{sh_state}->\exposid{waiting_states}},
evaluates \tcode{state.\exposid{sh_state}->\exposid{start-op}()}.
\end{itemize}

\rSec3[exec.when.all]{\tcode{execution::when_all}}

\pnum
Expand Down