|
3006 | 3006 | an error completion on \tcode{out_rcvr} shall be executed
|
3007 | 3007 | on an unspecified execution agent.
|
3008 | 3008 |
|
| 3009 | +\rSec3[exec.on]{\tcode{execution::on}} |
| 3010 | + |
| 3011 | +\pnum |
| 3012 | +The \tcode{on} sender adaptor has two forms: |
| 3013 | +\begin{itemize} |
| 3014 | +\item |
| 3015 | +\tcode{on(sch, sndr)}, |
| 3016 | +which starts a sender \tcode{sndr} on an execution agent |
| 3017 | +belonging to a scheduler \tcode{sch}'s associated execution resource and |
| 3018 | +that, upon \tcode{sndr}'s completion, |
| 3019 | +transfers execution back to the execution resource |
| 3020 | +on which the \tcode{on} sender was started. |
| 3021 | +\item |
| 3022 | +\tcode{on(sndr, sch, closure)}, |
| 3023 | +which upon completion of a sender \tcode{sndr}, |
| 3024 | +transfers execution to an execution agent |
| 3025 | +belonging to a scheduler \tcode{sch}'s associated execution resource, |
| 3026 | +then executes a sender adaptor closure \tcode{closure} |
| 3027 | +with the async results of the sender, and |
| 3028 | +that then transfers execution back to the execution resource |
| 3029 | +on which \tcode{sndr} completed. |
| 3030 | +\end{itemize} |
| 3031 | + |
| 3032 | +\pnum |
| 3033 | +The name \tcode{on} denotes a pipeable sender adaptor object. |
| 3034 | +For subexpressions \tcode{sch} and \tcode{sndr}, |
| 3035 | +\tcode{on(sch, sndr)} is ill-formed if any of the following is true: |
| 3036 | +\begin{itemize} |
| 3037 | +\item |
| 3038 | +\tcode{decltype((sch))} does not satisfy \libconcept{scheduler}, or |
| 3039 | +\item |
| 3040 | +\tcode{decltype((sndr))} does not satisfy \libconcept{sender} and |
| 3041 | +\tcode{sndr} is not |
| 3042 | +a pipeable sender adaptor closure object\iref{exec.adapt.objects}, or |
| 3043 | +\item |
| 3044 | +\tcode{decltype((sndr))} satisfies \libconcept{sender} and |
| 3045 | +\tcode{sndr }is also a pipeable sender adaptor closure object. |
| 3046 | +\end{itemize} |
| 3047 | + |
| 3048 | +\pnum |
| 3049 | +Otherwise, if \tcode{decltype((sndr))} satisfies \libconcept{sender}, |
| 3050 | +the expression \tcode{on(sch, sndr)} is expression-equivalent to: |
| 3051 | +\begin{codeblock} |
| 3052 | +transform_sender( |
| 3053 | + @\exposid{query-or-default}@(get_domain, sch, default_domain()), |
| 3054 | + @\exposid{make-sender}@(on, sch, sndr)) |
| 3055 | +\end{codeblock} |
| 3056 | +except that \tcode{sch} is evaluated only once. |
| 3057 | + |
| 3058 | +\pnum |
| 3059 | +For subexpressions \tcode{sndr}, \tcode{sch}, and \tcode{closure}, if |
| 3060 | +\begin{itemize} |
| 3061 | +\item |
| 3062 | +\tcode{decltype((sch))} does not satisfy \libconcept{scheduler}, or |
| 3063 | +\item |
| 3064 | +\tcode{decltype((sndr))} does not satisfy \libconcept{sender}, or |
| 3065 | +\item |
| 3066 | +\tcode{closure} is not a pipeable sender adaptor closure object\iref{exec.adapt.objects}, |
| 3067 | +\end{itemize} |
| 3068 | +the expression \tcode{on(sndr, sch, closure)} is ill-formed; |
| 3069 | +otherwise, it is expression-equivalent to: |
| 3070 | +\begin{codeblock} |
| 3071 | +transform_sender( |
| 3072 | + @\exposid{get-domain-early}@(sndr), |
| 3073 | + @\exposid{make-sender}@(on, @\exposid{product-type}@{sch, closure}, sndr)) |
| 3074 | +\end{codeblock} |
| 3075 | +except that \tcode{sndr} is evaluated only once. |
| 3076 | + |
| 3077 | +\pnum |
| 3078 | +Let \tcode{out_sndr} and \tcode{env} be subexpressions, |
| 3079 | +let \tcode{OutSndr} be \tcode{decltype((out_sndr))}, and |
| 3080 | +let \tcode{Env} be \tcode{decltype((env))}. |
| 3081 | +If \tcode{\exposconcept{sender-for}<OutSndr, on_t>} is \tcode{false}, |
| 3082 | +then the expressions \tcode{on.transform_env(out_sndr, env)} and |
| 3083 | +\tcode{on.transform_sender(out_sndr, env)} are ill-formed. |
| 3084 | + |
| 3085 | +\pnum |
| 3086 | +Otherwise: |
| 3087 | +Let \exposid{not-a-scheduler} be an unspecified empty class type, and |
| 3088 | +let \exposid{not-a-sender} be the exposition-only type: |
| 3089 | +\begin{codeblock} |
| 3090 | +struct @\exposid{not-a-sender}@ { |
| 3091 | + using sender_concept = sender_t; |
| 3092 | + |
| 3093 | + auto get_completion_signatures(auto&&) const { |
| 3094 | + return @\seebelow@; |
| 3095 | + } |
| 3096 | +}; |
| 3097 | +\end{codeblock} |
| 3098 | +where the member function \tcode{get_completion_signatures} returns |
| 3099 | +an object of a type that is not |
| 3100 | +a specialization of the \tcode{completion_signatures} class template. |
| 3101 | + |
| 3102 | +\pnum |
| 3103 | +The expression \tcode{on.transform_env(out_sndr, env)} |
| 3104 | +has effects equivalent to: |
| 3105 | +\begin{codeblock} |
| 3106 | +auto&& [_, data, _] = out_sndr; |
| 3107 | +if constexpr (@\libconcept{scheduler}@<decltype(data)>) { |
| 3108 | + return @\exposid{JOIN-ENV}@(@\exposid{SCHED-ENV}@(std::forward_like<OutSndr>(data)), @\exposid{FWD-ENV}@(std::forward<Env>(env))); |
| 3109 | +} else { |
| 3110 | + return std::forward<Env>(env); |
| 3111 | +} |
| 3112 | +\end{codeblock} |
| 3113 | + |
| 3114 | +\pnum |
| 3115 | +The expression \tcode{on.transform_sender(out_sndr, env)} |
| 3116 | +has effects equivalent to: |
| 3117 | +\begin{codeblock} |
| 3118 | +auto&& [_, data, child] = out_sndr; |
| 3119 | +if constexpr (@\libconcept{scheduler}@<decltype(data)>) { |
| 3120 | + auto orig_sch = |
| 3121 | + @\exposid{query-with-default}@(get_scheduler, env, @\exposid{not-a-scheduler}@()); |
| 3122 | + |
| 3123 | + if constexpr (@\libconcept{same_as}@<decltype(orig_sch), @\exposid{not-a-scheduler}@>) { |
| 3124 | + return @\exposid{not-a-sender}@{}; |
| 3125 | + } else { |
| 3126 | + return continues_on( |
| 3127 | + starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)), |
| 3128 | + std::move(orig_sch)); |
| 3129 | + } |
| 3130 | +} else { |
| 3131 | + auto& [sch, closure] = data; |
| 3132 | + auto orig_sch = @\exposid{query-with-default}@( |
| 3133 | + get_completion_scheduler<set_value_t>, |
| 3134 | + get_env(child), |
| 3135 | + @\exposid{query-with-default}@(get_scheduler, env, @\exposid{not-a-scheduler}@())); |
| 3136 | + |
| 3137 | + if constexpr (@\libconcept{same_as}@<decltype(orig_sch), @\exposid{not-a-scheduler}@>) { |
| 3138 | + return @\exposid{not-a-sender}@{}; |
| 3139 | + } else { |
| 3140 | + return @\exposid{write-env}@( |
| 3141 | + continues_on( |
| 3142 | + std::forward_like<OutSndr>(closure)( |
| 3143 | + continues_on( |
| 3144 | + @\exposid{write-env}@(std::forward_like<OutSndr>(child), @\exposid{SCHED-ENV}@(orig_sch)), |
| 3145 | + sch)), |
| 3146 | + orig_sch), |
| 3147 | + @\exposid{SCHED-ENV}@(sch)); |
| 3148 | + } |
| 3149 | +} |
| 3150 | +\end{codeblock} |
| 3151 | + |
| 3152 | +\pnum |
| 3153 | +\recommended |
| 3154 | +Implementations should use |
| 3155 | +the return type of \tcode{\exposid{not-a-sender}::get_completion_signatures} |
| 3156 | +to inform users that their usage of \tcode{on} is incorrect |
| 3157 | +because there is no available scheduler onto which to restore execution. |
| 3158 | + |
| 3159 | +\pnum |
| 3160 | +Let \tcode{out_sndr} be a subexpression denoting |
| 3161 | +a sender returned from \tcode{on(sch, sndr)} or one equal to such, and |
| 3162 | +let \tcode{OutSndr} be the type \tcode{decltype((out_sndr))}. |
| 3163 | +Let \tcode{out_rcvr} be a subexpression denoting a receiver |
| 3164 | +that has an environment of type \tcode{Env} |
| 3165 | +such that \tcode{\libconcept{sender_in}<OutSndr, Env>} is \tcode{true}. |
| 3166 | +Let \tcode{op} be an lvalue referring to the operation state |
| 3167 | +that results from connecting \tcode{out_sndr} with \tcode{out_rcvr}. |
| 3168 | +Calling \tcode{start(op)} shall |
| 3169 | +\begin{itemize} |
| 3170 | +\item |
| 3171 | +remember the current scheduler, \tcode{get_scheduler(get_env(rcvr))}; |
| 3172 | +\item |
| 3173 | +start \tcode{sndr} on an execution agent belonging to |
| 3174 | +sch's associated execution resource; |
| 3175 | +\item |
| 3176 | +upon \tcode{sndr}'s completion, |
| 3177 | +transfer execution back to the execution resource |
| 3178 | +associated with the scheduler remembered in step 1; and |
| 3179 | +\item |
| 3180 | +forward \tcode{sndr}'s async result to \tcode{out_rcvr}. |
| 3181 | +\end{itemize} |
| 3182 | +If any scheduling operation fails, |
| 3183 | +an error completion on \tcode{out_rcvr} shall be executed |
| 3184 | +on an unspecified execution agent. |
| 3185 | + |
| 3186 | +\pnum |
| 3187 | +Let \tcode{out_sndr} be a subexpression denoting |
| 3188 | +a sender returned from \tcode{on(sndr, sch, closure)} or one equal to such, and |
| 3189 | +let \tcode{OutSndr} be the type \tcode{decltype((out_sndr))}. |
| 3190 | +Let \tcode{out_rcvr} be a subexpression denoting a receiver |
| 3191 | +that has an environment of type \tcode{Env} |
| 3192 | +such that \tcode{\libconcept{sender_in}<OutSndr, Env>} is \tcode{true}. |
| 3193 | +Let \tcode{op} be an lvalue referring to the operation state |
| 3194 | +that results from connecting \tcode{out_sndr} with \tcode{out_rcvr}. |
| 3195 | +Calling \tcode{start(op)} shall |
| 3196 | +\begin{itemize} |
| 3197 | +\item |
| 3198 | +remember the current scheduler, |
| 3199 | +which is the first of the following expressions that is well-formed: |
| 3200 | +\begin{itemize} |
| 3201 | +\item \tcode{get_completion_scheduler<set_value_t>(get_env(sndr))} |
| 3202 | +\item \tcode{get_scheduler(get_env(rcvr))}; |
| 3203 | +\end{itemize} |
| 3204 | +\item |
| 3205 | +start \tcode{sndr} on the current execution agent; |
| 3206 | +\item |
| 3207 | +upon \tcode{sndr}'s completion, |
| 3208 | +transfer execution to an agent |
| 3209 | +owned by sch's associated execution resource; |
| 3210 | +\item |
| 3211 | +forward \tcode{sndr}'s async result as if by |
| 3212 | +connecting and starting a sender \tcode{closure(S)}, |
| 3213 | +where \tcode{S} is a sender |
| 3214 | +that completes synchronously with \tcode{sndr}'s async result; and |
| 3215 | +\item |
| 3216 | +upon completion of the operation started in the previous step, |
| 3217 | +transfer execution back to the execution resource |
| 3218 | +associated with the scheduler remembered in step 1 and |
| 3219 | +forward the operation's async result to \tcode{out_rcvr}. |
| 3220 | +\end{itemize} |
| 3221 | +If any scheduling operation fails, |
| 3222 | +an error completion on \tcode{out_rcvr} shall be executed on |
| 3223 | +an unspecified execution agent. |
| 3224 | + |
| 3225 | +\rSec3[exec.then]{\tcode{execution::then}, \tcode{execution::upon_error}, \tcode{execution::upon_stopped}} |
| 3226 | + |
| 3227 | +\pnum |
| 3228 | +\tcode{then} attaches an invocable as a continuation |
| 3229 | +for an input sender's value completion operation. |
| 3230 | +\tcode{upon_error} and \tcode{upon_stopped} do the same |
| 3231 | +for the error and stopped completion operations, respectively, |
| 3232 | +sending the result of the invocable as a value completion. |
| 3233 | + |
| 3234 | +\pnum |
| 3235 | +The names \tcode{then}, \tcode{upon_error}, and \tcode{upon_stopped} |
| 3236 | +denote pipeable sender adaptor objects. |
| 3237 | +Let the expression \exposid{then-cpo} be one of |
| 3238 | +\tcode{then}, \tcode{upon_error}, or \tcode{upon_stopped}. |
| 3239 | +For subexpressions \tcode{sndr} and \tcode{f}, |
| 3240 | +if \tcode{decltype((sndr))} does not satisfy \libconcept{sender}, or |
| 3241 | +\tcode{decltype((f))} does not satisfy \exposid{movable-value}, |
| 3242 | +\tcode{\exposid{then-cpo}(sndr, f) }is ill-formed. |
| 3243 | + |
| 3244 | +\pnum |
| 3245 | +Otherwise, |
| 3246 | +the expression \tcode{\exposid{then-cpo}(sndr, f)} is expression-equivalent to: |
| 3247 | +\begin{codeblock} |
| 3248 | +transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(@\exposid{then-cpo}@, f, sndr)) |
| 3249 | +\end{codeblock} |
| 3250 | +except that \tcode{sndr} is evaluated only once. |
| 3251 | + |
| 3252 | +\pnum |
| 3253 | +For \tcode{then}, \tcode{upon_error}, and \tcode{upon_stopped}, |
| 3254 | +let \exposid{set-cpo} be |
| 3255 | +\tcode{set_value}, \tcode{set_error}, and \tcode{set_stopped}, respectively. |
| 3256 | +The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} |
| 3257 | +is specialized for \exposid{then-cpo} as follows: |
| 3258 | +\begin{codeblock} |
| 3259 | +namespace std::execution { |
| 3260 | + template<> |
| 3261 | + struct @\exposid{impls-for}@<@\exposid{decayed-typeof}@<@\exposid{then-cpo}@>> : @\exposid{default-impls}@ { |
| 3262 | + static constexpr auto @\exposid{complete}@ = |
| 3263 | + []<class Tag, class... Args> |
| 3264 | + (auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void { |
| 3265 | + if constexpr (@\libconcept{same_as}@<Tag, @\exposid{decayed-typeof}@<set-cpo>>) { |
| 3266 | + @\exposid{TRY-SET-VALUE}@(rcvr, |
| 3267 | + invoke(std::move(fn), std::forward<Args>(args)...)); |
| 3268 | + } else { |
| 3269 | + Tag()(std::move(rcvr), std::forward<Args>(args)...); |
| 3270 | + } |
| 3271 | + }; |
| 3272 | + }; |
| 3273 | +} |
| 3274 | +\end{codeblock} |
| 3275 | + |
| 3276 | +\pnum |
| 3277 | +The expression \tcode{\exposid{then-cpo}(sndr, f)} has undefined behavior |
| 3278 | +unless it returns a sender\tcode{out_sndr} that |
| 3279 | +\begin{itemize} |
| 3280 | +\item |
| 3281 | +invokes \tcode{f} or a copy of such |
| 3282 | +with the value, error, or stopped result datums of \tcode{sndr} |
| 3283 | +for \tcode{then}, \tcode{upon_error}, and \tcode{upon_stopped}, respectively, |
| 3284 | +using the result value of \tcode{f} as \tcode{out_sndr}'s value completion, and |
| 3285 | +\item |
| 3286 | +forwards all other completion operations unchanged. |
| 3287 | +\end{itemize} |
| 3288 | + |
3009 | 3289 | \rSec1[exec.util]{Sender/receiver utilities}
|
3010 | 3290 |
|
3011 | 3291 |
|
|
0 commit comments