Open this lesson in your favourite AI. It'll walk you through the why, explain the demo, and quiz you on the try-it list.
The scheduler is the hidden arbiter of every concurrent program — it decides who runs next, for how long, and when preemption happens. In a preemptive scheduler (OS threads, Go runtime, JVM), the running task can be interrupted at any point; you must assume any shared variable can change between two lines of code. In a cooperative scheduler (async/await, Node.js event loop), the running task yields only at explicit await points — between yields, nothing else runs, which makes some concurrency bugs impossible but means one infinite loop freezes everything. Understanding which scheduler governs your code tells you what guarantees you have and what bugs are even possible.
Two flavors:
Preemptive (OS threads, most modern runtimes): the scheduler interrupts a running task at any point and switches. The running task has no say in it. Safe, but you must assume any variable can change underneath you.
Cooperative (async/await, coroutines): the running task has to explicitly yield (usually via await). Between yields, nothing else runs. Predictable — but one misbehaving task can freeze everything.
await boundaries, not between arbitrary lines.Use these three in order. Each builds on the one before.
Define preemptive vs. cooperative scheduling. Name one popular runtime that uses each.
Walk me through what happens during a preemptive context switch at the OS level: timer interrupt, saving registers, picking the next thread, TLB considerations.
Why does cooperative scheduling make some concurrency bugs impossible, and which new bug class does it introduce instead? Use a short code example.