1111#include " syscall.hpp"
1212
1313namespace klib ::rtos {
14+ template <uint32_t CpuId, typename Irq>
1415 class scheduler {
15- public:
16- /* *
17- * @brief All the available syscalls
18- *
19- */
20- enum class syscalls {
21- create_task = 0 ,
22- delete_task = 1 ,
23- yield = 2 ,
24- sleep = 3 ,
25- malloc = 4 ,
26- free = 5
27- };
28-
2916 protected:
3017 // make sure we can register our callback with the systick handler
3118 static_assert (SYSTICK_CALLBACK_ENABLED, " Systick callback needs to be enabled to switch tasks" );
@@ -67,14 +54,6 @@ namespace klib::rtos {
6754 return ;
6855 }
6956
70- // TODO: implement tick count for tasks
71- // decrement sleep time for all tasks
72- for (size_t i = 0 ; i < tasks.size (); i++) {
73- if (tasks[i]->time_to_sleep .value > 0 ) {
74- tasks[i]->time_to_sleep .value --;
75- }
76- }
77-
7857 // call the scheduler to pick the next task
7958 schedule ();
8059 }
@@ -85,41 +64,67 @@ namespace klib::rtos {
8564 *
8665 */
8766 static void schedule () {
67+ // get the current time
68+ const auto now = io::systick<CpuId, SYSTICK_CALLBACK_ENABLED>::get_runtime ();
69+
8870 // schedule the next task. We use the priority to pick
8971 // the next task to run. We always pick the first task
9072 // with the highest priority that is not sleeping. If no
9173 // task is ready we run the idle task.
92- next_task = (current_task == nullptr ? &idle_task : (
93- current_task->time_to_sleep .value == 0 ? current_task : &idle_task
94- ));
74+ if (current_task == nullptr ) {
75+ next_task = &idle_task;
76+ }
77+ else if (current_task->is_sleeping && current_task->wakup_time > now) {
78+ // current task is sleeping, switch to idle
79+ next_task = &idle_task;
80+ }
81+ else if (current_task->waitable != nullptr ) {
82+ next_task = &idle_task;
83+ }
84+ else {
85+ next_task = current_task;
86+ }
9587
9688 // TODO: implement a better scheduling algorithm
9789 for (size_t i = 0 ; i < tasks.size (); i++) {
98- if (tasks[i]->time_to_sleep .value > 0 ) {
99- // task is sleeping
90+ // get a reference to the task
91+ auto & task = *tasks[i];
92+
93+ // no need to check the next_task again. We checked
94+ // it above to determine if we need to switch to the
95+ // idle task
96+ if (&task == next_task) {
10097 continue ;
10198 }
102-
103- // do not check the current task again
104- if (tasks[i] == current_task) {
105- continue ;
99+
100+ // check if the task is sleeping
101+ if (task.is_sleeping ) {
102+ // check if the sleep time has elapsed
103+ if (task.wakup_time <= now) {
104+ // wake up the task
105+ task.is_sleeping = false ;
106+ }
107+ else {
108+ // task is still sleeping
109+ continue ;
110+ }
106111 }
107112
108113 // check if the task is waiting on a waitable
109- if (tasks[i]-> waitable != nullptr ) {
110- if (tasks[i]-> waitable ->is_waiting ()) {
114+ if (task. waitable != nullptr ) {
115+ if (task. waitable ->is_waiting ()) {
111116 // task is waiting on a waitable
112117 continue ;
113118 }
114119 else {
115120 // clear the waitable as it is no longer waiting
116- tasks[i]-> waitable = nullptr ;
121+ task. waitable = nullptr ;
117122 }
118123 }
119124
120125 // task is not sleeping, check priority
121- if (tasks[i]-> current_priority >= next_task->current_priority ) {
122- next_task = tasks[i] ;
126+ if (task. current_priority >= next_task->current_priority ) {
127+ next_task = &task ;
123128 break ;
124129 }
125130 }
@@ -147,7 +152,6 @@ namespace klib::rtos {
147152 * @brief Start the scheduler
148153 *
149154 */
150- template <uint32_t CpuId, typename Irq>
151155 static void start () {
152156 // update the callback to our scheduler
153157 io::systick<CpuId, SYSTICK_CALLBACK_ENABLED>::set_callback (schedule_irq);
@@ -189,14 +193,14 @@ namespace klib::rtos {
189193 * @param arg2
190194 * @return uint32_t
191195 */
192- static uint32_t syscall_handler (syscalls number, uint32_t arg0, uint32_t arg1, uint32_t arg2) {
196+ static uint32_t syscall_handler (detail:: syscalls number, uint32_t arg0, uint32_t arg1, uint32_t arg2) {
193197 switch (number) {
194- case syscalls::create_task:
198+ case detail:: syscalls::create_task:
195199 // add the task to the scheduler
196200 tasks.push_back (reinterpret_cast <detail::base_task*>(arg0));
197201 break ;
198202
199- case syscalls::delete_task:
203+ case detail:: syscalls::delete_task:
200204 // find and remove the task from the scheduler. We move backwards
201205 // to not mess up the indexing when erasing. Note this is something
202206 // that is specific to our dynamic array implementation.
@@ -224,21 +228,31 @@ namespace klib::rtos {
224228 // task not found
225229 return false ;
226230
227- case syscalls::yield:
228- // yield the cpu to the next task
229- current_task->waitable = reinterpret_cast <rtos::waitable*>(arg0);
231+ case detail::syscalls::sleep:
232+ // set the time to sleep for the current task
233+ current_task->wakup_time = (
234+ io::systick<CpuId, SYSTICK_CALLBACK_ENABLED>::get_runtime () +
235+ klib::time::ms (arg0)
236+ );
237+
238+ // mark the task as sleeping
239+ current_task->is_sleeping = true ;
230240
231241 // switch to the next task
232242 schedule ();
233243 break ;
234244
235- case syscalls::sleep :
236- // set the time to sleep for the current task
237- current_task->time_to_sleep . value = arg0;
245+ case detail:: syscalls::yield :
246+ // yield the cpu to the next task
247+ current_task->waitable = reinterpret_cast <rtos::waitable*>( arg0) ;
238248
239249 // switch to the next task
240250 schedule ();
241251 break ;
252+
253+ case detail::syscalls::get_time:
254+ // get the current time in ms
255+ return io::systick<CpuId, SYSTICK_CALLBACK_ENABLED>::get_runtime ().value ;
242256 }
243257
244258 return true ;
0 commit comments