Coroutines and async/await 138 Remember to change the ports in the code if you for some reason have to change what port delayserver listens on. Introduction to stackless coroutines So, we've finally arrived at the point where we introduce the last method of modeling asynchronous operations in this book. Y ou probably remember that we gave a high-level overview of stackful and stackless coroutines in Chapter 2. In Chapter 5, we implemented an example of stackful coroutines when writing our own fibers/green threads, so now it's time to take a closer look at how stackless coroutines are implemented and used. A stackless coroutine is a way of representing a task that can be interrupted and resumed. If you remember all the way back in Chapter 1, we mentioned that if we want tasks to run concurrently (be in progress at the same time) but not necessarily in parallel, we need to be able to pause and resume the task. In its simplest form, a coroutine is just a task that can stop and resume by yielding control to either its caller, another coroutine, or a scheduler. Many languages will have a coroutine implementation that also provides a runtime that handles scheduling and non-blocking I/O for you, but it's helpful to make a distinction between what a coroutine is and the rest of the machinery involved in creating an asynchronous system. This is especially true in Rust, since Rust doesn't come with a runtime and only provides the infrastructure you need to create coroutines that have native support in the language. Rust makes sure that everyone programming in Rust uses the same abstraction for tasks that can be paused and resumed, but it leaves all the other details of getting an asynchronous system up and running for the programmer. Stackless coroutines or just coroutines? Most often you'll see stackless coroutines simply referred to as coroutines. To try to keep some consistency (you remember I don't like to introduce terms that mean different things based on the context), I've consistently referred to coroutines as either stackless or stackful, but going forward, I'll simply refer to stackless coroutines as coroutines. This is also what you'll have to expect when reading about them in other sources. Fibers/green threads represent this kind of resumable task in a very similar way to how an operating system does. A task has a stack where it stores/restores its current execution state, making it possible to pause and resume the task. A state machine in its simplest form is a data structure that has a predetermined set of states it can be in. In the case of coroutines, each state represents a possible pause/resume point. We don't store the state needed to pause/resume the task in a separate stack. We save it in a data structure instead.