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

Feature/tasks #5

Merged
merged 7 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions Export.sh
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ alias check_esp32_s3="$Clear $ESP32_S3_environment_variables $Cargo_check $ESP32

alias clean="$Cargo clean"

alias doc="$Linux_environment_variables $Cargo doc"

export RUST_BACKTRACE=1

alias update_export="source Export.sh"
192 changes: 192 additions & 0 deletions src/Task/Manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// - Dependencies
// - - Local
use super::*;
// - - External
// - - - Standard library
use std::{
collections::HashMap,
sync::{Arc, RwLock},
};

/// Internal representation of a task.
struct Task_internal_type {
/// The thread that runs the task.
Thread: Thread_wrapper_type,
/// The identifiers of the children of the task.
Children: Vec<Task_identifier_type>,
}

/// A manager for tasks.
#[derive(Clone)]
pub struct Manager_type {
/// A map of all tasks managed by the manager.
Tasks: Arc<RwLock<HashMap<Task_identifier_type, Task_internal_type>>>,
}

impl Manager_type {
/// The identifier of the root task (the task that is created when the manager is created).
const Root_task_identifier: Task_identifier_type = 0;

pub fn New() -> Self {
let Manager = Manager_type {
Tasks: Arc::new(RwLock::new(HashMap::new())),
};

Manager
}

fn Get_new_task_identifier(&self) -> Task_identifier_type {
if self.Tasks.read().unwrap().len() == 0 {
return Self::Root_task_identifier;
}

for Process_identifier in 0..std::usize::MAX - 1 {
if !self.Tasks.read().unwrap().contains_key(&Process_identifier) {
return Process_identifier;
}
}
panic!("No more process identifier available."); // Should never happen since the maximum number of tasks is usize::MAX - 1 which is a lot.
}

pub fn Get_task_name(&self, Process_identifier: Task_identifier_type) -> Result<String, ()> {
match self.Tasks.read().unwrap().get(&Process_identifier) {
Some(Task) => match Task.Thread.Get_name() {
Some(Name) => Ok(Name.to_string()),
None => Err(()),
},
None => Err(()),
}
}

pub fn New_root_task<F>(&self, Stack_size: Option<usize>, Function: F)
where
F: FnOnce() + Send + 'static,
{
let Thread_wrapper = match Thread_wrapper_type::New("Xila", Stack_size, Function) {
Ok(Thread_wrapper) => Thread_wrapper,
Err(()) => panic!(),
};

let mut Tasks = self.Tasks.write().unwrap(); // Acquire lock

Tasks.insert(
Self::Root_task_identifier,
Task_internal_type {
Thread: Thread_wrapper,
Children: Vec::new(),
},
);
}

/// Create a new child task, returns the identifier of the child task.
/// # Arguments
/// * `Parent_task_identifier` - The identifier of the parent task.
/// * `Name` - The human readable name of the task.
/// * `Stack_size` - The size of the stack of the task.
/// * `Function` - The function that the task will execute.
///
pub fn New_task<F>(
&self,
Parent_task_identifier: Task_identifier_type,
Name: &str,
Stack_size: Option<usize>,
Function: F,
) -> Result<Task_identifier_type, ()>
where
F: FnOnce() + Send + 'static,
{
let Child_task_identifier = self.Get_new_task_identifier();

let mut Tasks = self.Tasks.write().unwrap(); // Acquire lock

let Parent_task = match Tasks.get_mut(&Parent_task_identifier) {
Some(Parent_task) => Parent_task,
None => return Err(()),
};

let Thread_wrapper = match Thread_wrapper_type::New(Name, Stack_size, Function) {
Ok(Thread_wrapper) => Thread_wrapper,
Err(()) => return Err(()),
};

Parent_task.Children.push(Child_task_identifier);

std::mem::drop(Tasks); // Force Release lock // TODO : Find a better way to do this
self.Tasks.write().unwrap().insert(
Child_task_identifier,
Task_internal_type {
Thread: Thread_wrapper,
Children: Vec::new(),
},
);

Ok(Child_task_identifier)
}

fn Delete_task(&self, Task_identifier: Task_identifier_type) -> Result<(), ()> {
let mut Tasks = self.Tasks.write().unwrap(); // Acquire lock

// - Remove task from hashmap and take ownership of it
let Task = match Tasks.remove(&Task_identifier) {
Some(Task) => Task,
None => return Err(()),
};

std::mem::drop(Tasks); // Force Release lock // TODO : Find a better way to do this

// - Waiting for thread to terminate
Task.Thread.Join().unwrap();

let mut R = Ok(());

for Child_task_identifier in Task.Children.iter() {
if self.Delete_task(*Child_task_identifier).is_err() {
R = Err(());
}
}

R
}

pub fn Get_current_task_identifier(&self) -> Result<Task_identifier_type, ()> {
let Tasks = self.Tasks.read().unwrap(); // Acquire lock

for (Task_identifier, Task) in Tasks.iter() {
if Task.Thread.Get_name().unwrap() == std::thread::current().name().unwrap() {
return Ok(*Task_identifier);
}
}

Err(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;

#[test]
fn Test() {
let Manager = Manager_type::New();

Manager.New_root_task(None, || {
Task_type::Sleep(Duration::from_millis(100));
});

let _ = Manager
.New_task(
Manager_type::Root_task_identifier,
"Child task",
None,
|| {
Task_type::Sleep(Duration::from_millis(100));
},
)
.unwrap();

Manager
.Delete_task(Manager_type::Root_task_identifier)
.unwrap();
}
}
59 changes: 59 additions & 0 deletions src/Task/Pipe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use std::sync::{Arc, RwLock};

struct Pipe_internal_type<const Buffer_size: usize> {
Buffer: [u8; Buffer_size],
Read_index: usize,
Write_index: usize,
}

pub struct Pipe_type<const Buffer_size: usize>(Arc<RwLock<Pipe_internal_type<Buffer_size>>>);

impl<const Buffer_size: usize> Pipe_type<Buffer_size> {
pub fn New() -> Self {
Self(Arc::new(RwLock::new(Pipe_internal_type {
Buffer: [0; Buffer_size],
Read_index: 0,
Write_index: 0,
})))
}

pub fn Write(&self, Data: &[u8]) -> Result<(), ()> {
if Data.len() > Buffer_size {
return Err(());
}

let mut Pipe = self.0.write().unwrap();

for Byte in Data {
// ? : Probably not the most efficient way to do this.
let Write_index = Pipe.Write_index; // * Make the borrow checker happy.
Pipe.Buffer[Write_index] = *Byte;
Pipe.Write_index += 1;

if Pipe.Write_index == Buffer_size {
Pipe.Write_index = 0;
}
}

Ok(())
}

pub fn Read(&self, Data: &mut [u8]) -> Result<(), ()> {
if Data.len() > Buffer_size {
return Err(());
}

let mut Pipe = self.0.write().unwrap();

for Byte in Data {
*Byte = Pipe.Buffer[Pipe.Read_index];
Pipe.Read_index += 1;

if Pipe.Read_index == Buffer_size {
Pipe.Read_index = 0;
}
}

Ok(())
}
}
1 change: 1 addition & 0 deletions src/Task/Prelude.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub use super::*;
83 changes: 83 additions & 0 deletions src/Task/Task.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use super::*;

pub type Task_identifier_type = usize;

/// A wrapper for individual tasks that are managed by [Manager_type].
pub struct Task_type<'a> {
/// The identifier of the task.
Identifier: Task_identifier_type,
/// A reference to the [Manager_type] that manages the task.
Manager: &'a Manager_type,
}

impl<'a> Task_type<'a> {
/// Internal method to create a new task.
fn New(Identifier: Task_identifier_type, Manager: &'a Manager_type) -> Self {
Self {
Identifier,
Manager,
}
}

/// Create a new child task.
pub fn New_child_task<F>(
&self,
Name: &str,
Stack_size: Option<usize>,
Function: F,
) -> Result<Task_type, ()>
where
F: FnOnce() + Send + 'static,
{
match self
.Manager
.New_task(self.Identifier, Name, Stack_size, Function)
{
Ok(Child_task_identifier) => Ok(Self::New(Child_task_identifier, self.Manager)),
Err(()) => Err(()),
}
}

pub fn Get_name(&self) -> Result<String, ()> {
self.Manager.Get_task_name(self.Identifier)
}

pub fn Get_identifier(&self) -> Task_identifier_type {
self.Identifier
}

pub fn Get_manager(&self) -> &'a Manager_type {
self.Manager
}

pub fn Get_current_task(Manager: &'a Manager_type) -> Result<Self, ()> {
let Current_task_identifier = Manager.Get_current_task_identifier()?;
Ok(Self::New(Current_task_identifier, Manager))
}

pub fn Sleep(Duration: std::time::Duration) {
Thread_wrapper_type::Sleep(Duration)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn Test() {
let Manager = Manager_type::New();

let Manager_copy = Manager.clone();

let _ = Manager.New_root_task(None, move || {
let Task = Task_type::Get_current_task(&Manager_copy).unwrap();

let _ = Task
.New_child_task("Child task", None, || {
Task_type::Sleep(std::time::Duration::from_millis(100));
})
.unwrap();
});
}
}
43 changes: 43 additions & 0 deletions src/Task/Thread.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use std::thread;

/// A wrapper around [std::thread::Thread].
pub struct Thread_wrapper_type(thread::JoinHandle<()>);

impl Thread_wrapper_type {
/// Creates a new thread with a given name, stack size and function.
pub fn New<F>(Name: &str, Stack_size: Option<usize>, Function: F) -> Result<Self, ()>
where
F: FnOnce() + Send + 'static,
{
let Thread_builder = thread::Builder::new().name(Name.to_string());

let Thread_builder = match Stack_size {
Some(Stack_size) => Thread_builder.stack_size(Stack_size),
None => Thread_builder,
};

let Join_handle = match Thread_builder.spawn(Function) {
Ok(Join_handle) => Join_handle,
Err(_) => return Err(()),
};

Ok(Self(Join_handle))
}

/// Block the current thread until the thread terminates.
pub fn Join(self) -> Result<(), ()> {
match self.0.join() {
Ok(_) => Ok(()),
Err(_) => Err(()),
}
}

/// Gets the name of the thread.
pub fn Get_name(&self) -> Option<&str> {
self.0.thread().name()
}

pub fn Sleep(Duration: std::time::Duration) {
std::thread::sleep(Duration);
}
}
Loading
Loading