Skip to content

Commit 73613fb

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 a44c86e commit 73613fb

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
@@ -127,8 +127,6 @@ typedef struct {
127127
extern kcb_t *kcb;
128128

129129
/* System Configuration Constants */
130-
#define SCHED_IMAX \
131-
500 /* Safety limit for scheduler iterations to prevent livelock */
132130
#define MIN_TASK_STACK_SIZE \
133131
256 /* Minimum stack size to prevent stack overflow */
134132
#define TASK_CACHE_SIZE \

kernel/task.c

Lines changed: 31 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -457,16 +457,15 @@ void sched_wakeup_task(tcb_t *task)
457457
*
458458
* Selects the next ready task using circular traversal of the master task list.
459459
*
460-
* Complexity: O(n) where n = number of tasks
461-
* - Best case: O(1) when next task in sequence is ready
462-
* - Worst case: O(n) when only one task is ready and it's the last checked
463-
* - Typical case: O(k) where k << n (number of non-ready tasks to skip)
460+
* Complexity: O(1)
461+
* - Always constant-time selection, regardless of total task count.
462+
* - No need to traverse the task list.
464463
*
465464
* Performance characteristics:
466-
* - Excellent for small-to-medium task counts (< 50 tasks)
467-
* - Simple and reliable implementation
468-
* - Good cache locality due to sequential list traversal
469-
* - Priority-aware time slice allocation
465+
* - Ideal for systems with frequent context switches or many tasks.
466+
* - Excellent cache locality: only touches nodes in the active ready queue.
467+
* - Priority-aware: highest non-empty ready queue is chosen via bitmap lookup.
468+
* - Each priority level maintains its own rr_cursor to ensure fair rotation.
470469
*/
471470
uint16_t sched_select_next_task(void)
472471
{
@@ -479,53 +478,35 @@ uint16_t sched_select_next_task(void)
479478
if (current_task->state == TASK_RUNNING)
480479
current_task->state = TASK_READY;
481480

482-
/* Round-robin search: find next ready task in the master task list */
483-
list_node_t *start_node = kcb->task_current;
484-
list_node_t *node = start_node;
485-
int iterations = 0; /* Safety counter to prevent infinite loops */
486-
487-
do {
488-
/* Move to next task (circular) */
489-
node = list_cnext(kcb->tasks, node);
490-
if (!node || !node->data)
491-
continue;
492-
493-
tcb_t *task = node->data;
481+
/* Bitmap search, from bit0 (highest priority level) to bit7 (lowest
482+
* priority level) */
483+
uint32_t bitmap = kcb->ready_bitmap;
484+
uint8_t top_prio_level = 0;
485+
while (top_prio_level < 8) {
486+
if (bitmap & 1U)
487+
break;
488+
bitmap >>= 1;
489+
top_prio_level++;
490+
}
494491

495-
/* Skip non-ready tasks */
496-
if (task->state != TASK_READY)
497-
continue;
492+
list_node_t **cursor = &kcb->rr_cursors[top_prio_level];
493+
list_t *rq = kcb->ready_queues[top_prio_level];
494+
if (unlikely(!rq || !*cursor))
495+
panic(ERR_NO_TASKS);
498496

499-
/* Found a ready task */
500-
kcb->task_current = node;
501-
task->state = TASK_RUNNING;
502-
task->time_slice = get_priority_timeslice(task->prio_level);
497+
/* Update next task with top priority cursor */
498+
kcb->task_current = *cursor;
503499

504-
return task->id;
500+
/* Advance top priority cursor to next task node */
501+
*cursor = list_cnext(rq, *cursor);
505502

506-
} while (node != start_node && ++iterations < SCHED_IMAX);
503+
/* Update new task properties */
504+
tcb_t *new_task = kcb->task_current->data;
505+
new_task->time_slice = get_priority_timeslice(new_task->prio_level);
506+
new_task->state = TASK_RUNNING;
507507

508-
/* No ready tasks found in preemptive mode - all tasks are blocked.
509-
* This is normal for periodic RT tasks waiting for their next period.
510-
* We CANNOT return a BLOCKED task as that would cause it to run.
511-
* Instead, find ANY task (even blocked) as a placeholder, then wait for
512-
* interrupt.
513-
*/
514-
if (kcb->preemptive) {
515-
/* Select any task as placeholder (dispatcher won't actually switch to
516-
* it if blocked) */
517-
list_node_t *any_node = list_next(kcb->tasks->head);
518-
while (any_node && any_node != kcb->tasks->tail) {
519-
if (any_node->data) {
520-
kcb->task_current = any_node;
521-
tcb_t *any_task = any_node->data;
522-
return any_task->id;
523-
}
524-
any_node = list_next(any_node);
525-
}
526-
/* No tasks at all - this is a real error */
527-
panic(ERR_NO_TASKS);
528-
}
508+
if (kcb->task_current)
509+
return new_task->id;
529510

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

0 commit comments

Comments
 (0)