|
3574 | 3574 | propagates all completion operations sent by \tcode{sndr}.
|
3575 | 3575 | \end{itemize}
|
3576 | 3576 |
|
| 3577 | +\rSec3[exec.split]{\tcode{execution::split}} |
| 3578 | + |
| 3579 | +\pnum |
| 3580 | +\tcode{split} adapts an arbitrary sender |
| 3581 | +into a sender that can be connected multiple times. |
| 3582 | + |
| 3583 | +\pnum |
| 3584 | +Let \exposid{split-env} be the type of an environment |
| 3585 | +such that, given an instance \tcode{env}, |
| 3586 | +the expression \tcode{get_stop_token(env)} is well-formed and |
| 3587 | +has type \tcode{inplace_stop_token.} |
| 3588 | + |
| 3589 | +\pnum |
| 3590 | +The name \tcode{split} denotes a pipeable sender adaptor object. |
| 3591 | +For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))}. |
| 3592 | +If \tcode{\libconcept{sender_in}<Sndr, \exposid{split-env}>} is \tcode{false}, |
| 3593 | +\tcode{split(sndr)} is ill-formed. |
| 3594 | + |
| 3595 | +\pnum |
| 3596 | +Otherwise, the expression \tcode{split(sndr)} is expression-equivalent to: |
| 3597 | +\begin{codeblock} |
| 3598 | +transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(split, {}, sndr)) |
| 3599 | +\end{codeblock} |
| 3600 | +except that \tcode{sndr} is evaluated only once. |
| 3601 | +\begin{note} |
| 3602 | +The default implementation of \tcode{transform_sender} |
| 3603 | +will have the effect of connecting the sender to a receiver. |
| 3604 | +It will return a sender with a different tag type. |
| 3605 | +\end{note} |
| 3606 | + |
| 3607 | +\pnum |
| 3608 | +Let \exposid{local-state} denote the following exposition-only class template: |
| 3609 | + |
| 3610 | +\begin{codeblock} |
| 3611 | +namespace std::execution { |
| 3612 | + struct @\exposid{local-state-base}@ { // \expos |
| 3613 | + virtual ~@\exposid{local-state-base}@() = default; |
| 3614 | + virtual void @\exposid{notify}@() noexcept = 0; // \expos |
| 3615 | + }; |
| 3616 | + |
| 3617 | + template<class Sndr, class Rcvr> |
| 3618 | + struct @\exposid{local-state}@ : @\exposid{local-state-base}@ { // \expos |
| 3619 | + using @\exposid{on-stop-callback}@ = // \expos |
| 3620 | + stop_callback_of_t<stop_token_of_t<env_of_t<Rcvr>>, @\exposid{on-stop-request}@>; |
| 3621 | + |
| 3622 | + @\exposid{local-state}@(Sndr&& sndr, Rcvr& rcvr) noexcept; |
| 3623 | + ~@\exposid{local-state}@(); |
| 3624 | + |
| 3625 | + void @\exposid{notify}@() noexcept override; |
| 3626 | + |
| 3627 | + private: |
| 3628 | + optional<@\exposid{on-stop-callback}@> @\exposid{on_stop}@; // \expos |
| 3629 | + @\exposid{shared-state}@<Sndr>* @\exposid{sh_state}@; // \expos |
| 3630 | + Rcvr* @\exposid{rcvr}@; // \expos |
| 3631 | + }; |
| 3632 | +} |
| 3633 | +\end{codeblock} |
| 3634 | + |
| 3635 | +\begin{itemdecl} |
| 3636 | +local-state(Sndr&& sndr, Rcvr& rcvr) noexcept; |
| 3637 | +\end{itemdecl} |
| 3638 | + |
| 3639 | +\begin{itemdescr} |
| 3640 | +\pnum |
| 3641 | +\effects |
| 3642 | +Equivalent to: |
| 3643 | +\begin{codeblock} |
| 3644 | +auto& [_, data, _] = sndr; |
| 3645 | +this->@\exposid{sh_state}@ = data.sh_state.get(); |
| 3646 | +this->@\exposid{sh_state}@->@\exposid{inc-ref}@(); |
| 3647 | +this->@\exposid{rcvr}@ = addressof(rcvr); |
| 3648 | +\end{codeblock} |
| 3649 | +\end{itemdescr} |
| 3650 | + |
| 3651 | +\begin{itemdecl} |
| 3652 | +~@\exposid{local-state}@(); |
| 3653 | +\end{itemdecl} |
| 3654 | + |
| 3655 | +\begin{itemdescr} |
| 3656 | +\pnum |
| 3657 | +\effects |
| 3658 | +Equivalent to: |
| 3659 | +\begin{codeblock} |
| 3660 | +sh_state->@\exposid{dec-ref}@(); |
| 3661 | +\end{codeblock} |
| 3662 | +\end{itemdescr} |
| 3663 | + |
| 3664 | +\begin{itemdecl} |
| 3665 | +void @\exposid{notify}@() noexcept override; |
| 3666 | +\end{itemdecl} |
| 3667 | + |
| 3668 | +\begin{itemdescr} |
| 3669 | +\pnum |
| 3670 | +\effects |
| 3671 | +Equivalent to: |
| 3672 | +\begin{codeblock} |
| 3673 | +@\exposid{on_stop}@.reset(); |
| 3674 | +visit( |
| 3675 | + [this](const auto& tupl) noexcept -> void { |
| 3676 | + apply( |
| 3677 | + [this](auto tag, const auto&... args) noexcept -> void { |
| 3678 | + tag(std::move(*@\exposid{rcvr}@), args...); |
| 3679 | + }, |
| 3680 | + tupl); |
| 3681 | + }, |
| 3682 | + @\exposid{sh_state}@->result); |
| 3683 | +\end{codeblock} |
| 3684 | +\end{itemdescr} |
| 3685 | + |
| 3686 | +\pnum |
| 3687 | +Let \exposid{split-receiver} denote |
| 3688 | +the following exposition-only class template: |
| 3689 | +\begin{codeblock} |
| 3690 | +namespace std::execution { |
| 3691 | + template<class Sndr> |
| 3692 | + struct @\exposid{split-receiver}@ { // \expos |
| 3693 | + using receiver_concept = receiver_t; |
| 3694 | + |
| 3695 | + template<class Tag, class... Args> |
| 3696 | + void @\exposid{complete}@(Tag, Args&&... args) noexcept { // \expos |
| 3697 | + using tuple_t = @\exposid{decayed-tuple}@<Tag, Args...>; |
| 3698 | + try { |
| 3699 | + @\exposid{sh_state}@->result.template emplace<tuple_t>(Tag(), std::forward<Args>(args)...); |
| 3700 | + } catch (...) { |
| 3701 | + using tuple_t = tuple<set_error_t, exception_ptr>; |
| 3702 | + @\exposid{sh_state}@->result.template emplace<tuple_t>(set_error, current_exception()); |
| 3703 | + } |
| 3704 | + @\exposid{sh_state}@->notify(); |
| 3705 | + } |
| 3706 | + |
| 3707 | + template<class... Args> |
| 3708 | + void set_value(Args&&... args) && noexcept { |
| 3709 | + @\exposid{complete}@(execution::set_value, std::forward<Args>(args)...); |
| 3710 | + } |
| 3711 | + |
| 3712 | + template<class Error> |
| 3713 | + void set_error(Error&& err) && noexcept { |
| 3714 | + @\exposid{complete}@(execution::set_error, std::forward<Error>(err)); |
| 3715 | + } |
| 3716 | + |
| 3717 | + void set_stopped() && noexcept { |
| 3718 | + @\exposid{complete}@(execution::set_stopped); |
| 3719 | + } |
| 3720 | + |
| 3721 | + struct @\exposid{env}@ { // \expos |
| 3722 | + @\exposid{shared-state}@<Sndr>* @\exposid{sh-state}@; // \expos |
| 3723 | + |
| 3724 | + inplace_stop_token query(get_stop_token_t) const noexcept { |
| 3725 | + return @\exposid{sh-state}@->stop_src.get_token(); |
| 3726 | + } |
| 3727 | + }; |
| 3728 | + |
| 3729 | + @\exposid{env}@ get_env() const noexcept { |
| 3730 | + return @\exposid{env}@{@\exposid{sh_state}@}; |
| 3731 | + } |
| 3732 | + |
| 3733 | + @\exposid{shared-state}@<Sndr>* @\exposid{sh_state}@; // \expos |
| 3734 | + }; |
| 3735 | +} |
| 3736 | +\end{codeblock} |
| 3737 | + |
| 3738 | +\pnum |
| 3739 | +Let \exposid{shared-state} denote the following exposition-only class template: |
| 3740 | +\begin{codeblock} |
| 3741 | +namespace std::execution { |
| 3742 | + template<class Sndr> |
| 3743 | + struct @\exposid{shared-state}@ { |
| 3744 | + using @\exposid{variant-type}@ = @\seebelow@; // \expos |
| 3745 | + using @\exposid{state-list-type}@ = @\seebelow@; // \expos |
| 3746 | + |
| 3747 | + explicit @\exposid{shared-state}@(Sndr&& sndr); |
| 3748 | + |
| 3749 | + void @\exposid{start-op}@() noexcept; // \expos |
| 3750 | + void @\exposid{notify}@() noexcept; // \expos |
| 3751 | + void @\exposid{inc-ref}@() noexcept; // \expos |
| 3752 | + void @\exposid{dec-ref}@() noexcept; // \expos |
| 3753 | + |
| 3754 | + inplace_stop_source @\exposid{stop_src}@{}; // \expos |
| 3755 | + @\exposid{variant-type}@ @\exposid{result}@{}; // \expos |
| 3756 | + @\exposid{state-list-type}@ @\exposid{waiting_states}@; // \expos |
| 3757 | + atomic<bool> @\exposid{completed}@{false}; // \expos |
| 3758 | + atomic<size_t> @\exposid{ref_count}@{1}; // \expos |
| 3759 | + connect_result_t<Sndr, @\exposid{split-receiver}@<Sndr>> @\exposid{op_state}@; // \expos |
| 3760 | + }; |
| 3761 | +} |
| 3762 | +\end{codeblock} |
| 3763 | + |
| 3764 | +\pnum |
| 3765 | +Let \tcode{Sigs} be a pack of the arguments |
| 3766 | +to the \tcode{completion_signatures} specialization |
| 3767 | +named by \tcode{completion_signatures_of_t<Sndr>}. |
| 3768 | +For type \tcode{Tag} and pack \tcode{Args}, |
| 3769 | +let \exposid{as-tuple} be an alias template |
| 3770 | +such that \tcode{\exposid{as-tuple}<Tag(Args...)>} denotes |
| 3771 | +the type \tcode{\exposid{decayed-tuple}<Tag, Args...>}. |
| 3772 | +Then \exposid{variant-type} denotes the type |
| 3773 | +\tcode{variant<tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, \exposid{as-tuple}<Sigs>...>}, |
| 3774 | +but with duplicate types removed. |
| 3775 | + |
| 3776 | +\pnum |
| 3777 | +Let \exposid{state-list-type} be a type |
| 3778 | +that stores a list of pointers to \exposid{local-state-base} objects and |
| 3779 | +that permits atomic insertion. |
| 3780 | + |
| 3781 | +\begin{itemdecl} |
| 3782 | +explicit @\exposid{shared-state}@(Sndr&& sndr); |
| 3783 | +\end{itemdecl} |
| 3784 | + |
| 3785 | +\begin{itemdescr} |
| 3786 | +\pnum |
| 3787 | +\effects |
| 3788 | +Initializes \exposid{op_state} with the result of |
| 3789 | +\tcode{connect(std::forward<Sndr>(sndr), \exposid{split-receiver}\{this\})}. |
| 3790 | + |
| 3791 | +\pnum |
| 3792 | +\ensures |
| 3793 | +\exposid{waiting_states} is empty, and \exposid{completed} is \tcode{false}. |
| 3794 | +\end{itemdescr} |
| 3795 | + |
| 3796 | +\begin{itemdecl} |
| 3797 | +void @\exposid{start-op}@() noexcept; |
| 3798 | +\end{itemdecl} |
| 3799 | + |
| 3800 | +\begin{itemdescr} |
| 3801 | +\pnum |
| 3802 | +\effects |
| 3803 | +Evaluates \tcode{\exposid{inc-ref}()}. |
| 3804 | +If \tcode{stop_src.stop_requested()} is \tcode{true}, |
| 3805 | +evaluates \tcode{\exposid{notify}()}; |
| 3806 | +otherwise, evaluates \tcode{start(\exposid{op_state})}. |
| 3807 | +\end{itemdescr} |
| 3808 | + |
| 3809 | +\begin{itemdecl} |
| 3810 | +void @\exposid{notify}@() noexcept; |
| 3811 | +\end{itemdecl} |
| 3812 | + |
| 3813 | +\begin{itemdescr} |
| 3814 | +\pnum |
| 3815 | +\effects |
| 3816 | +Atomically does the following: |
| 3817 | +\begin{itemize} |
| 3818 | +\item |
| 3819 | +Sets \tcode{completed} to \tcode{true}, and |
| 3820 | +\item |
| 3821 | +Exchanges \tcode{waiting_states} with an empty list, |
| 3822 | +storing the old value in a local \tcode{prior_states}. |
| 3823 | +\end{itemize} |
| 3824 | +Then, for each pointer \tcode{p} in \tcode{prior_states}, |
| 3825 | +evaluates \tcode{p->\exposid{notify}()}. |
| 3826 | +Finally, evaluates \tcode{\exposid{dec-ref}()}. |
| 3827 | +\end{itemdescr} |
| 3828 | + |
| 3829 | +\begin{itemdecl} |
| 3830 | +void @\exposid{inc-ref}@() noexcept; |
| 3831 | +\end{itemdecl} |
| 3832 | + |
| 3833 | +\begin{itemdescr} |
| 3834 | +\pnum |
| 3835 | +\effects |
| 3836 | +Increments \exposid{ref_count}. |
| 3837 | +\end{itemdescr} |
| 3838 | + |
| 3839 | +\begin{itemdecl} |
| 3840 | +void @\exposid{dec-ref}@() noexcept; |
| 3841 | +\end{itemdecl} |
| 3842 | + |
| 3843 | +\begin{itemdescr} |
| 3844 | +\pnum |
| 3845 | +\effects |
| 3846 | +Decrements \exposid{ref_count}. |
| 3847 | +If the new value of \exposid{ref_count} is \tcode{0}, |
| 3848 | +calls \tcode{delete this}. |
| 3849 | + |
| 3850 | +\pnum |
| 3851 | +\sync |
| 3852 | +If an evaluation of \tcode{\exposid{dec-ref}()} does not |
| 3853 | +decrement the \tcode{ref_count} to \tcode{0} then |
| 3854 | +synchronizes with the evaluation of \tcode{dec-ref()} |
| 3855 | +that decrements \tcode{ref_count} to \tcode{0}. |
| 3856 | +\end{itemdescr} |
| 3857 | + |
| 3858 | +\pnum |
| 3859 | +Let \exposid{split-impl-tag} be an empty exposition-only class type. |
| 3860 | +Given an expression \tcode{sndr}, |
| 3861 | +the expression \tcode{split.transform_sender(sndr)} is equivalent to: |
| 3862 | +\begin{codeblock} |
| 3863 | +auto&& [tag, _, child] = sndr; |
| 3864 | +auto* sh_state = new @\exposid{shared-state}@{std::forward_like<decltype((sndr))>(child)}; |
| 3865 | +return @\exposid{make-sender}@(@\exposid{split-impl-tag}@(), @\exposid{shared-wrapper}@{sh_state, tag}); |
| 3866 | +\end{codeblock} |
| 3867 | +where \exposid{shared-wrapper} is an exposition-only class |
| 3868 | +that manages the reference count of the \exposid{shared-state} object |
| 3869 | +pointed to by sh_state. |
| 3870 | +\exposid{shared-wrapper} models copyable |
| 3871 | +with move operations nulling out the moved-from object, |
| 3872 | +copy operations incrementing the reference count |
| 3873 | +by calling \tcode{sh_state->\exposid{inc-ref}()}, and |
| 3874 | +assignment operations performing a copy-and-swap operation. |
| 3875 | +The destructor has no effect if sh_state is null; |
| 3876 | +otherwise, it decrements the reference count |
| 3877 | +by evaluating \tcode{sh_state->\exposid{dec-ref}()}. |
| 3878 | + |
| 3879 | +\pnum |
| 3880 | +The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} |
| 3881 | +is specialized for \exposid{split-impl-tag} as follows: |
| 3882 | +\begin{codeblock} |
| 3883 | +namespace std::execution { |
| 3884 | + template<> |
| 3885 | + struct @\exposid{impls-for}@<@\exposid{split-impl-tag}@> : @\exposid{default-impls}@ { |
| 3886 | + static constexpr auto @\exposid{get-state}@ = @\seebelow@; |
| 3887 | + static constexpr auto @\exposid{start}@ = @\seebelow@; |
| 3888 | + }; |
| 3889 | +} |
| 3890 | +\end{codeblock} |
| 3891 | + |
| 3892 | +\pnum |
| 3893 | +The member |
| 3894 | +\tcode{\exposid{impls-for}<\exposid{split-impl-tag}>::\exposid{get-state}} |
| 3895 | +is initialized with a callable object equivalent to |
| 3896 | +the following lambda expression: |
| 3897 | +\begin{codeblock} |
| 3898 | +[]<class Sndr>(Sndr&& sndr, auto& rcvr) noexcept { |
| 3899 | + return @\exposid{local-state}@{std::forward<Sndr>(sndr), rcvr}; |
| 3900 | +} |
| 3901 | +\end{codeblock} |
| 3902 | + |
| 3903 | +\pnum |
| 3904 | +The member |
| 3905 | +\tcode{\exposid{impls-for}<\exposid{split-impl-tag}>::\exposid{start}} |
| 3906 | +is initialized with a callable object |
| 3907 | +that has a function call operator equivalent to the following: |
| 3908 | +\begin{codeblock} |
| 3909 | +template<class Sndr, class Rcvr> |
| 3910 | +void operator()(local-state<Sndr, Rcvr>& state, Rcvr& rcvr) const noexcept; |
| 3911 | +\end{codeblock} |
| 3912 | + |
| 3913 | +\effects |
| 3914 | +If \tcode{state.\exposid{sh_state}->\exposid{completed}} is \tcode{true}, |
| 3915 | +evaluates \tcode{state.\exposid{notify}()} and returns. |
| 3916 | +Otherwise, does the following in order: |
| 3917 | +\begin{itemize} |
| 3918 | +\item |
| 3919 | +Evaluates |
| 3920 | +\begin{codeblock} |
| 3921 | +state.@\exposid{on_stop}@.emplace( |
| 3922 | + get_stop_token(get_env(rcvr)), |
| 3923 | + @\exposid{on-stop-request}@{state.@\exposid{sh_state}@->@\exposid{stop_src}@}); |
| 3924 | +\end{codeblock} |
| 3925 | +\item |
| 3926 | +Then atomically does the following: |
| 3927 | +\begin{itemize} |
| 3928 | +\item |
| 3929 | +Reads the value \tcode{c} of |
| 3930 | +\tcode{state.\exposid{sh_state}->\exposid{completed}}, and |
| 3931 | +\item |
| 3932 | +Inserts \tcode{addressof(state)} into |
| 3933 | +\tcode{state.\exposid{sh_state}->\exposid{waiting_states}} |
| 3934 | +if \tcode{c} is \tcode{false}. |
| 3935 | +\end{itemize} |
| 3936 | +\item |
| 3937 | +If \tcode{c} is \tcode{true}, |
| 3938 | +calls \tcode{state.\exposid{notify}()} and returns. |
| 3939 | +\item |
| 3940 | +Otherwise, |
| 3941 | +if \tcode{addressof(state)} is the first item added to |
| 3942 | +\tcode{state.\exposid{sh_state}->\exposid{waiting_states}}, |
| 3943 | +evaluates \tcode{state.\exposid{sh_state}->\exposid{start-op}()}. |
| 3944 | +\end{itemize} |
| 3945 | + |
3577 | 3946 | \rSec1[exec.util]{Sender/receiver utilities}
|
3578 | 3947 |
|
3579 | 3948 |
|
|
0 commit comments