Add async tasks support
Signed-off-by: Julien CLEMENT <julien.clement@epita.fr>
This commit is contained in:
parent
204b464f59
commit
9ae31eb2f6
93
Cargo.lock
generated
93
Cargo.lock
generated
@ -2,6 +2,12 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.1"
|
||||
@ -20,10 +26,79 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "conquer-once"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96eb12fb69466716fbae9009d389e6a30830ae8975e170eff2d2cff579f9efa3"
|
||||
dependencies = [
|
||||
"conquer-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "conquer-util"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"maybe-uninit",
|
||||
]
|
||||
|
||||
[[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 = "futures-core"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "julios"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"conquer-once",
|
||||
"crossbeam-queue",
|
||||
"futures-util",
|
||||
"lazy_static",
|
||||
"linked_list_allocator",
|
||||
"multiboot2",
|
||||
@ -61,6 +136,12 @@ dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-uninit"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||
|
||||
[[package]]
|
||||
name = "multiboot2"
|
||||
version = "0.1.0"
|
||||
@ -85,6 +166,18 @@ dependencies = [
|
||||
"x86_64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.6"
|
||||
|
14
Cargo.toml
14
Cargo.toml
@ -20,3 +20,17 @@ linked_list_allocator = "0.9.0"
|
||||
[dependencies.lazy_static]
|
||||
version = "1.0"
|
||||
features = ["spin_no_std"]
|
||||
|
||||
[dependencies.crossbeam-queue]
|
||||
version = "0.2.1"
|
||||
default-features = false
|
||||
features = ["alloc"]
|
||||
|
||||
[dependencies.conquer-once]
|
||||
version = "0.2.0"
|
||||
default-features = false
|
||||
|
||||
[dependencies.futures-util]
|
||||
version = "0.3.4"
|
||||
default-features = false
|
||||
features = ["alloc"]
|
||||
|
@ -1,32 +1,16 @@
|
||||
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
|
||||
use lazy_static::lazy_static;
|
||||
use super::{PICS, InterruptIndex};
|
||||
|
||||
use x86_64::instructions::port::Port;
|
||||
use spin::{self, Mutex};
|
||||
use crate::{print};
|
||||
use x86_64::structures::idt::{InterruptStackFrame};
|
||||
|
||||
pub const PS2_CONTROLLER_PORT: u16 = 0x60;
|
||||
|
||||
pub extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
lazy_static! {
|
||||
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(
|
||||
Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore)
|
||||
);
|
||||
}
|
||||
|
||||
let mut keyboard = KEYBOARD.lock();
|
||||
let mut port = Port::new(PS2_CONTROLLER_PORT);
|
||||
let scancode: u8 = unsafe { port.read() };
|
||||
|
||||
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
|
||||
if let Some(key) = keyboard.process_keyevent(key_event) {
|
||||
match key {
|
||||
DecodedKey::Unicode(character) => print!("{}", character),
|
||||
DecodedKey::RawKey(key) => print!("{:?}", key),
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::task::keyboard::add_scancode(scancode);
|
||||
|
||||
unsafe {
|
||||
PICS.lock()
|
||||
.notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
|
||||
|
@ -8,6 +8,7 @@ mod interrupts;
|
||||
mod memory;
|
||||
mod serial;
|
||||
mod vga;
|
||||
mod task;
|
||||
|
||||
//#[macro_use]
|
||||
extern crate alloc;
|
||||
@ -16,6 +17,7 @@ extern crate multiboot2;
|
||||
use core::panic::PanicInfo;
|
||||
use multiboot2::BootInformation;
|
||||
use vga::{Color, ColorCode};
|
||||
use task::{executor::Executor, Task, keyboard};
|
||||
|
||||
#[alloc_error_handler]
|
||||
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
|
||||
@ -53,8 +55,7 @@ pub extern "C" fn julios_main(multiboot_info_addr: usize) -> ! {
|
||||
println!("***JuliOS V0.1.0***");
|
||||
serial_println!("Hello serial");
|
||||
|
||||
use alloc::boxed::Box;
|
||||
let x = Box::new(41);
|
||||
|
||||
panic!("Kernel end of flow");
|
||||
let mut executor = Executor::new();
|
||||
executor.spawn(Task::new(keyboard::print_keypresses()));
|
||||
executor.run();
|
||||
}
|
||||
|
101
src/task/executor.rs
Normal file
101
src/task/executor.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use super::{Task, TaskId};
|
||||
use alloc::{collections::BTreeMap, sync::Arc, task::Wake};
|
||||
use core::task::{Context, Poll, Waker};
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
|
||||
pub struct Executor {
|
||||
tasks: BTreeMap<TaskId, Task>,
|
||||
task_queue: Arc<ArrayQueue<TaskId>>,
|
||||
waker_cache: BTreeMap<TaskId, Waker>
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
pub fn new() -> Self {
|
||||
Executor {
|
||||
tasks: BTreeMap::new(),
|
||||
task_queue: Arc::new(ArrayQueue::new(100)),
|
||||
waker_cache: BTreeMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> ! {
|
||||
loop {
|
||||
self.run_ready_tasks();
|
||||
self.sleep_if_idle();
|
||||
}
|
||||
}
|
||||
|
||||
fn sleep_if_idle(&self) {
|
||||
use x86_64::instructions::interrupts::{self, enable_and_hlt};
|
||||
interrupts::disable();
|
||||
|
||||
if self.task_queue.is_empty() {
|
||||
enable_and_hlt();
|
||||
} else {
|
||||
interrupts::enable();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn(&mut self, task: Task) {
|
||||
let task_id = task.id;
|
||||
if self.tasks.insert(task.id, task).is_some() {
|
||||
panic!("Duplicate task ID");
|
||||
}
|
||||
self.task_queue.push(task_id).expect("Task queue full");
|
||||
}
|
||||
|
||||
fn run_ready_tasks(&mut self) {
|
||||
let Self {
|
||||
tasks,
|
||||
task_queue,
|
||||
waker_cache
|
||||
} = self; // Executor destructuring
|
||||
|
||||
while let Ok(task_id) = task_queue.pop() {
|
||||
let task = match tasks.get_mut(&task_id) {
|
||||
Some(task) => task,
|
||||
None => continue, // Task does not exist anymore
|
||||
};
|
||||
let waker = waker_cache
|
||||
.entry(task_id)
|
||||
.or_insert_with(|| TaskWaker::new(task_id, task_queue.clone()));
|
||||
let mut context = Context::from_waker(waker);
|
||||
match task.poll(&mut context) {
|
||||
Poll::Ready(()) => {
|
||||
// task is done
|
||||
tasks.remove(&task_id);
|
||||
waker_cache.remove(&task_id);
|
||||
}
|
||||
Poll::Pending => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TaskWaker {
|
||||
task_id: TaskId,
|
||||
task_queue: Arc<ArrayQueue<TaskId>>
|
||||
}
|
||||
|
||||
impl TaskWaker {
|
||||
fn new(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
|
||||
Waker::from(Arc::new(TaskWaker {
|
||||
task_id,
|
||||
task_queue
|
||||
}))
|
||||
}
|
||||
|
||||
fn wake_task(&self) {
|
||||
self.task_queue.push(self.task_id).expect("Task queue full");
|
||||
}
|
||||
}
|
||||
|
||||
impl Wake for TaskWaker {
|
||||
fn wake(self: Arc<Self>) {
|
||||
self.wake_task();
|
||||
}
|
||||
|
||||
fn wake_by_ref(self: &Arc<Self>) {
|
||||
self.wake_task();
|
||||
}
|
||||
}
|
80
src/task/keyboard.rs
Normal file
80
src/task/keyboard.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use crate::{println, print};
|
||||
|
||||
use conquer_once::spin::OnceCell;
|
||||
use core::{pin::Pin, task::{Context, Poll}};
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
use futures_util::task::AtomicWaker;
|
||||
use futures_util::stream::{Stream, StreamExt};
|
||||
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
|
||||
|
||||
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
|
||||
|
||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||
|
||||
pub struct ScancodeStream {
|
||||
_private: (), // Makes ScancodeStream constructable only
|
||||
// inside the module with ScancodeStream::new()
|
||||
}
|
||||
|
||||
impl ScancodeStream {
|
||||
pub fn new() -> Self {
|
||||
SCANCODE_QUEUE.try_init_once(|| ArrayQueue::new(100))
|
||||
.expect("ScancodeStream::new should only be called once");
|
||||
ScancodeStream { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for ScancodeStream {
|
||||
type Item = u8;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<u8>> {
|
||||
let queue = SCANCODE_QUEUE
|
||||
.try_get()
|
||||
.expect("Keyboard scancode queue not initialized");
|
||||
|
||||
if let Ok(scancode) = queue.pop() {
|
||||
return Poll::Ready(Some(scancode));
|
||||
}
|
||||
|
||||
WAKER.register(&cx.waker());
|
||||
|
||||
match queue.pop() {
|
||||
Ok(scancode) => {
|
||||
WAKER.take();
|
||||
Poll::Ready(Some(scancode))
|
||||
},
|
||||
Err(crossbeam_queue::PopError) => Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_scancode(scancode: u8) {
|
||||
if let Ok(queue) = SCANCODE_QUEUE.try_get() {
|
||||
if let Err(_) = queue.push(scancode) {
|
||||
println!("Keyboard scancode queue full, dropping input {}", scancode);
|
||||
}
|
||||
else {
|
||||
WAKER.wake();
|
||||
}
|
||||
}
|
||||
else {
|
||||
println!("Keyboard scancode queue uninitialized");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn print_keypresses() {
|
||||
let mut scancodes = ScancodeStream::new();
|
||||
let mut keyboard: Keyboard<layouts::Us104Key, ScancodeSet1> =
|
||||
Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore);
|
||||
|
||||
while let Some(scancode) = scancodes.next().await {
|
||||
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
|
||||
if let Some(key) = keyboard.process_keyevent(key_event) {
|
||||
match key {
|
||||
DecodedKey::Unicode(character) => print!("{}", character),
|
||||
DecodedKey::RawKey(key) => print!("{:?}", key),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
src/task/mod.rs
Normal file
36
src/task/mod.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
use core::task::{Context, Poll};
|
||||
use alloc::boxed::Box;
|
||||
|
||||
pub mod executor;
|
||||
pub mod keyboard;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct TaskId(u64);
|
||||
|
||||
impl TaskId {
|
||||
fn new() -> Self {
|
||||
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
|
||||
TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Task {
|
||||
id: TaskId,
|
||||
future: Pin<Box<dyn Future<Output = ()>>>
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn new(future: impl Future<Output = ()> + 'static) -> Task {
|
||||
Task {
|
||||
id: TaskId::new(),
|
||||
future: Box::pin(future)
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&mut self, context: &mut Context) -> Poll<()> {
|
||||
self.future.as_mut().poll(context)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user