From 6329274f02d7c56b3912946b484d0f26f7608a76 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Fri, 28 Feb 2020 11:42:36 +0100 Subject: [PATCH] Create a basic executor based on crossbeam_queue --- Cargo.lock | 33 ++++++++++++++ Cargo.toml | 4 ++ src/lib.rs | 2 + src/main.rs | 31 +++++++++++-- src/task/executor.rs | 102 +++++++++++++++++++++++++++++++++++++++++++ src/task/mod.rs | 1 + 6 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 src/task/executor.rs create mode 100644 src/task/mod.rs diff --git a/Cargo.lock b/Cargo.lock index c5a74880..bf4bb669 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,12 @@ dependencies = [ "nodrop", ] +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + [[package]] name = "bit_field" version = "0.9.0" @@ -26,6 +32,7 @@ name = "blog_os" version = "0.1.0" dependencies = [ "bootloader", + "crossbeam-queue", "lazy_static", "linked_list_allocator", "pc-keyboard", @@ -51,12 +58,38 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cpuio" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa" +[[package]] +name = "crossbeam-queue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", +] + [[package]] name = "lazy_static" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index ae67fe5a..c5864d92 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,10 @@ linked_list_allocator = "0.6.4" version = "1.0" features = ["spin_no_std"] +[dependencies.crossbeam-queue] +version = "0.2.1" +default-features = false +features = ["alloc"] [package.metadata.bootimage] test-args = [ diff --git a/src/lib.rs b/src/lib.rs index e79038ae..5550f263 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ #![feature(alloc_error_handler)] #![feature(const_fn)] #![feature(alloc_layout_extra)] +#![feature(wake_trait)] #![feature(const_in_array_repeat_expressions)] #![test_runner(crate::test_runner)] #![reexport_test_harness_main = "test_main"] @@ -18,6 +19,7 @@ pub mod gdt; pub mod interrupts; pub mod memory; pub mod serial; +pub mod task; pub mod vga_buffer; pub fn init() { diff --git a/src/main.rs b/src/main.rs index 84d42343..16a46706 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,11 +51,22 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! { Rc::strong_count(&cloned_reference) ); - #[cfg(test)] - test_main(); + use blog_os::task::executor::Executor; - println!("It did not crash!"); - blog_os::hlt_loop(); + let mut executor = Executor::new(); + let spawner = executor.create_spawner(); + spawner.spawn(bar()); + + spawner.spawn(async { + #[cfg(test)] + test_main(); + }); + + spawner.spawn(async { + println!("It did not crash!"); + }); + + executor.run(); } /// This function is called on panic. @@ -71,3 +82,15 @@ fn panic(info: &PanicInfo) -> ! { fn panic(info: &PanicInfo) -> ! { blog_os::test_panic_handler(info) } + +async fn test() -> u32 { + 42 +} + +async fn foo() -> u32 { + test().await * 2 +} + +async fn bar() { + println!("foo result: {}", foo().await); +} diff --git a/src/task/executor.rs b/src/task/executor.rs new file mode 100644 index 00000000..2aeed4e5 --- /dev/null +++ b/src/task/executor.rs @@ -0,0 +1,102 @@ +use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, task::Wake}; +use core::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; +use crossbeam_queue::SegQueue; + +pub type Task = Pin>>; +type TaskQueue = SegQueue; +type TaskId = usize; +type WakeQueue = SegQueue; + +pub struct Executor { + task_queue: Arc, + wake_queue: Arc, + pending_tasks: BTreeMap, +} + +impl Executor { + pub fn new() -> Self { + Executor { + task_queue: Arc::new(TaskQueue::new()), + wake_queue: Arc::new(WakeQueue::new()), + pending_tasks: BTreeMap::new(), + } + } + + pub fn create_spawner(&self) -> Spawner { + Spawner { + task_queue: self.task_queue.clone(), + } + } + + pub fn run(&mut self) -> ! { + loop { + // wakeup waiting tasks + while let Ok(task_id) = self.wake_queue.pop() { + let task = self + .pending_tasks + .remove(&task_id) + .expect("woken task not found in pending_tasks"); + self.task_queue.push(task); + } + // run ready tasks + while let Ok(mut task) = self.task_queue.pop() { + let waker = self.create_waker(&task).into(); + let mut context = Context::from_waker(&waker); + match task.as_mut().poll(&mut context) { + Poll::Ready(()) => {} // task done + Poll::Pending => { + // add task to pending_tasks list and wait for wakeup + let task_id = Self::task_id(&task); + self.pending_tasks.insert(task_id, task); + } + } + } + // wait for next interrupt if there is nothing left to do + if self.wake_queue.is_empty() { + crate::hlt_loop(); + } + } + } + + fn task_id(task: &Task) -> TaskId { + let future_ref: &dyn Future = &*task; + future_ref as *const _ as *const () as usize + } + + fn create_waker(&self, task: &Task) -> Arc { + Arc::new(Waker { + wake_queue: self.wake_queue.clone(), + task_id: Self::task_id(task), + }) + } +} + +#[derive(Debug, Clone)] +pub struct Spawner { + task_queue: Arc, +} + +impl Spawner { + pub fn spawn(&self, task: impl Future + 'static) { + self.task_queue.push(Box::pin(task)) + } +} + +pub struct Waker { + wake_queue: Arc, + task_id: TaskId, +} + +impl Wake for Waker { + fn wake(self: Arc) { + self.wake_by_ref(); + } + + fn wake_by_ref(self: &Arc) { + self.wake_queue.push(self.task_id); + } +} diff --git a/src/task/mod.rs b/src/task/mod.rs new file mode 100644 index 00000000..0c95fdab --- /dev/null +++ b/src/task/mod.rs @@ -0,0 +1 @@ +pub mod executor;