Skip to content

Commit 009b167

Browse files
committed
Refactor scheduler to RR cursor-based O(1) design
Previously, the scheduler iterated through the global task list (kcb->tasks) to find the next TASK_READY task, resulting in O(N) selection time. This approach limited scalability and caused inconsistent task rotation under heavy load. The new scheduling process: 1. Check the ready bitmap and find the highest priority level. 2. Select the RR cursor node from the corresponding ready queue. 3. Advance the selected cursor node circularly. Why RR cursor instead of pop/enqueue rotation: - Fewer operations on the ready queue: compared to the pop/enqueue approach, which requires two function calls per switch, the RR cursor method only advances one pointer per scheduling cycle. - Cache friendly: always accesses the same cursor node, improving cache locality on hot paths. - Cycle deterministic: RR cursor design allows deterministic task rotation and enables potential future extensions such as cycle accounting or fairness-based algorithms. This change introduces a fully O(1) scheduler design based on per-priority ready queues and round-robin (RR) cursors. Each ready queue maintains its own cursor, allowing the scheduler to select the next runnable task in constant time.
1 parent 72d3d91 commit 009b167

File tree

2 files changed

+32
-31
lines changed

2 files changed

+32
-31
lines changed

include/sys/task.h

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

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

kernel/task.c

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -495,20 +495,20 @@ static inline tcb_t *sched_switch_to_idle(void)
495495
return idle;
496496
}
497497

498-
/* Efficient Round-Robin Task Selection with O(n) Complexity
498+
/* Efficient Round-Robin Task Selection (Cursor-Based, O(1) Complexity)
499499
*
500-
* Selects the next ready task using circular traversal of the master task list.
500+
* Selects the next ready task by advancing the per-priority round-robin
501+
* cursor (rr_cursor) circularly using list API list_cnext().
501502
*
502-
* Complexity: O(n) where n = number of tasks
503-
* - Best case: O(1) when next task in sequence is ready
504-
* - Worst case: O(n) when only one task is ready and it's the last checked
505-
* - Typical case: O(k) where k << n (number of non-ready tasks to skip)
503+
* Complexity: O(1)
504+
* - Always constant-time selection, regardless of total task count.
505+
* - No need to traverse the task list.
506506
*
507507
* Performance characteristics:
508-
* - Excellent for small-to-medium task counts (< 50 tasks)
509-
* - Simple and reliable implementation
510-
* - Good cache locality due to sequential list traversal
511-
* - Priority-aware time slice allocation
508+
* - Ideal for systems with frequent context switches or many tasks.
509+
* - Excellent cache locality: only touches nodes in the active ready queue.
510+
* - Priority-aware: highest non-empty ready queue is chosen via bitmap lookup.
511+
* - Each priority level maintains its own rr_cursor to ensure fair rotation.
512512
*/
513513
uint16_t sched_select_next_task(void)
514514
{
@@ -521,31 +521,34 @@ uint16_t sched_select_next_task(void)
521521
if (current_task->state == TASK_RUNNING)
522522
current_task->state = TASK_READY;
523523

524-
/* Round-robin search: find next ready task in the master task list */
525-
list_node_t *start_node = kcb->task_current;
526-
list_node_t *node = start_node;
527-
int iterations = 0; /* Safety counter to prevent infinite loops */
524+
/* Check out bitmap */
525+
uint32_t bitmap = kcb->ready_bitmap;
526+
if (unlikely(!bitmap))
527+
panic(ERR_NO_TASKS);
528528

529-
do {
530-
/* Move to next task (circular) */
531-
node = list_cnext(kcb->tasks, node);
532-
if (!node || !node->data)
533-
continue;
529+
/* Find top priority ready queue */
530+
int top_prio_level = 0;
531+
for (; !(bitmap & 1U); top_prio_level++, bitmap >>= 1)
532+
;
534533

535-
tcb_t *task = node->data;
534+
list_node_t **cursor = &kcb->rr_cursors[top_prio_level];
535+
list_t *rq = kcb->ready_queues[top_prio_level];
536+
if (unlikely(!rq || !*cursor))
537+
panic(ERR_NO_TASKS);
536538

537-
/* Skip non-ready tasks */
538-
if (task->state != TASK_READY)
539-
continue;
539+
/* Update next task with top priority cursor */
540+
kcb->task_current = *cursor;
540541

541-
/* Found a ready task */
542-
kcb->task_current = node;
543-
task->state = TASK_RUNNING;
544-
task->time_slice = get_priority_timeslice(task->prio_level);
542+
/* Advance top priority cursor to next task node */
543+
*cursor = list_cnext(rq, *cursor);
545544

546-
return task->id;
545+
/* Update new task properties */
546+
tcb_t *new_task = kcb->task_current->data;
547+
new_task->time_slice = get_priority_timeslice(new_task->prio_level);
548+
new_task->state = TASK_RUNNING;
547549

548-
} while (node != start_node && ++iterations < SCHED_IMAX);
550+
if (kcb->task_current)
551+
return new_task->id;
549552

550553
/* No ready tasks found - this should not happen in normal operation */
551554
panic(ERR_NO_TASKS);

0 commit comments

Comments
 (0)