use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll, Waker};
use alloc::boxed::Box;
use alloc::sync::Arc;
use crate::os::sync::spinlock::IrqSpinlock;
use crate::os::time;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TaskId(pub u64);
impl TaskId {
pub fn new() -> Self {
use core::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(1);
TaskId(COUNTER.fetch_add(1, Ordering::Relaxed))
}
}
impl Default for TaskId {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskState {
Pending,
Woken,
Running,
Completed,
}
#[derive(Debug)]
pub struct TaskMetadata {
pub id: TaskId,
pub state: TaskState,
pub created_at: u64,
pub last_wake_at: u64,
pub last_run_at: Option<u64>,
pub run_count: u64,
}
impl TaskMetadata {
pub fn new(id: TaskId) -> Self {
let now = time::since_boot().as_millis() as u64;
Self {
id,
state: TaskState::Woken, created_at: now,
last_wake_at: now,
last_run_at: None,
run_count: 0,
}
}
pub fn is_expired(&self, timeout_ms: u64) -> bool {
if let Some(last_run) = self.last_run_at {
time::since_boot().as_millis() as u64 - last_run > timeout_ms
} else {
time::since_boot().as_millis() as u64 - self.created_at > timeout_ms
}
}
pub fn update_execution(&mut self) {
self.last_run_at = Some(time::since_boot().as_millis() as u64);
self.run_count += 1;
self.state = TaskState::Running;
}
pub fn mark_woken(&mut self) {
self.last_wake_at = time::since_boot().as_millis() as u64;
self.state = TaskState::Woken;
}
pub fn mark_completed(&mut self) {
self.state = TaskState::Completed;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct TaskPriority {
pub is_woken: bool,
pub timestamp: u64,
pub task_id: TaskId,
}
impl TaskPriority {
pub fn new(metadata: &TaskMetadata) -> Self {
Self {
is_woken: metadata.state == TaskState::Woken,
timestamp: metadata.last_wake_at,
task_id: metadata.id,
}
}
}
pub struct TaskRef {
pub metadata: IrqSpinlock<TaskMetadata>,
pub future: IrqSpinlock<Pin<Box<dyn Future<Output = ()> + Send>>>,
}
impl core::fmt::Debug for TaskRef {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let id = self.metadata.lock().id;
write!(f, "TaskRef({id:?})")
}
}
impl TaskRef {
pub fn new<F>(future: F, metadata: TaskMetadata) -> Self
where
F: Future<Output = ()> + Send + 'static,
{
Self {
metadata: IrqSpinlock::new(metadata),
future: IrqSpinlock::new(Box::pin(future)),
}
}
pub fn id(&self) -> TaskId {
self.metadata.lock().id
}
pub fn is_completed(&self) -> bool {
self.metadata.lock().state == TaskState::Completed
}
pub fn priority(&self) -> TaskPriority {
TaskPriority::new(&self.metadata.lock())
}
pub fn poll(&self, waker: &Waker) -> Poll<()> {
let metadata = &mut self.metadata.lock();
if metadata.state == TaskState::Completed {
return Poll::Ready(());
}
metadata.update_execution();
let future = &mut self.future.lock();
let mut cx = Context::from_waker(waker);
match future.as_mut().poll(&mut cx) {
Poll::Ready(()) => {
metadata.mark_completed();
Poll::Ready(())
}
Poll::Pending => {
metadata.state = TaskState::Pending;
Poll::Pending
}
}
}
}
#[derive(Debug, Clone)]
pub struct TaskHandle {
pub id: TaskId,
task_ref: Option<Arc<TaskRef>>,
}
impl TaskHandle {
pub fn new(id: TaskId) -> Self {
Self { id, task_ref: None }
}
pub fn with_ref(id: TaskId, task_ref: Arc<TaskRef>) -> Self {
Self {
id,
task_ref: Some(task_ref),
}
}
pub fn wake(&self) -> bool {
if let Some(task_ref) = &self.task_ref {
task_ref.metadata.lock().mark_woken();
true
} else {
false
}
}
}