std::execution: Sender

std::execution: Sender

std::execution offers three types of senders: factories, adapters, and consumers. I’ll take a closer look at these today.

Most of the following content is from proposal P2300R10. I will try to represent it more concisely.

Sender Factory

A sender factory is an algorithm that takes no senders as parameters and returns a sender.

execution::schedule

execution::sender auto schedule(
    execution::scheduler auto scheduler
);
        

Returns a sender, which starts on the provided scheduler.


Article content

execution::just

execution::sender auto just(
    auto ...&& values
);
        

Returns a sender, which sends the provided values.

execution::just_error

execution::sender auto just_error(
    auto && error
);
        

Returns a sender, which completes with the specific error.

execution::just_stopped

execution::sender auto just_stopped();
        

Returns a sender, which completes immediately by calling the receiver’s set_stopped.

execution::read_env

execution::sender auto read_env(auto tag);
        

Returns a sender that reaches into a receiver’s environment and pulls out the current value associated with the environment value tag. It then sends the value read back to the receiver through the value channel. For instance, read_env(get_scheduler) is a sender that asks the receiver for the currently suggested scheduler and passes it to the receiver’s set_value completion-signal.

This can be useful when scheduling nested dependent work. The following sender pulls the current scheduler into the value channel and then schedules more work onto it.


Article content

Sender Adaptor

A sender adaptor is an algorithm that takes one or more senders as parameters and returns a sender.


Article content
Article content

 

Modernes C++ Mentoring

Do you want to stay informed: Subscribe.

 

Sender adaptors are lazy. Sender consumers such as this_thread::sync_wait start senders.

execution::continues_on

execution::sender auto continues_on(
    execution::sender auto input,
    execution::scheduler auto scheduler
);
        

Returns a sender describing the transition from the execution agent of the input sender to the execution agent of the target scheduler.


Article content

execution::then

execution::sender auto then(
    execution::sender auto input,
    std::invocable<values-sent-by(input)...> function
);
        

then returns a sender describing the task graph described by the input sender, with an added node of invoking the provided function with the values sent by the input sender as arguments.

Article content

execution::upon_*

execution::sender auto upon_error(
    execution::sender auto input,
    std::invocable<errors-sent-by(input)...> function
);

execution::sender auto upon_stopped(
    execution::sender auto input,
    std::invocable auto function
);
        

upon_error and upon_stopped are similar to then, but where then works with values sent by the input sender, upon_error works with errors, and upon_stopped is invoked when the “stopped” signal is sent.

A nice example about then,upon_error, and upon_stopped has the prototype library stdexec.

The following example shows an HTTP request handler.(https://github.com/NVIDIA/stdexec/blob/main/examples/server_theme/then_upon.cpp#L151)

/*
 * Copyright (c) 2022 Lucian Radu Teodorescu
 *
 * Licensed under the Apache License Version 2.0 with LLVM Exceptions
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *   https://llvm.org/LICENSE.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 // Handler for the "classify" request type

ex::sender auto handle_classify_request(const http_request& req) {
  return
    // start with the input buffer
    ex::just(req)
    // extract the image from the input request
    | ex::then(extract_image)
    // analyze the content of the image and classify it
    // we are doing the processing on the same thread
    | ex::then(do_classify)
    // handle errors
    | ex::upon_error(on_classification_error)
    // handle cancellation
    | ex::upon_stopped(on_classification_cancelled)
    // transform this into a response
    | ex::then(to_response)
    // done
    ;
}
        

The function extracts images and returns an ex::sender object, representing an asynchronous operation that can be composed of other operations.

The function takes a constant reference to an http_request req. The processing pipeline begins with the ex::just(req) function, creating a sender that starts with the HTTP request’s input buffer.

Error handling is incorporated into the pipeline using the ex::upon_error function, which specifies the on_classification_error function to handle any errors that occur during the classification process.

The pipeline then uses the ex::then function to chain a series of operations. The first operation, extract_image, extracts the image from the input request. The next operation, do_classify, classifies the extracted image’s content. This processing is done on the same thread.

Similarly, the ex::upon_stopped function is used to handle the cancellation of the operation, specifying the on_classification_cancelled function.

Finally, the to_response function is used to transform the classification result into an HTTP response. This transformation is also done using the ex::then function.

This sender adaptor changes the execution resource on which the sender runs. It does not adapt the sender.

This code demonstrates a composable and asynchronous processing pipeline for gracefully handling HTTP requests, classifying images, and errors and cancellations.

execution::starts_on

execution::sender auto starts_on(
    execution::scheduler auto sched,
    execution::sender auto snd
);
        

What’s Next?

std::execution has more sender adaptors to offer: execution::let_*, execution::into_variant, execution::stopped_as_optional, execution::stopped_as_error, execution::bulk, execution::split, and execution::when_all.

To view or add a comment, sign in

Others also viewed

Explore topics