ECE344 Fall 2026 (Sec 1) Lec 14 - Threading Models
Watch on YouTube →
Overview
Jon Eyolfson explains how user-level and kernel-level threads differ in scheduling, blocking behavior, creation cost, and parallelism, then compares many-to-one, one-to-one, and many-to-many mappings. He demonstrates user-thread context switching with `ucontext`—including 64 KiB stacks, `getcontext`, `makecontext`, `setcontext`, and `swapcontext`—and traces how saved registers and stack values behave in Lab 4-style code.
Key takeaways
- Many-to-one user threads cannot execute in parallel because the kernel sees only one schedulable thread for the process.
- A blocking system call from a many-to-one user thread blocks the process's only kernel thread, whereas kernel threads let the kernel schedule other threads and use multiple cores.
- One-to-one threading maps each application thread to a kernel thread; Jon Eyolfson identifies Pthreads as an example.
- A user-level context switch can be built from a saved `ucontext_t` and a separately allocated stack; the lecture's example uses 64 KiB stacks.
- `setcontext` restores saved registers and resumes as though the earlier `getcontext` returned again, so control flow must account for repeated returns.
- Context restoration resets the stack pointer, not the memory contents on the stack; local variables retain the values written before a thread switched out.
Chapters
0:00
Thread State and Who Manages the Thread Table
- Each thread within a process has its own registers and stack, while sharing the process's address space.
- Thread implementations must decide where the thread table lives and who switches between threads.
- Unlike processes, threads can be managed in user space by a library, in kernel space, or through a combination of both.
4:00
User Threads Versus Kernel Threads: Blocking and Parallelism
- A user-thread library schedules threads the kernel cannot see, so a process with one kernel thread cannot run its user threads in parallel.
- If a user thread makes a blocking system call such as `read`, the kernel blocks the process's sole visible thread, stopping all its user threads.
- Kernel threads require system calls for creation and switching, but the kernel can schedule another thread after a block and run threads on different CPU cores.
- Thread tables resemble process control blocks and can reside in user space or kernel space depending on the model.
9:00
Many-to-One, One-to-One, and Many-to-Many Thread Mappings
- In the many-to-one model, a library maps many user threads to one kernel thread; this is portable and inexpensive but offers neither parallelism nor independent progress during blocking calls.
- In the one-to-one model, each application thread maps to a kernel thread; Jon Eyolfson identifies POSIX threads (Pthreads) as an example that enables parallel execution.
- Many-to-many maps user threads across a pool of kernel threads, potentially using a pool sized to the CPU cores while keeping additional user threads cheap.
- Many-to-many scheduling is more complex, and a blocking call can still stall the user threads mapped to that particular kernel thread.
13:00
Building User Threads with Stacks and the ucontext API
- A user-thread library can switch execution by saving and restoring registers while pointing the stack pointer at a separately allocated stack.
- The `ucontext_t` structure stores a thread's execution context; `getcontext` saves registers and `setcontext` restores them.
- `makecontext` configures a saved context to begin at a function, while `swapcontext` saves the current context and switches to another.
- For Lab 4, each user thread uses a context and its own stack; the example allocates stacks with `mmap` and uses 64 KiB per stack.
16:00
getcontext and setcontext: Restoring Execution Creates Repeated Returns
- `getcontext` saves the current register state without changing the program's immediate execution.
- Restoring that state with `setcontext` makes execution appear to return from the earlier `getcontext` call.
- Saving a context and then continually restoring it revisits the same code, producing an infinite loop.
22:30
makecontext: Starting a User Thread on Its Own Stack
- The example initializes a new `ucontext_t`, assigns it a separately allocated stack, and calls `makecontext` to set its entry function.
- The configured T1 function receives the integer argument `42` when its context is restored.
- Calling `setcontext` on T1 switches to its registers and stack; returning from its function terminates the process unless a successor context is configured.
29:30
Switching Between Main, T1, and T2 with swapcontext
- `swapcontext` saves the running thread's state and restores the selected thread, allowing execution to alternate among main, T1, and T2.
- The example uses `uc_link` to send T1 to T2 when T1's function returns, then T2 switches back to the main context.
- A real user-thread scheduler would track created and active threads and select the next context, for example with a round-robin queue.
- These manually coordinated switches provide concurrency on one kernel thread, not parallel execution across multiple cores.
36:00
Tracing Two ucontext Threads and Shared Global State
- The exercise starts with global `i = 0` and switches between contexts UA and UB, which run thread A and thread B on independent stacks.
- The interleaving prints `A1`, `B2`, and `A3` before both loops finish, because both threads update the shared global counter.
- Each thread's local `d` remains on its own stack, so restoring a context restores the stack pointer but does not reset the values stored in stack memory.
- The trace shows why a naive `getcontext` followed by `setcontext` is not a complete swap: resuming can repeat the same path without a first-call-versus-return check.
45:00
Core ucontext Takeaways and Lab 4 Connection
- A library-managed user thread consists of saved registers in a `ucontext_t` and a stack allocated by the library.
- `swapcontext` performs the save-and-restore operation needed for switching, without a system call in the user-thread model.
- When a context resumes, execution continues from its saved return point, and stack values persist across switches.
Summary, takeaways, and chapters were generated by AI from the video's transcript and may contain errors. The video belongs to its creator, Jon Eyolfson.