Definitions 27 Note In a system where the scheduler can pre-empt running tasks, tasks can also yield voluntarily as they do in a cooperative system, and it's rare with a system that only relies on pre-emption. We can further divide these abstractions into two broad categories based on the characteristics of their implementation: 1. Stackful : Each task has its own call stack. This is often implemented as a stack that's similar to the stack used by the operating system for its threads. Stackful tasks can suspend execution at any point in the program as the whole stack is preserved. 2. Stackless : There is not a separate stack for each task; they all run sharing the same call stack. A task can't be suspended in the middle of a stack frame, limiting the runtime's ability to pre-empt the task. However, they need to store/restore less information when switching between tasks so they can be more efficient. There are more nuances to these two categories that you'll get a deep understanding of when we implement an example of both a stackful coroutine (fiber) and a stackless coroutine (Rust futures generated by async /await ) later in the book. For now, we keep the details to a minimum to just provide an overview. Threads We keep referring to threads all throughout this book, so before we get too far in, let's stop and give “thread” a good definition since it's one of those fundamental terms that causes a lot of confusion. In the most general sense, a thread refers to a thread of execution, meaning a set of instructions that need to be executed sequentially. If we tie this back to the first chapter of this book, where we provided several definitions under the Concurrency vs. Parallelism subsection, a thread of execution is similar to what we defined as a task with multiple steps that need resources to progress. The generality of this definition can be a cause of some confusion. A thread to one person can obviously refer to an OS thread, and to another person, it can simply refer to any abstraction that represents a thread of execution on a system. Threads are often divided into two broad categories: OS threads : These threads are created by the OS and managed by the OS scheduler. On Linux, this is known as a kernel thread. User-level threads : These threads are created and managed by us as programmers without the OS knowing about them. Now, this is where things get a bit tricky: OS threads on most modern operating systems have a lot of similarities. Some of these similarities are dictated by the design of modern CPUs. One example 