How Programming Languages Model Asynchronous Program Flow 28 of this is that most CPUs assume that there is a stack it can perform operations on and that it has a register for the stack pointer and instructions for stack manipulation. User-level threads can, in their broadest sense, refer to any implementation of a system (runtime) that creates and schedules tasks, and you can't make the same assumptions as you do with OS threads. They can closely resemble OS threads by using separate stacks for each task, as we'll see in Chapter 5 when we go through our fiber/green threads example, or they can be radically different in nature, as we'll see when we go through how Rust models concurrent operations later on in Part 3 of this book. No matter the definition, a set of tasks needs something that manages them and decides who gets what resources to progress. The most obvious resource on a computer system that all tasks need to progress is CPU time. We call the “something” that decides who gets CPU time to progress a scheduler. Most likely, when someone refers to a “thread” without adding extra context, they refer to an OS thread/kernel thread, so that's what we'll do going forward. I'll also keep referring to a thread of execution as simply a task. I find the topic of asynchronous programming easier to reason about when we limit the use of terms that have different assumptions associated with them depending on the context as much as possible. With that out of the way, let's go through some defining characteristics of OS threads while we also highlight their limitations. Important! Definitions will vary depending on what book or article you read. For example, if you read about how a specific operating system works, you might see that processes or threads are abstractions that represent “tasks”, which will seem to contradict the definitions we use here. As I mentioned earlier, the choice of reference frame is important, and it's why we take so much care to define the terms we use thoroughly as we encounter them throughout the book. The definition of a thread can also vary by operating system, even though most popular systems share a similar definition today. Most notably, Solaris (pre-Solaris 9, which was released in 2002) used to have a two-level thread system that differentiated between application threads, lightweight processes, and kernel threads. This was an implementation of what we call M:N threading, which we'll get to know more about later in this book. Just beware that if you read older material, the definition of a thread in such a system might differ significantly from the one that's commonly used today. Now that we've gone through the most important definitions for this chapter, it's time to talk more about the most popular ways of handling concurrency when programming.