Rescheduling a running thread

The microkernel makes scheduling decisions whenever it's entered as the result of a kernel call, exception, or hardware interrupt.

A scheduling decision is made whenever the execution state of any thread changes—it doesn't matter which processes the threads might reside within. Threads are scheduled globally across all processes.

Normally the running thread continues to run, but the scheduler performs a context switch from one thread to another whenever the running thread:

The running thread blocks when it must wait for some event to occur (response to an IPC request, wait on a mutex, etc.). The blocked thread is removed from the running array, and another thread is selected to run:
  • In the single-core (simple) case, the highest-priority ready thread that's at the head of its priority's queue is chosen and allowed to run.
  • In a multicore system, the scheduler has some flexibility in deciding exactly how to schedule the other threads, with an eye towards optimizing cache usage and minimizing thread migration. This could mean that some processors will be running lower-priority threads while a higher-priority thread is waiting to run on the processor it last ran on. The next time a processor that's running a lower-priority thread makes a scheduling decision, it will choose the higher-priority one.

    In any case, the realtime scheduling rules that were in place on a uniprocessor system are guaranteed to be upheld on a multicore system.

When the blocked thread is subsequently unblocked, it's usually placed on the end of the ready queue for its priority level.

Is preempted
The running thread is preempted when a higher-priority thread is placed on the ready queue (it becomes READY as the result of its block condition being resolved). The preempted thread is moved to the start of the ready queue for that priority, and the higher-priority thread runs. When it's time for a thread at that priority level to run again, that thread resumes execution—a preempted thread doesn't lose its place in the queue for its priority level.
The running thread voluntarily yields the processor (e.g., via sched_yield) and is placed on the end of the ready queue for that priority. The highest-priority thread then runs (which may still be the thread that just yielded).