ableos/kernel/src/task.rs

179 lines
4.2 KiB
Rust
Raw Normal View History

2023-08-22 08:52:30 -05:00
use {
2024-09-13 16:41:31 -05:00
alloc::{boxed::Box, sync::Arc},
2023-08-22 08:52:30 -05:00
core::{
future::Future,
pin::Pin,
2024-09-13 16:41:31 -05:00
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
2023-08-22 08:52:30 -05:00
},
crossbeam_queue::SegQueue,
slab::Slab,
};
pub fn yield_now() -> impl Future<Output = ()> {
struct YieldNow(bool);
impl Future for YieldNow {
type Output = ();
2024-09-13 16:41:31 -05:00
#[inline(always)]
2023-08-22 08:52:30 -05:00
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.0 {
Poll::Ready(())
} else {
self.0 = true;
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
YieldNow(false)
}
2024-11-17 14:29:32 -06:00
pub struct Executor {
tasks: Slab<Task>,
2024-09-13 16:41:31 -05:00
task_queue: Arc<TaskQueue>,
2023-08-22 08:52:30 -05:00
}
2024-11-17 14:29:32 -06:00
impl Executor {
pub fn new() -> Self {
2024-09-13 16:41:31 -05:00
Self {
2024-11-17 14:29:32 -06:00
tasks: Slab::new(),
2024-09-13 16:41:31 -05:00
task_queue: Arc::new(TaskQueue::new()),
}
}
#[inline]
2024-11-17 14:29:32 -06:00
pub fn spawn(&mut self, future: Pin<Box<dyn Future<Output = ()> + Send>>) -> usize {
let id = self.tasks.insert(Task::new(future));
self.task_queue.queue.push(id);
id
2023-08-22 08:52:30 -05:00
}
pub fn run(&mut self) {
2024-09-13 16:41:31 -05:00
let mut task_batch = [0; 32];
let mut batch_len = 0;
loop {
self.task_queue.batch_pop(&mut task_batch, &mut batch_len);
if batch_len == 0 {
if self.task_queue.is_empty() {
break;
} else {
continue;
}
2023-08-22 08:52:30 -05:00
}
2024-09-13 16:41:31 -05:00
for &id in &task_batch[..batch_len] {
if let Some(task) = self.tasks.get_mut(id) {
let waker = task
.waker
.get_or_insert_with(|| TaskWaker::new(id, Arc::clone(&self.task_queue)));
2023-08-22 08:52:30 -05:00
2024-09-13 16:41:31 -05:00
let waker = unsafe { Waker::from_raw(TaskWaker::into_raw_waker(waker)) };
let mut cx = Context::from_waker(&waker);
if let Poll::Ready(()) = task.poll(&mut cx) {
self.tasks.remove(id);
self.task_queue.free_tasks.push(id);
2023-08-22 08:52:30 -05:00
}
2024-09-13 16:41:31 -05:00
}
2023-08-22 08:52:30 -05:00
}
}
}
}
2024-11-17 14:29:32 -06:00
struct Task {
future: Pin<Box<dyn Future<Output = ()> + Send>>,
2024-09-13 16:41:31 -05:00
waker: Option<TaskWaker>,
2023-08-22 08:52:30 -05:00
}
2024-11-17 14:29:32 -06:00
impl Task {
2024-09-13 16:41:31 -05:00
#[inline(always)]
2024-11-17 14:29:32 -06:00
pub fn new(future: Pin<Box<dyn Future<Output = ()> + Send>>) -> Self {
2023-08-22 08:52:30 -05:00
Self {
2024-11-17 14:29:32 -06:00
future,
waker: None,
2023-08-22 08:52:30 -05:00
}
}
2024-09-13 16:41:31 -05:00
#[inline(always)]
2023-08-22 08:52:30 -05:00
fn poll(&mut self, cx: &mut Context) -> Poll<()> {
self.future.as_mut().poll(cx)
}
}
2024-09-13 16:41:31 -05:00
struct TaskWaker {
id: usize,
task_queue: Arc<TaskQueue>,
}
2023-08-22 08:52:30 -05:00
2024-09-13 16:41:31 -05:00
impl TaskWaker {
#[inline(always)]
fn new(id: usize, task_queue: Arc<TaskQueue>) -> Self {
Self { id, task_queue }
}
2023-08-22 08:52:30 -05:00
2024-09-13 16:41:31 -05:00
#[inline(always)]
fn wake(&self) {
self.task_queue.queue.push(self.id);
}
fn into_raw_waker(waker: &TaskWaker) -> RawWaker {
let ptr = waker as *const TaskWaker;
RawWaker::new(ptr.cast(), &VTABLE)
}
}
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw);
unsafe fn clone_raw(ptr: *const ()) -> RawWaker {
let waker = &*(ptr as *const TaskWaker);
TaskWaker::into_raw_waker(waker)
}
unsafe fn wake_raw(ptr: *const ()) {
let waker = &*(ptr as *const TaskWaker);
waker.wake();
}
unsafe fn wake_by_ref_raw(ptr: *const ()) {
let waker = &*(ptr as *const TaskWaker);
waker.wake();
2023-08-22 08:52:30 -05:00
}
2024-09-13 16:41:31 -05:00
unsafe fn drop_raw(_: *const ()) {}
struct TaskQueue {
queue: SegQueue<usize>,
next_task: usize,
free_tasks: SegQueue<usize>,
}
impl TaskQueue {
fn new() -> Self {
Self {
queue: SegQueue::new(),
next_task: 0,
free_tasks: SegQueue::new(),
}
}
#[inline(always)]
fn batch_pop(&self, output: &mut [usize], len: &mut usize) {
*len = 0;
while let Some(id) = self.queue.pop() {
output[*len] = id;
*len += 1;
if *len == output.len() {
break;
}
}
2023-08-22 08:52:30 -05:00
}
2024-09-13 16:41:31 -05:00
#[inline(always)]
fn is_empty(&self) -> bool {
self.queue.is_empty()
2023-08-22 08:52:30 -05:00
}
}