StuBS
Assignment 4: Context Switch

Enhance StuBS with simple thread management, where user threads voluntarily yield control of the core according to the coroutine concept.

You have to implement the Thread class, low level functions for handling the Context Switch of a thread, the Scheduler and the Dispatcher classes, which provide the scheduling policy and the dispatching mechanism respectively.

It's recommended to split the task in three parts:

  • First implement the low level context switching mechanism,
  • then implement Dispatcher as an abstraction layer and interface,
  • lastly implement Scheduler, which realizes the policy by which threads are selected to run.

For testing it is strongly recommended to test each step thoroughly and begin the next one, when it is working as expected. Disable interrupts for the time being and ignore synchronization between interrupt service routines and the normal control flow.

dot_a4.png
Map of important classes for the fourth assignment

Learning Objectives

  • Refreshing your assembler knowledge (see also Introduction to Assembler)
  • Understanding the procedure of thread switching
  • Distinguish between active and passive objects

Cooperative Context Switch

(1) Low-level Context Switch

Context switching needs the Context Switch structure to store the context of the thread (i.e. its register values). You'll need to implement context_launch and context_switch for switching the contexts in assembly, and prepareContext for setting up the context of a thread before it is first run. You'll only need the mov and ret instructions in assembly.

When starting a new thread, the first routine it should call is Thread::kickoff, then it uses its Thread::action virtual method to call into your application code. Be sure to prepare the stack and the context of your new threads correctly. The stack space is to be allocated in the system-image.

The first thread running on a CPU after boot-up, i.e. leaving the boot-up code of our operating system is special. It needs to launch via context_launch, which does not store the previous state, making it impossible to return to the left routine.

For testing purposes, create several threads, which all call context_switch after a few lines of code to switch to the next thread. You'll hardcode to know the successor thread for the time being.

(2) Dispatcher

Next, implement the Dispatcher which provides a nicer interface to the context switching mechanism and manages the life pointer of the currently active thread. In your test program the thread switch should now be performed by calling the Dispatcher, still with known successor.

(3) Scheduler

Finally, the scheduler should be added, a simple First-Come-First-Served (FCFS) strategy is sufficient here. Threads are enqueued in a Queue, and the next thread to be scheduled is always the one at the head of the queue. For realizing its policy, the Scheduler uses the mechanism provided by the Dispatcher. Thread now have to be known to the Scheduler (Scheduler::ready). When switching cooperatively between threads, the routines of the scheduler will be used, so the next thread does not have to be known.

Notes

In assignment 4 we always assume that there are enough threads in the system, ready to be executed, so the ready-list should never run empty. Make sure, that this assumption holds in your test system! Test your code intensively with a variable number of threads. In MPStuBS we recommend to test your code on a single core at first, and if that works, switch on scheduling on the others as well.

Threads are managed in a single ready list in both OOStuBS and MPStuBS. However, on multicore systems it is possible that different cores access the data structure of the scheduler at the same time. Hence, calls to the Scheduler need to be synchronized in MPStuBS even in the case of cooperative scheduling. In particular, you have to ensure that a thread running on the current core will not be made available for execution prematurely on another core.

Further Reading