Skip to content

Commit

Permalink
book: add scheduling & context switching sections
Browse files Browse the repository at this point in the history
  • Loading branch information
elenaf9 committed Mar 5, 2024
1 parent 55cf275 commit 3103c52
Showing 1 changed file with 48 additions and 1 deletion.
49 changes: 48 additions & 1 deletion book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,56 @@ The `riot-rs-threads` crate implements multi-threading and the scheduler.

It makes heavy use of the `critical_section` crate to ensure that there are no conflicting accesses to the shared thread state.

Threads are managed through the static `Threads` structure, that contains all thread data, runqueues and state information.
Threads are managed through the static `Threads` structure, that contains all thread data, runqueues, and state information.
The `EnsureOnce` wrapper around it uses a [`critical_section::Mutex`](https://docs.rs/critical-section/latest/critical_section/struct.Mutex.html) to ensures that each access is marked as a [`critical_section`](https://doc.rust-lang.org/std/cell/struct.RefCell.html), and that a reference is dropped directly after the access.

Thread data is stored in the `Thread` structure.
Apart from general management info like the `ThreadId` and runqueue number (`RunqueueId`), it stores the thread's execution state after a context switch.
A context switch may happen after the scheduler was triggered.
On ARM Cortex-M, it is initiated through a PendSV exception, by calling the public function `riot_rs_threads::arch::schedule()`.

### Scheduling

The scheduler is triggered in the following cases:
- The current thread is blocked on a resource.
- The current thread cooperatively yields to another thread with the same priority.
- The current thread sleeps.
- The current thread has run to completion.
- Another thread was unblocked on a resource or woken up from sleep.

Triggering the scheduler doesn't necessarily imply a thread switch.
In particular in the last case, the switch only occurs if the now unblocked thread has a higher priority than the current one.

The runqueue lives in a separate crate `riot_rs_runqueue`.
It is implemented as circular linked lists for each priority level, to which `ThreadId`s can be added and removed.
The runqueue always returns the head from the highest-priority list.
Within a priority list, the head can be advanced if a thread cooperatively yields.

If the scheduler is triggered and all runqueues are empty, sleep mode is entered until an interrupt occurs.

### Context Switching

(*TODO: is this arch-specific or always the case?*)

Context switching is implemented in the `riot_rs-threads::arch` module.

Following the initial PendSV exception (for Cortex-M), the arch-specific exception handler calls the scheduler to prompt for a context switch.
If a context switch is required, the following steps occur:
1. the scheduler:
- updates the state in `Threads`
- stores the stack pointer of the old thread
- returns:
- pointer to memory for storing the register state of the old thread
- pointer to memory location for loading the register state of the new thread
- the new stack pointer
2. the arch-specific handler:
- stores the current register state
- loads the new register state
- updates the stack pointer
3. return from exception

### Thread Creation

(*TODO*)

- [Appendices](./appendices.md)
Expand Down

0 comments on commit 3103c52

Please sign in to comment.