Concurrency and Thread Dispatching

Processes (Review)

  • Memory I/O State, CPU state, sequential stream of instructions.
  • Each process has its own memory space and I/O state.
  • Process switch overhead is high due to memory/IO state.
  • Process creation is high. Provides CPU and Memory/IO protection.
  • Sharing overhead is high.

Multithreaded Processes (Review)

  • Share the same memory/IO state.
  • Switch overhead is low (only CPU state).
  • Thread creation is low.
  • No Memory/IO protection.
  • Low sharing overhead.

Why Processes & Threads? (Review)

  • Processes: Unit of execution and allocation; Virtual Machine abstraction.
  • Threads: Decouple allocation and execution; Run multiple threads within the same process.

Thread State (Review)

  • Shared State: Memory, I/O.
  • Private State (TCB): CPU registers, program counter PC, Execution stack.

Execution Stack Example (Review)

  • Stack holds function arguments and return addresses, enabling recursion.

Single-Threaded Example (Review)

  • A program may not complete all tasks if a function never finishes.

Use of Threads (Review)

  • Multiple threads can run concurrently, behaving as if there are multiple CPUs.

Cooperating Threads

  • Allow resource sharing, speedup via overlapping I/O and computation, and modularity.

Multithreaded Processes

  • PCB points to multiple TCBs.
  • Switching threads within a block is a simple thread switch.
  • Switching threads across blocks requires changes to memory and I/O address tables.

Lifecycle of a Thread

  • States: new, ready, running, waiting, terminated.
  • TCBs are organized in queues based on state.

Ready Queues

  • Most threads are in the ready state.
  • TCBs are in scheduler queues when not running.

Ready Queue And Various I/O Device Queues

  • Separate queue for each device/signal/condition, with different scheduler policies.

Choosing a Thread to Run

  • Dispatcher chooses the next thread to run using scheduling priorities (LIFO, FIFO, Priority queue).

Per Thread State

  • TCB stores execution state, scheduling info, pointers, and a pointer to the PCB.

Dispatch Loop

  • The OS dispatching loop runs threads, chooses the next thread, saves and loads CPU state.

Running a Thread

  • Load thread state into CPU, load environment, and jump to PC.
  • Dispatcher regains control through internal (I/O, yield) or external (preemption) events.

Yielding through Internal Events

  • Threads yield CPU when blocking on I/O, waiting for a signal, or calling yield().

Stack for Yielding a Thread

  • Dispatcher switches to a new thread by saving the current thread's state and loading the new thread's state.

Need for External Events

  • External events (interrupts, timer) ensure dispatcher can regain control.

Detour: Interrupt Controller

  • Interrupt controller manages interrupt requests and priorities.

Preemptive Multithreading

  • Timer interrupts force scheduling decisions, preempting threads.

ThreadFork(): Create a New Thread

  • Creates a new thread and adds it to the ready queue by allocating stack/TCB and initializing TCB.

How do we initialize TCB and Stack?

  • Initialize register fields (stack pointer, PC return address, argument registers).

How does Thread get started?

  • runnewthread() selects the TCB and returns into ThreadRoot().

What does ThreadRoot() look like?

  • ThreadRoot() calls thread code and then ThreadFinish().

What does ThreadFinish() do?

  • Enters kernel mode, wakes up waiting threads, marks thread for destruction, and calls runnewthread().

ThreadJoin() system call

  • Allows one thread to wait for another to finish.

Use of Join for Traditional Procedure Call

  • ThreadFork() followed by ThreadJoin() is logically equivalent to a traditional procedure call.

Summary

  • Thread state is in the TCB (registers, PC, stack pointer) with states including New, Ready, Running, Waiting, or Terminated.
  • Interrupts return control to OS, enabling preemptive multithreading.
  • ThreadFork() creates threads, ThreadRoot() executes code, and ThreadFinish() prepares for destruction.
  • Threads use ThreadJoin() to wait for others.