Skip to content

Commit b506944

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 85fa026 commit b506944

File tree

2 files changed

+28
-33
lines changed

2 files changed

+28
-33
lines changed

include/sys/task.h

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

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

kernel/task.c

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -445,20 +445,20 @@ void sched_wakeup_task(tcb_t *task)
445445
}
446446
}
447447

448-
/* Efficient Round-Robin Task Selection with O(n) Complexity
448+
/* Efficient Round-Robin Task Selection (Cursor-Based, O(1) Complexity)
449449
*
450-
* Selects the next ready task using circular traversal of the master task list.
450+
* Selects the next ready task by advancing the per-priority round-robin
451+
* cursor (rr_cursor) circularly using list API list_cnext().
451452
*
452-
* Complexity: O(n) where n = number of tasks
453-
* - Best case: O(1) when next task in sequence is ready
454-
* - Worst case: O(n) when only one task is ready and it's the last checked
455-
* - Typical case: O(k) where k << n (number of non-ready tasks to skip)
453+
* Complexity: O(1)
454+
* - Always constant-time selection, regardless of total task count.
455+
* - No need to traverse the task list.
456456
*
457457
* Performance characteristics:
458-
* - Excellent for small-to-medium task counts (< 50 tasks)
459-
* - Simple and reliable implementation
460-
* - Good cache locality due to sequential list traversal
461-
* - Priority-aware time slice allocation
458+
* - Ideal for systems with frequent context switches or many tasks.
459+
* - Excellent cache locality: only touches nodes in the active ready queue.
460+
* - Priority-aware: highest non-empty ready queue is chosen via bitmap lookup.
461+
* - Each priority level maintains its own rr_cursor to ensure fair rotation.
462462
*/
463463
uint16_t sched_select_next_task(void)
464464
{
@@ -471,31 +471,28 @@ uint16_t sched_select_next_task(void)
471471
if (current_task->state == TASK_RUNNING)
472472
current_task->state = TASK_READY;
473473

474-
/* Round-robin search: find next ready task in the master task list */
475-
list_node_t *start_node = kcb->task_current;
476-
list_node_t *node = start_node;
477-
int iterations = 0; /* Safety counter to prevent infinite loops */
478-
479-
do {
480-
/* Move to next task (circular) */
481-
node = list_cnext(kcb->tasks, node);
482-
if (!node || !node->data)
483-
continue;
484-
485-
tcb_t *task = node->data;
474+
/* Check out bitmap */
475+
uint32_t bitmap = kcb->harts->ready_bitmap;
476+
if (unlikely(!bitmap))
477+
panic(ERR_NO_TASKS);
486478

487-
/* Skip non-ready tasks */
488-
if (task->state != TASK_READY)
489-
continue;
479+
/* Find top priority ready queue */
480+
int top_prio_level = 0;
481+
for (; !(bitmap & 1U); top_prio_level++, bitmap >>= 1)
482+
;
490483

491-
/* Found a ready task */
492-
kcb->task_current = node;
493-
task->state = TASK_RUNNING;
494-
task->time_slice = get_priority_timeslice(task->prio_level);
484+
/* Advance top priority rr_cursor to next task node */
485+
list_node_t **cursor = &kcb->harts->rr_cursors[top_prio_level];
486+
list_t *rq = kcb->harts->ready_queues[top_prio_level];
487+
if (unlikely(!rq || !*cursor))
488+
panic(ERR_NO_TASKS);
495489

496-
return task->id;
490+
kcb->task_current = *cursor;
491+
*cursor = list_cnext(rq, *cursor);
492+
((tcb_t *) (kcb->task_current->data))->state = TASK_RUNNING;
497493

498-
} while (node != start_node && ++iterations < SCHED_IMAX);
494+
if (kcb->task_current)
495+
return ((tcb_t *) (kcb->task_current->data))->id;
499496

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

0 commit comments

Comments
 (0)