Rotary supports pre-emptive, software-based multitasking, allowing multiple threads of execution on a single core.

Task Structures

Each task is defined by a struct task entry, which stores:

  • Type (kernel or usermode)
  • State (running, waiting, killed, etc.)
  • Kernel stack pointers and size
  • Virtual address space and mappings

Scheduling

task_schedule() is responsible for performing all task scheduling decisions and invoking context switches. It is invoked periodically by hooking the system’s Programmable Interval Timer. The scheduler:

  • Uses a round-robin algorithm, giving each task equal CPU time.
  • Cleans up resources for tasks marked as KILLED.
  • Calls architecture-specific code to perform context switching.

A priority-based scheduler is planned for future implementation.

Context Switching

Context switches are handled in two stages:

  1. task_schedule() invokes arch_task_switch(), an architecture-specific function.
  2. On x86, arch_task_switch() updates the ESP0 field in the Task State Segment (TSS) to set the next task’s kernel stack.

Although hardware-assisted task switching via the TSS is possible, Rotary performs all context switching in software, only using ESP0 to set the correct stack pointer.

The assembly routine task_context_switch:

  • Saves the current task’s registers onto its stack or struct task.
  • Updates ESP to point to the next task’s stack.
  • Restores registers before resuming execution.

This ensures a seamless transition between tasks.

Additional Reading

I’ve written up my approach in developing a multitasking implementation on my blog, which provides significantly more information and detail.