Skip to content

Commit 3c285e0

Browse files
committed
Refactor scheduler to RR cursor-based O(1) design
Previously, the scheduler performed an O(N) scan of the global task list (kcb->tasks) to locate the next TASK_READY task. This resulted in non-deterministic selection latency and unstable round-robin rotation under heavy load or frequent task state transitions. This change introduces a strict O(1) scheduler based on per-priority ready queues and round-robin (RR) cursors. Each priority level maintains its own ready queue and cursor, enabling constant-time selection of the next runnable task while preserving fairness within the same priority.
1 parent 4a6e6c1 commit 3c285e0

File tree

2 files changed

+31
-52
lines changed

2 files changed

+31
-52
lines changed

include/sys/task.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,6 @@ typedef struct {
124124
extern kcb_t *kcb;
125125

126126
/* System Configuration Constants */
127-
#define SCHED_IMAX \
128-
500 /* Safety limit for scheduler iterations to prevent livelock */
129127
#define MIN_TASK_STACK_SIZE \
130128
256 /* Minimum stack size to prevent stack overflow */
131129
#define TASK_CACHE_SIZE \

kernel/task.c

Lines changed: 31 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -471,16 +471,15 @@ void _sched_block_enqueue(tcb_t *blocked_task)
471471
*
472472
* Selects the next ready task using circular traversal of the master task list.
473473
*
474-
* Complexity: O(n) where n = number of tasks
475-
* - Best case: O(1) when next task in sequence is ready
476-
* - Worst case: O(n) when only one task is ready and it's the last checked
477-
* - Typical case: O(k) where k << n (number of non-ready tasks to skip)
474+
* Complexity: O(1)
475+
* - Always constant-time selection, regardless of total task count.
476+
* - No need to traverse the task list.
478477
*
479478
* Performance characteristics:
480-
* - Excellent for small-to-medium task counts (< 50 tasks)
481-
* - Simple and reliable implementation
482-
* - Good cache locality due to sequential list traversal
483-
* - Priority-aware time slice allocation
479+
* - Ideal for systems with frequent context switches or many tasks.
480+
* - Excellent cache locality: only touches nodes in the active ready queue.
481+
* - Priority-aware: highest non-empty ready queue is chosen via bitmap lookup.
482+
* - Each priority level maintains its own rr_cursor to ensure fair rotation.
484483
*/
485484
uint16_t sched_select_next_task(void)
486485
{
@@ -493,53 +492,35 @@ uint16_t sched_select_next_task(void)
493492
if (current_task->state == TASK_RUNNING)
494493
current_task->state = TASK_READY;
495494

496-
/* Round-robin search: find next ready task in the master task list */
497-
list_node_t *start_node = kcb->task_current;
498-
list_node_t *node = start_node;
499-
int iterations = 0; /* Safety counter to prevent infinite loops */
500-
501-
do {
502-
/* Move to next task (circular) */
503-
node = list_cnext(kcb->tasks, node);
504-
if (!node || !node->data)
505-
continue;
506-
507-
tcb_t *task = node->data;
495+
/* Bitmap search, from bit0 (highest priority level) to bit7 (lowest
496+
* priority level) */
497+
uint32_t bitmap = kcb->ready_bitmap;
498+
uint8_t top_prio_level = 0;
499+
while (top_prio_level < 8) {
500+
if (bitmap & 1U)
501+
break;
502+
bitmap >>= 1;
503+
top_prio_level++;
504+
}
508505

509-
/* Skip non-ready tasks */
510-
if (task->state != TASK_READY)
511-
continue;
506+
list_node_t **cursor = &kcb->rr_cursors[top_prio_level];
507+
list_t *rq = kcb->ready_queues[top_prio_level];
508+
if (unlikely(!rq || !*cursor))
509+
panic(ERR_NO_TASKS);
512510

513-
/* Found a ready task */
514-
kcb->task_current = node;
515-
task->state = TASK_RUNNING;
516-
task->time_slice = get_priority_timeslice(task->prio_level);
511+
/* Update next task with top priority cursor */
512+
kcb->task_current = *cursor;
517513

518-
return task->id;
514+
/* Advance top priority cursor to next task node */
515+
*cursor = list_cnext(rq, *cursor);
519516

520-
} while (node != start_node && ++iterations < SCHED_IMAX);
517+
/* Update new task properties */
518+
tcb_t *new_task = kcb->task_current->data;
519+
new_task->time_slice = get_priority_timeslice(new_task->prio_level);
520+
new_task->state = TASK_RUNNING;
521521

522-
/* No ready tasks found in preemptive mode - all tasks are blocked.
523-
* This is normal for periodic RT tasks waiting for their next period.
524-
* We CANNOT return a BLOCKED task as that would cause it to run.
525-
* Instead, find ANY task (even blocked) as a placeholder, then wait for
526-
* interrupt.
527-
*/
528-
if (kcb->preemptive) {
529-
/* Select any task as placeholder (dispatcher won't actually switch to
530-
* it if blocked) */
531-
list_node_t *any_node = list_next(kcb->tasks->head);
532-
while (any_node && any_node != kcb->tasks->tail) {
533-
if (any_node->data) {
534-
kcb->task_current = any_node;
535-
tcb_t *any_task = any_node->data;
536-
return any_task->id;
537-
}
538-
any_node = list_next(any_node);
539-
}
540-
/* No tasks at all - this is a real error */
541-
panic(ERR_NO_TASKS);
542-
}
522+
if (kcb->task_current)
523+
return new_task->id;
543524

544525
/* In cooperative mode, having no ready tasks is an error */
545526
panic(ERR_NO_TASKS);

0 commit comments

Comments
 (0)