Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cooperative task manager #82

Open
wants to merge 53 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
9e3387e
Implement cooperative scheduler
vicitori Nov 25, 2024
b822163
Add template for context switcher
vicitori Nov 25, 2024
73ee053
Remove excess type FunctionType
vicitori Nov 25, 2024
3c873cb
Add task id
vicitori Nov 29, 2024
396d59e
Remove context_switcher
vicitori Nov 29, 2024
f9d5fe1
Add put_to_sleep method
vicitori Nov 29, 2024
a14b93b
Add terminate_task method
vicitori Nov 29, 2024
5a30dfb
Remove context_switchers from lib.rs
vicitori Nov 29, 2024
b5e5821
Add changes before merging
vicitori Dec 7, 2024
924f126
Sync
vicitori Dec 7, 2024
1f6777a
Change sched according to preemptive
vicitori Dec 7, 2024
597d9fb
Implement TaskManagerTrait
vicitori Dec 9, 2024
cd97e60
Rename method to add_priority_task
vicitori Dec 9, 2024
38e2abf
Rename task_core to core
vicitori Dec 9, 2024
e96e24f
Fix condition for priority in add_priority_task
vicitori Dec 9, 2024
43f2da0
Add stop condition check in schedule()
vicitori Dec 9, 2024
26542ea
Merge with preemptive
vicitori Dec 9, 2024
dfa2536
Fix fn call from CooperativeTM not TM
vicitori Dec 9, 2024
7c66e2a
Fix errors and warnings
vicitori Dec 9, 2024
3f5186d
Add comments and refactor code
vicitori Dec 11, 2024
c7d71d4
Add TODO errors handling comment
vicitori Dec 11, 2024
73b24fb
Remove .idea
vicitori Dec 11, 2024
e8e9bd3
Add comments for statuses
vicitori Dec 12, 2024
0d276b1
Add panic when errors
vicitori Dec 12, 2024
9f18efa
Fix warnings
vicitori Dec 12, 2024
916d452
Add test_start_task_manager for tests
vicitori Dec 13, 2024
fe0c827
Fix bugs according to test
vicitori Dec 14, 2024
c8b6db7
Test add_fn, find_fn
vicitori Dec 18, 2024
48d640a
Remove Created state
vicitori Dec 18, 2024
f61e8ea
Add tests functions, current_task_id
vicitori Dec 18, 2024
5caf5fe
Add simple test for put_to_sleep
vicitori Dec 18, 2024
980d22b
Tests for get_id, replace reset to fn head
vicitori Dec 19, 2024
5640ff9
Add panic get_id_from_position
vicitori Dec 19, 2024
0debdfa
Test diff states for put_to_sleep
vicitori Dec 19, 2024
956b568
Add wake_up and test it
vicitori Dec 19, 2024
0b5db20
Refactor fn terminate task with current_task_id
vicitori Dec 19, 2024
980376c
Fix schedule fn (sleeping state)
vicitori Dec 19, 2024
2baecc9
Remove terminate_curr_task fn
vicitori Dec 19, 2024
9c32884
Test terminate_task fn
vicitori Dec 19, 2024
63a9061
Add rest of tests
vicitori Dec 19, 2024
e5bc524
Fix code according to tests
vicitori Dec 19, 2024
bf57940
Add timer_unit_tests to separate file
vicitori Dec 19, 2024
f231a08
Refactor tests, add more comments
vicitori Dec 22, 2024
7790516
Add comments, refactor and change fn new
vicitori Dec 23, 2024
af6d517
Fix tests and bugs
vicitori Dec 23, 2024
b7390b9
Fix according to clippy
vicitori Dec 23, 2024
e37ceb5
Add examples for coop sched
vicitori Dec 24, 2024
104ca6c
Merge with 'main'
vicitori Dec 26, 2024
520b344
Fix linker scripts according to ci errors
vicitori Dec 26, 2024
8c99ca9
Fix linker script for esp32c6
vicitori Dec 26, 2024
899cb66
Fix wake up test
vicitori Dec 29, 2024
9ce3a1c
Add ci to local repo
vicitori Dec 29, 2024
f013647
Fix ld/esp32c6.ld
vicitori Dec 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions c-library/mips64/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

294 changes: 210 additions & 84 deletions src/task_manager/cooperative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,131 +5,257 @@ use crate::task_manager::{
TaskManagerTrait, TASK_MANAGER,
};
use alloc::vec::Vec;
use core::task::{Poll, RawWaker, RawWakerVTable, Waker};
use core::{future::Future, pin::Pin, task::Context};

/// The number of tasks can fit into a type usize.
pub type TaskNumberType = usize;
/// The number of tasks id can fit into a type usize.
type TaskIdType = usize;
/// Type of priority number of a task.
type TaskPriorityType = usize;

/// Number of existing priorities.
const NUM_PRIORITIES: usize = 11;

/// The status of the task changes during execution. ```enum TaskStatusType``` contains possible states.
#[derive(PartialEq)]
enum TaskStatusType {
/// Task status after it is created.
Created,
/// Task status after setup function. It is ready to be executed.
Ready,
/// Task status when loop function is running.
Running,
/// Task status when it is sleeping.
Sleeping,
/// Task status when it terminated.
/// It can be in both cases
/// when a task is finished and when the other task called ```terminate_task``` function
/// with id of a task that will be terminated.
Terminated,
}

/// The main structure for a cooperative task.
/// Shell for ```Task```, the same for both cooperative and preemptive task managers.
#[repr(C)]
/// Future shell for task for cooperative execution.
pub struct FutureTask {
/// Task to execute in task manager.
pub(crate) task: Task,
/// Marker for setup function completion.
pub(crate) is_setup_completed: bool,
pub struct CooperativeTask {
/// Contains 3 functions for task execution inherited from the ```Task```: ```setup_fn```,
/// ```loop_fn``` and ```stop_condition_fn```.
pub(crate) core: Task,
/// Each task has a unique ```id```. The First ```id``` number is 0.
id: TaskIdType,
/// Status of existing ```CooperativeTask```. It may change during the task executing.
status: TaskStatusType,
/// Each ```CooperativeTask``` has a ```priority```.
/// It is taken into account when selecting the next task to execute.
priority: TaskPriorityType,
}

impl Future for FutureTask {
type Output = ();

fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut array: [usize; 8] = core::array::from_fn(|i| i);
array[0] = 5;
if (self.task.stop_condition_fn)() {
Poll::Ready(())
} else {
if !self.is_setup_completed {
(self.task.setup_fn)();
self.is_setup_completed = true;
} else {
(self.task.loop_fn)();
}
Poll::Pending
/// Cooperative task manager representation. Based on round-robin scheduling with priorities.
#[repr(C)]
pub struct CooperativeTaskManager {
/// Array of vectors with ```CooperativeTask``` to execute.
pub(crate) tasks: [Vec<CooperativeTask>; NUM_PRIORITIES],
/// ```id``` of a task that will be created the next.
pub(crate) next_task_id: TaskIdType,
}

/// Cooperative implementation of ```TaskManagerTrait```.
impl TaskManagerTrait for CooperativeTaskManager {
/// Add a task to task manager.
/// It should pass setup, loop, and condition functions.
/// Task added with this function has ```priority``` 0.
fn add_task(
setup_fn: TaskSetupFunctionType,
loop_fn: TaskLoopFunctionType,
stop_condition_fn: TaskStopConditionFunctionType,
) {
CooperativeTaskManager::add_priority_task(setup_fn, loop_fn, stop_condition_fn, 0);
}

/// Starts task manager work.
fn start_task_manager() -> ! {
loop {
CooperativeTaskManager::schedule();
}
}
}

/// Creates simple task waker. May be more difficult in perspective.
pub fn task_waker() -> Waker {
fn raw_clone(_: *const ()) -> RawWaker {
RawWaker::new(core::ptr::null::<()>(), &NOOP_WAKER_VTABLE)
impl CooperativeTaskManager {
/// Creates new task manager.
pub(crate) const fn new() -> CooperativeTaskManager {
let tasks = [
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
Vec::new(),
];
CooperativeTaskManager {
tasks,
next_task_id: 0,
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we begin with empty tasks array?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each vector is for tasks of a specific priority and all possible priorities are initialized at the start of the manager.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't we create vector for exact priority, when task with such priority appears? Cause seems like we use unnecessary memory. It may be critical for devices with small memory such as esp32.

}

fn raw_wake(_: *const ()) {}

fn raw_wake_by_ref(_: *const ()) {}
/// Add a task to task manager.
/// It should pass setup, loop, and condition functions.
/// Task added with this function has given priority.
pub fn add_priority_task(
setup_fn: TaskSetupFunctionType,
loop_fn: TaskLoopFunctionType,
stop_condition_fn: TaskStopConditionFunctionType,
priority: TaskPriorityType,
) {
if priority >= NUM_PRIORITIES {
panic!("Error: add_task: Task's priority is invalid. It must be between 0 and 11.");
}
let mut new_task =
CooperativeTaskManager::create_task(setup_fn, loop_fn, stop_condition_fn, priority);
CooperativeTaskManager::setup_task(&mut new_task);
CooperativeTaskManager::push_to_queue(new_task);
}

fn raw_drop(_: *const ()) {}
/// Find a task by ```id``` and return it.
pub unsafe fn find_task<'a>(id: TaskIdType) -> &'a mut CooperativeTask {
for vec in TASK_MANAGER.tasks.iter_mut() {
for task in vec.iter_mut() {
if task.id == id {
return task;
}
}
}
panic!("Error: find_task: Task with this id not found.");
}

static NOOP_WAKER_VTABLE: RawWakerVTable =
RawWakerVTable::new(raw_clone, raw_wake, raw_wake_by_ref, raw_drop);
/// Task can put to sleep another task by ```id```.
pub fn put_to_sleep(id: TaskIdType) {
let res = unsafe { CooperativeTaskManager::find_task(id) };
let task = res;
match task.status {
TaskStatusType::Running => {
panic!("Error: put_to_sleep: Task with this id is currently running.");
}
TaskStatusType::Sleeping => {
panic!("Error: put_to_sleep: Task with this id is currently sleeping.");
}
TaskStatusType::Terminated => {
panic!(
"Error: put_to_sleep: Task with this id is terminated and recently will be removed."
);
}
_ => {
task.status = TaskStatusType::Sleeping;
}
}
}

let raw_waker = RawWaker::new(core::ptr::null::<()>(), &NOOP_WAKER_VTABLE);
unsafe { Waker::from_raw(raw_waker) }
}
/// Task can terminate and delete another task by ```id``` even if it executes.
pub fn terminate_task(id: TaskIdType) {
let res = unsafe { CooperativeTaskManager::find_task(id) };
let task = res;
task.status = TaskStatusType::Terminated;
CooperativeTaskManager::delete_task(task);
}

#[repr(C)]
/// Task manager representation. Based on round-robin scheduling without priorities.
pub struct CooperativeTaskManager {
/// Vector of tasks to execute.
pub(crate) tasks: Vec<FutureTask>,
/// Index of task, that should be executed.
pub(crate) task_to_execute_index: TaskNumberType,
}
/// One task manager iteration.
pub fn schedule() {
if CooperativeTaskManager::has_tasks() {
let task = CooperativeTaskManager::get_next_task();
match task.status {
TaskStatusType::Created => {
CooperativeTaskManager::setup_task(task);
}
TaskStatusType::Ready => {
task.status = TaskStatusType::Running;
(task.core.loop_fn)();
}
TaskStatusType::Running => {}
TaskStatusType::Sleeping => {}
TaskStatusType::Terminated => {
if (task.core.stop_condition_fn)() {
CooperativeTaskManager::delete_task(task);
} else {
task.status = TaskStatusType::Ready;
}
}
}
}
}

impl TaskManagerTrait for CooperativeTaskManager {
fn add_task(
fn create_task(
setup_fn: TaskSetupFunctionType,
loop_fn: TaskLoopFunctionType,
stop_condition_fn: TaskStopConditionFunctionType,
) {
priority: TaskPriorityType,
) -> CooperativeTask {
let task = Task {
setup_fn,
loop_fn,
stop_condition_fn,
};
let future_task = FutureTask {
task,
is_setup_completed: false,
};

unsafe {
TASK_MANAGER.next_task_id += 1;
let task_id = TASK_MANAGER.next_task_id;
CooperativeTask {
core: task,
id: task_id,
status: TaskStatusType::Created,
priority,
}
}
}

fn push_to_queue(task: CooperativeTask) {
unsafe {
TASK_MANAGER.tasks.push(future_task);
let task_vector = &mut TASK_MANAGER.tasks[task.priority];
task_vector.push(task);
}
}

fn start_task_manager() -> ! {
loop {
Self::task_manager_step();
fn setup_task(task: &mut CooperativeTask) {
(task.core.setup_fn)();
task.status = TaskStatusType::Ready;
}

fn delete_task(task: &mut CooperativeTask) {
unsafe {
let vec = &mut TASK_MANAGER.tasks[task.priority];
if let Some(pos) = vec.iter().position(|vec_task| vec_task.id == task.id) {
vec.remove(pos);
}
}
}
}

impl CooperativeTaskManager {
/// Creates new task manager.
pub(crate) const fn new() -> CooperativeTaskManager {
CooperativeTaskManager {
tasks: Vec::new(),
task_to_execute_index: 0,
fn has_tasks() -> bool {
unsafe {
for vec in TASK_MANAGER.tasks.iter() {
if !vec.is_empty() {
return true;
}
}
}
false
}

/// One step of task manager's work.
// TODO: Support priorities.
// TODO: Delete tasks from task vector if they are pending?
fn task_manager_step() {
if unsafe { !TASK_MANAGER.tasks.is_empty() } {
let waker = task_waker();

let task = unsafe { &mut TASK_MANAGER.tasks[TASK_MANAGER.task_to_execute_index] };
let mut task_future_pin = Pin::new(task);
let _ = task_future_pin
.as_mut()
.poll(&mut Context::from_waker(&waker));

unsafe {
if TASK_MANAGER.task_to_execute_index + 1 < TASK_MANAGER.tasks.len() {
TASK_MANAGER.task_to_execute_index += 1;
} else {
TASK_MANAGER.task_to_execute_index = 0;
fn get_next_task<'a>() -> &'a mut CooperativeTask {
unsafe {
for vec in TASK_MANAGER.tasks.iter_mut() {
if let Some(task) = vec.last_mut() {
return task;
}
}
}
panic!("Error: get_next_task: No tasks currently, waiting for new tasks.");
}

/// Starts task manager work. Returns after 1000 steps only for testing task_manager_step.
pub fn test_start_task_manager() {
for _n in 1..=1000 {
Self::task_manager_step();
CooperativeTaskManager::schedule();
}
}
}
2 changes: 1 addition & 1 deletion src/task_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ cfg_if::cfg_if! {
}

/// Operating system task manager.
/// By default [cooperative::CooperativeTaskManager] is used
/// By default, [cooperative::CooperativeTaskManager] is used
static mut TASK_MANAGER: TaskManager = TaskManager::new();

pub trait TaskManagerTrait {
Expand Down
Loading