Scheduler implementation.
More...
Scheduler
The scheduler is responsible for ... well, scheduling proceses.
It has to decide which threads to run next, and when, among all currently running threads. This is what allows running multiple threads on a single CPU.
A good scheduler is crucial to the user's experience, and this is often what makes the "feel" of the entire OS.
Design
For now, our scheduler uses a preemptive round-robin design. It holds a list of currently running threads, called the runqueue, and cycles between them at a regular interval.
The interval is set currently set to 2MS, per thread, per cycle, and is handled inside irq_timer_handler. If the current thread is still running when the current interval reaches its end, the next one takes its place, and the timer is reset. This is called preemption.
During the execution of a thread, it often needs to access some resources, thus having to wait until the resource is available. When this is the case, the thread is marked as SCHED_WAITING using sched_block_current_thread, and we switch to the next available running thread. Once the resource is available, the relevant interface has the resposibility to notify the scheduler that the thread can be rescheduled, using sched_unblock_thread.
Improvements
- Use a separate timer for pre-emption, instead of mingling with the one used for timekeeping. This would allow for dynamically resetting the timer interrupt without sacrificing precision on ticks.
- Priority levels. We would like to give more time to more "important" thread ideally. This would require multiple runqueues, but is a MUST have for any actual scheduler.
- SMP: when activating multiprocessing, we want to setup a scheduler per core, and dispatch threads across them. This would also imply other smart opimizations (time stealing, ...)
◆ no_preemption_scope
| #define no_preemption_scope |
( |
| ) |
|
Value: for (sched_scope_t scope CLEANUP(sched_scope_destructor) = \
sched_scope_constructor(); \
!scope.done; scope.done = true)
WARNING: As this macro uses a for loop to function, any 'break' directive placed inside it will break out of the guarded scope instead of that of its containing loop.
◆ sched_block_thread()
| void sched_block_thread |
( |
struct thread * |
thread | ) |
|
◆ sched_new_thread()
| void sched_new_thread |
( |
thread_t * |
thread | ) |
|
◆ sched_new_thread_create()
| static ALWAYS_INLINE void sched_new_thread_create |
( |
thread_entry_t |
entrypoint, |
|
|
void * |
data, |
|
|
u32 |
flags |
|
) |
| |
|
static |
◆ sched_unblock_thread()
| void sched_unblock_thread |
( |
thread_t * |
thread | ) |
|
The thread is marked as SCHED_RUNNING and is automatically added to the appropriate runqueue.
◆ schedule()
This is the main function of the scheduler. It is called when we want to switch to the next scheduled thread. It automatically reinserts the current thread into the correct queue depending on its state.
◆ scheduler_preempt_disable()
| bool scheduler_preempt_disable |
( |
void |
| ) |
|
- Returns
- Wether interrupts were previously enabled
◆ scheduler_preempt_enable()
| void scheduler_preempt_enable |
( |
bool |
old_if_flag | ) |
|
- Parameters
-
| old_if_flag | The state of the interrputs prior to locking |