use crate::executor::Runnable;
use crate::util::RingBuf;
use crate::{println, singleton, sys};
use alloc::boxed::Box;
use alloc::collections::VecDeque;
use alloc::string::String;
use alloc::vec::Vec;
use core::future::Future;
use core::option::Option;
use core::pin::Pin;
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
pub type TaskId = u16;
pub type TaskCountType = TaskId;
pub type ExitCode = i8;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ExitStatus {
Exited(ExitCode), ErrorPid, NotExist, NotRunning, Aborted, }
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Signal {
SIGINT, SIGTERM, SIGKILL, SIGSTOP, SIGCONT, SIGUSR(u8), SIGNULL, }
impl Default for Signal {
fn default() -> Self {
Signal::SIGNULL
}
}
#[derive(Clone, Copy, Debug)]
pub enum SignalAction {
Ignore, Terminate(ExitCode), Pause, Continue, }
pub struct Task {
id: TaskId,
cmd: String,
future: Pin<Box<dyn Future<Output = ExitCode>>>,
paused: bool, exited: Option<ExitCode>, waiters: TaskCountType, pending_signals: RingBuf<Signal, 4>, signal_handler: Option<Box<dyn Fn(Signal) -> SignalAction>>, }
impl Task {
pub fn new(id: TaskId, cmd: String, future: Pin<Box<dyn Future<Output = ExitCode>>>) -> Self {
Self {
id,
cmd,
future,
paused: false,
exited: None,
waiters: 0,
pending_signals: RingBuf::new(),
signal_handler: None,
}
}
}
pub struct Executor {
tasks: VecDeque<Task>,
next_id_hint: TaskId,
current_task_id: Option<TaskId>,
}
singleton!(Executor {
tasks: VecDeque::new(),
next_id_hint: 0,
current_task_id: None,
});
impl Executor {
fn next_id(&mut self) -> TaskId {
let mut candidate = self.next_id_hint;
let start = candidate;
loop {
if !self.tasks.iter().any(|task| task.id == candidate) {
self.next_id_hint = candidate.wrapping_add(1);
return candidate;
}
candidate = candidate.wrapping_add(1);
if candidate == start {
panic!("No more available task IDs");
}
}
}
pub fn spawn(
cmd: impl Into<String>,
future: Pin<Box<dyn Future<Output = ExitCode>>>,
) -> TaskId {
let executor = Executor::get_mut();
let id = executor.next_id();
executor.tasks.push_back(Task::new(id, cmd.into(), future));
id
}
pub fn spawn_runnable(runner: Runnable, args: &[String]) -> TaskId {
let executor = Executor::get_mut();
let id = executor.next_id();
executor
.tasks
.push_back(Task::new(id, runner.get_name(), runner.run(args)));
id
}
pub fn default_signal_handler(signal: Signal) -> SignalAction {
match signal {
Signal::SIGINT | Signal::SIGTERM => SignalAction::Terminate(-1),
Signal::SIGKILL => SignalAction::Terminate(-9),
Signal::SIGSTOP => SignalAction::Pause,
Signal::SIGCONT => SignalAction::Continue,
Signal::SIGUSR(_) => SignalAction::Ignore,
Signal::SIGNULL => SignalAction::Ignore,
}
}
pub fn run() {
let executor = Executor::get_mut();
let waker = unsafe { Waker::from_raw(dummy_raw_waker()) };
let mut context = Context::from_waker(&waker);
loop {
let task: &mut Task = {
if let Some(task) = executor.tasks.front_mut() {
task
} else {
break; }
};
if task.exited.is_some() {
if task.waiters == 0 {
executor.tasks.pop_front(); } else {
executor.tasks.rotate_left(1); }
continue;
}
executor.current_task_id = Some(task.id);
while let Some(signal) = task.pending_signals.pop() {
let action = if let Some(ref handler) = task.signal_handler {
match signal {
Signal::SIGKILL => SignalAction::Terminate(-9),
Signal::SIGSTOP => SignalAction::Pause,
Signal::SIGNULL => SignalAction::Ignore,
_ => handler(signal),
}
} else {
Self::default_signal_handler(signal)
};
match action {
SignalAction::Terminate(code) => {
task.exited = Some(code);
task.paused = false;
break;
}
SignalAction::Ignore => continue,
SignalAction::Pause => {
task.paused = true;
continue;
}
SignalAction::Continue => {
task.paused = false;
continue;
}
}
}
if !task.paused {
match task.future.as_mut().poll(&mut context) {
Poll::Ready(exit_code) => {
task.exited = Some(exit_code); if task.waiters == 0 {
executor.tasks.pop_front();
} else {
executor.tasks.rotate_left(1); }
}
Poll::Pending => {
executor.tasks.rotate_left(1); }
}
}
let _ = executor.current_task_id.take().unwrap();
}
}
#[allow(unused)]
pub fn has_tasks() -> bool {
!Self::get_mut().tasks.is_empty()
}
#[allow(unused)]
pub fn task_count() -> usize {
Self::get_mut().tasks.len()
}
pub fn task_list() -> Vec<(TaskId, String)> {
Self::get_mut()
.tasks
.iter()
.map(|task| (task.id, task.cmd.clone()))
.collect()
}
pub fn current_task_id() -> Option<TaskId> {
Self::get_mut().current_task_id
}
pub fn kill(id: TaskId) -> bool {
Self::send_signal(id, Signal::SIGTERM)
}
pub async fn exit(exit_code: ExitCode) -> ! {
let current_id = match Self::current_task_id() {
Some(id) => id,
None => {
panic!("BUG: exit() called outside of task context");
}
};
if let Some(task) = Self::get_mut()
.tasks
.iter_mut()
.find(|t| t.id == current_id)
{
task.exited = Some(exit_code);
}
sys::yield_now().await;
panic!("BUG: exit() should not return");
}
#[allow(unused)]
pub fn is_running(id: TaskId) -> bool {
if Self::current_task_id() == Some(id) {
return true;
}
let executor = Executor::get_mut();
executor.tasks.iter().any(|task| task.id == id)
}
#[allow(unused)]
pub async fn wait(id: TaskId) -> ExitStatus {
let current_id = match Self::current_task_id() {
Some(id) => id,
None => {
return ExitStatus::NotRunning;
}
};
if id == current_id {
return ExitStatus::ErrorPid;
}
if let Some(task) = Self::get_mut().tasks.iter_mut().find(|t| t.id == id) {
task.waiters = task.waiters.wrapping_add(1);
} else {
return ExitStatus::NotExist;
}
loop {
sys::yield_now().await;
if let Some(task) = Self::get_mut().tasks.iter_mut().find(|t| t.id == id) {
if let Some(exit_code) = task.exited {
task.waiters = task.waiters.wrapping_sub(1);
return ExitStatus::Exited(exit_code);
}
} else {
return ExitStatus::Aborted;
}
}
}
pub fn send_signal(target_id: TaskId, signal: Signal) -> bool {
if let Some(task) = Self::get_mut().tasks.iter_mut().find(|t| t.id == target_id) {
task.pending_signals.push(signal);
return true;
}
false
}
pub fn register_signal_handler<F>(handler: F)
where
F: Fn(Signal) -> SignalAction + 'static,
{
if let Some(current_id) = Self::current_task_id() {
if let Some(task) = Self::get_mut()
.tasks
.iter_mut()
.find(|t| t.id == current_id)
{
task.signal_handler = Some(Box::new(handler));
}
} else {
println!("No current task");
}
}
}
fn dummy_raw_waker() -> RawWaker {
fn clone(_: *const ()) -> RawWaker {
dummy_raw_waker()
}
fn wake(_: *const ()) {}
fn wake_by_ref(_: *const ()) {}
fn drop(_: *const ()) {}
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
RawWaker::new(core::ptr::null::<()>(), &VTABLE)
}