Creating coroutines – Coroutines and async/await

We’ll continue to build our knowledge and understanding from the ground up. The first thing we’ll do is create a task that we can stop and resume by modeling it as a state machine by hand.

Once we’ve done that, we’ll take a look at how this way of modeling pausable tasks enables us to write a syntax much like async/await and rely on code transformations to create these state machines instead of writing them by hand.

We’ll create a simple program that does the following:

  1. Prints a message when our pausable task is starting.
  2. Makes a GET request to our delayserver.
  3. Waits for the GET request.
  4. Prints the response from the server.
  5. Makes a second GET request to our delayserver.
  6. Waits for the second response from the server.
  7. Prints the response from the server.
  8. Exits the program.

In addition, we’ll execute our program by calling Future::poll on our hand-crafted coroutine as many times as required to run it to completion. There’s no runtime, reactor, or executor yet since we’ll cover those in the next chapter.

If we wrote our program as an async function, it would look as follows:
async fn async_main() {
    println!(“Program starting”)
    let txt = Http::get(“/1000/HelloWorld”).await;
    println!(“{txt}”);
    let txt2 = Http::(“500/HelloWorld2”).await;
    println!(“{txt2}”);
}

In main.rs, start by making the necessary imports and module declarations:

ch07/a-coroutine/src/main.rs
use std::time::Instant;
mod future;
mod http;
use crate::http::Http;
use future::{Future, PollState};

The next thing we write is our stoppable/resumable task called Coroutine:

ch07/a-coroutine/src/main.rs
struct Coroutine {
    state: State,
}

Once that’s done, we write the different states this task could be in:

ch07/a-coroutine/src/main.rs
enum State {
    Start,
    Wait1(Box<dyn Future<Output = String>>),
    Wait2(Box<dyn Future<Output = String>>),
    Resolved,
}

This specific coroutine can be in four states:

  • Start: The Coroutine has been created but it hasn’t been polled yet
  • Wait1: When we call Http::get, we get a HttpGetFuture returned that we store in the State enum. At this point, we return control back to the calling function so it can do other things if needed. We chose to make this generic over all Future functions that output a String, but since we only have one kind of future right now, we could have made it simply hold a HttpGetFuture and it would work the same way.
  • Wait2: The second call to Http::get is the second place where we’ll pass control back to the calling function.
  • Resolved: The future is resolved and there is no more work to do.

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Post