Peer Review: My Scheduler Design
Posted: Fri Jan 11, 2008 12:57 pm
My Scheduler Spec v1.3
Scheduler will acknowledge 6 queue:
Very High
High
Norm
Low
Idle
Sleeping
The Scheduler will monitor two volatile Bytes used for status and notification in the scheduler.
Byte 1: Scheduler Key (unsigned char scheduler_key)
---------------------------------------------------
0 - 4 - Active Queue Bit - Used to signify what queue its currently in, 0 = no, 1 = yes (0 = Very High, 1 = High ...)
5 - Reserved
6 - Reserved
7 - Scheduler Lock Bit - This is set when entering the scheduler and cleared exiting. When entering the scheduler, this bit is checked, if its set, we leave, no harm, no foul. This is important if an active task yields back to the scheduler, for any reason, and as the scheduler is processing, a timer event fires.
Byte 2: Scheduler Queue (unsigned char scheduler_queue)
-------------------------------------------------------
0 - 4 - Queue Has Items - Used to mark if a queue has an threads waiting to run 0 = nothing in this queue, 1 = something(s) in this queue
5 - New Very High Notification - Signal that a new Very High Thread has been added...
6 - New High Notification - Signal that a new High Thread has been added...
7 - New Norm Notification - Signal that a new Norm Thread has been added...
Any given queue will work as such:
1.) At init, all queues are pointing to a unique tail_task (a seperate 1 per queue), and the active kernel thread is then added to the HEAD of the Norm Queue (and the kernel thread's -> next will point to the tail_thread)
2.) When a new task is added to the system, it is given a free initial run boost, and placed at the HEAD of its priority queue. At this point, we set [priority queue] Has items bit in scheduler_queue and if the priority is NORM or better, we set the new thread waiting flag, also in the scheduler_queue.
3.) When processing a queue, a queue will run until its head becomes the tail_task, at that point the next (lower) priority is run.
4.) When a Thread is put to sleep, (wait() is different) is will be removed from its priority queue and added to a generic sleep queue, it will also be assigned a ticks_to_wake_up_at value.
5.) When a Sleeping thread is awoken, it will be placed back at the HEAD of its queue, given a run boost of 50 ticks, and the scheduler_queue flags will be set to indicate that the priority has items, and has new items if applicable.
time_to_run:
------------
This is the amount of time a current task has left to run, it is calculated as follows (using the below key)
time_to_run = 100 - (priority_value * 10);
The priority_value key:
Very High = 0
High = 2
Norm = 5
Low = 7
Idle = 9
This is how the Timer handler will work (logically)
1.) Scheduler Checks [Scheduler Lock Bit] if set, it returns, if not set, it is then set and the logic flow continues.
2.) Scheduler will increment the ticks counter
3.) Scheduler will then loop through all sleeping threads, and wake any that are ready (ticks_to_wake_up = system_ticks).
4.) Scheduler will check the Notification Bits, if there is a new thread in a priority GREATER then the current, the Active Priority becomes that priority. All of this is done by calling select_next_task() *** Important things to remember *** If we are processing NORM, and a Very High comes in, the new very high is placed at the HEAD of the very high queue. Furthermore, since we have already completed the very high queue, the previous HEAD was actually the tail. So once the new very high task has completed, the scheduler will attempt to grab the next very high, since its the tail, it goes to the next priority lower...
5.) Scheduler will check current_task time_to_run != 0, in that case then we decrement it by 1, and return.
6.)If the current_task time_to_run == 0 --OR-- we had a new item notification in a queue GREATER than the current, we call select_next_task() (Explained below)
6.) Once here, we have a new task to run, we check the time_to_run, is it 0? If it is 0 we calculate the new time_to_run, if its not we give it a yield bonus of + 1/2 of a new time_to_run.
7.) The Scheduler switches to the new current_task and returns.
select_next_task() (logically)
--------------------------------
if New Very High and Active Queue != Very High
Active Queue Bit = Very High
if New High and Active Queue < High
Active Queue Bit = High
if New Norm and Active Queue < Norm
Active Queue Bit = Norm
[code for each priority, but only run for the active priority]
if current_priority_queue != priority_tail_task
active_task = current_priority_queue
current_priority_queue = current_priority_queue->next
[add active_task to the end of the current_priority_queue]
else
active priority is then set to the next lower one
if low_priority_queue == low_tail_task
if no other queue has items (bit checking) run the first idle thread
if idle_queue == idle_tail_task
kernel_idle()
if any queue has items, cycle their tail marker to the end of the queue.
Set the active queue to the first queue with items, call select_next_task(); and then return.
So basically this is my Scheduler Logic in a nut shell.
Also a side note, my task structure has the related variables mentioned above.
So what do you think, I am not looking for anything crazy good, but something that will show bias for priorities, and handle sleeping states...
Comments and critics welcome.
Thanks,
Rich
Scheduler will acknowledge 6 queue:
Very High
High
Norm
Low
Idle
Sleeping
The Scheduler will monitor two volatile Bytes used for status and notification in the scheduler.
Byte 1: Scheduler Key (unsigned char scheduler_key)
---------------------------------------------------
0 - 4 - Active Queue Bit - Used to signify what queue its currently in, 0 = no, 1 = yes (0 = Very High, 1 = High ...)
5 - Reserved
6 - Reserved
7 - Scheduler Lock Bit - This is set when entering the scheduler and cleared exiting. When entering the scheduler, this bit is checked, if its set, we leave, no harm, no foul. This is important if an active task yields back to the scheduler, for any reason, and as the scheduler is processing, a timer event fires.
Byte 2: Scheduler Queue (unsigned char scheduler_queue)
-------------------------------------------------------
0 - 4 - Queue Has Items - Used to mark if a queue has an threads waiting to run 0 = nothing in this queue, 1 = something(s) in this queue
5 - New Very High Notification - Signal that a new Very High Thread has been added...
6 - New High Notification - Signal that a new High Thread has been added...
7 - New Norm Notification - Signal that a new Norm Thread has been added...
Any given queue will work as such:
1.) At init, all queues are pointing to a unique tail_task (a seperate 1 per queue), and the active kernel thread is then added to the HEAD of the Norm Queue (and the kernel thread's -> next will point to the tail_thread)
2.) When a new task is added to the system, it is given a free initial run boost, and placed at the HEAD of its priority queue. At this point, we set [priority queue] Has items bit in scheduler_queue and if the priority is NORM or better, we set the new thread waiting flag, also in the scheduler_queue.
3.) When processing a queue, a queue will run until its head becomes the tail_task, at that point the next (lower) priority is run.
4.) When a Thread is put to sleep, (wait() is different) is will be removed from its priority queue and added to a generic sleep queue, it will also be assigned a ticks_to_wake_up_at value.
5.) When a Sleeping thread is awoken, it will be placed back at the HEAD of its queue, given a run boost of 50 ticks, and the scheduler_queue flags will be set to indicate that the priority has items, and has new items if applicable.
time_to_run:
------------
This is the amount of time a current task has left to run, it is calculated as follows (using the below key)
time_to_run = 100 - (priority_value * 10);
The priority_value key:
Very High = 0
High = 2
Norm = 5
Low = 7
Idle = 9
This is how the Timer handler will work (logically)
1.) Scheduler Checks [Scheduler Lock Bit] if set, it returns, if not set, it is then set and the logic flow continues.
2.) Scheduler will increment the ticks counter
3.) Scheduler will then loop through all sleeping threads, and wake any that are ready (ticks_to_wake_up = system_ticks).
4.) Scheduler will check the Notification Bits, if there is a new thread in a priority GREATER then the current, the Active Priority becomes that priority. All of this is done by calling select_next_task() *** Important things to remember *** If we are processing NORM, and a Very High comes in, the new very high is placed at the HEAD of the very high queue. Furthermore, since we have already completed the very high queue, the previous HEAD was actually the tail. So once the new very high task has completed, the scheduler will attempt to grab the next very high, since its the tail, it goes to the next priority lower...
5.) Scheduler will check current_task time_to_run != 0, in that case then we decrement it by 1, and return.
6.)If the current_task time_to_run == 0 --OR-- we had a new item notification in a queue GREATER than the current, we call select_next_task() (Explained below)
6.) Once here, we have a new task to run, we check the time_to_run, is it 0? If it is 0 we calculate the new time_to_run, if its not we give it a yield bonus of + 1/2 of a new time_to_run.
7.) The Scheduler switches to the new current_task and returns.
select_next_task() (logically)
--------------------------------
if New Very High and Active Queue != Very High
Active Queue Bit = Very High
if New High and Active Queue < High
Active Queue Bit = High
if New Norm and Active Queue < Norm
Active Queue Bit = Norm
[code for each priority, but only run for the active priority]
if current_priority_queue != priority_tail_task
active_task = current_priority_queue
current_priority_queue = current_priority_queue->next
[add active_task to the end of the current_priority_queue]
else
active priority is then set to the next lower one
if low_priority_queue == low_tail_task
if no other queue has items (bit checking) run the first idle thread
if idle_queue == idle_tail_task
kernel_idle()
if any queue has items, cycle their tail marker to the end of the queue.
Set the active queue to the first queue with items, call select_next_task(); and then return.
So basically this is my Scheduler Logic in a nut shell.
Also a side note, my task structure has the related variables mentioned above.
So what do you think, I am not looking for anything crazy good, but something that will show bias for priorities, and handle sleeping states...
Comments and critics welcome.
Thanks,
Rich