Skip to content

Commit 62b7fc4

Browse files
committed
add async/await support
- add Executor to spawn and run tasks - add ScancodeStream to asynchronously handle keyboard input
1 parent 97ab6b9 commit 62b7fc4

9 files changed

+399
-24
lines changed

Cargo.lock

+93
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+15-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ version = "0.6.0"
1414
panic = "abort" # disable stack unwinding on panic
1515

1616
[dependencies]
17-
bootloader = {version = "0.9.8", features = ["map_physical_memory"]}# for BIOS bootloader.
17+
bootloader = {version = "0.9.8", features = ["map_physical_memory"]}# for BIOS bootloader.
1818
linked_list_allocator = "0.8.0"
1919
pc-keyboard = "0.5.0" # interprets PS/2 keyboard scancode sets 1 and 2
2020
pic8259_simple = "0.2.0" # keyboard interrupts
@@ -23,6 +23,20 @@ uart_16550 = "0.2.0" # serial port for qemu
2323
volatile = "0.2.6" # to mark VGA buffer as volatile. TODO: try upgrading
2424
x86_64 = "0.13.2"
2525

26+
[dependencies.crossbeam-queue]
27+
default-features = false
28+
features = ["alloc"]
29+
version = "0.2.1"
30+
31+
[dependencies.conquer-once]
32+
default-features = false
33+
version = "0.2.0"
34+
35+
[dependencies.futures-util]
36+
default-features = false
37+
features = ["alloc"]
38+
version = "0.3.4"
39+
2640
[dependencies.lazy_static]
2741
features = ["spin_no_std"]
2842
version = "1.0"

src/interrupts.rs

+2-20
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use pic8259_simple::ChainedPics;
33
use spin;
44
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
55

6-
use crate::{gdt, print, println};
6+
use crate::{gdt, println};
77

88
pub fn init_idt() {
99
IDT.load();
@@ -66,30 +66,12 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptSt
6666
}
6767

6868
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
69-
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
70-
use spin::Mutex;
7169
use x86_64::instructions::port::Port;
7270

7371
// Read the scancode. The keyboard controller won't send more interrupts until we do so.
74-
75-
lazy_static! {
76-
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> = Mutex::new(
77-
Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore)
78-
);
79-
}
80-
81-
let mut keyboard = KEYBOARD.lock();
8272
let mut port = Port::new(0x60);
83-
8473
let scancode: u8 = unsafe { port.read() };
85-
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
86-
if let Some(key) = keyboard.process_keyevent(key_event) {
87-
match key {
88-
DecodedKey::Unicode(character) => print!("{}", character),
89-
DecodedKey::RawKey(key) => print!("{:?}", key),
90-
}
91-
}
92-
}
74+
crate::task::keyboard::add_scancode(scancode);
9375

9476
unsafe {
9577
PICS.lock()

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![feature(alloc_error_handler)]
55
#![feature(const_mut_refs)]
66
#![feature(custom_test_frameworks)]
7+
// #![feature(wake_trait)]
78
#![test_runner(crate::test_runner)]
89
#![reexport_test_harness_main = "test_main"]
910

@@ -19,6 +20,7 @@ pub mod gdt;
1920
pub mod interrupts;
2021
pub mod memory;
2122
pub mod serial;
23+
pub mod task;
2224
pub mod vga_buffer;
2325

2426
#[alloc_error_handler]

src/main.rs

+21-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
extern crate alloc;
88

99
use alloc::{boxed::Box, rc::Rc, vec, vec::Vec};
10-
use blog_os::{allocator, memory::BootInfoFrameAllocator, println};
10+
use blog_os::{
11+
allocator,
12+
memory::BootInfoFrameAllocator,
13+
println,
14+
task::{executor::Executor, keyboard},
15+
};
1116
use bootloader::{entry_point, BootInfo};
1217
use core::panic::PanicInfo;
1318

@@ -18,6 +23,8 @@ pub fn kernel_main(boot_info: &'static BootInfo) -> ! {
1823
use blog_os::memory;
1924
use x86_64::VirtAddr;
2025

26+
use blog_os::task::Task;
27+
2128
println!("Hello World{}", "!");
2229
blog_os::init();
2330

@@ -53,8 +60,10 @@ pub fn kernel_main(boot_info: &'static BootInfo) -> ! {
5360
#[cfg(test)]
5461
test_main();
5562

56-
println!("It did not crash!");
57-
blog_os::hlt_loop();
63+
let mut executor = Executor::new();
64+
executor.spawn(Task::new(example_task()));
65+
executor.spawn(Task::new(keyboard::print_keypresses()));
66+
executor.run();
5867
}
5968

6069
/// Panic handler. Prints to the VGA buffer
@@ -71,3 +80,12 @@ fn panic(info: &PanicInfo) -> ! {
7180
fn panic(info: &PanicInfo) -> ! {
7281
blog_os::test_panic_handler(info)
7382
}
83+
84+
async fn async_number() -> u32 {
85+
42
86+
}
87+
88+
async fn example_task() {
89+
let number = async_number().await;
90+
println!("async number: {}", number);
91+
}

src/task/executor.rs

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use super::{Task, TaskId};
2+
use alloc::{collections::BTreeMap, sync::Arc, task::Wake};
3+
use core::task::{Context, Poll, Waker};
4+
use crossbeam_queue::ArrayQueue;
5+
6+
pub struct Executor {
7+
tasks: BTreeMap<TaskId, Task>,
8+
task_queue: Arc<ArrayQueue<TaskId>>,
9+
waker_cache: BTreeMap<TaskId, Waker>,
10+
}
11+
12+
impl Executor {
13+
pub fn new() -> Self {
14+
Executor {
15+
tasks: BTreeMap::new(),
16+
task_queue: Arc::new(ArrayQueue::new(100)),
17+
waker_cache: BTreeMap::new(),
18+
}
19+
}
20+
21+
pub fn spawn(&mut self, task: Task) {
22+
let task_id = task.id;
23+
if self.tasks.insert(task.id, task).is_some() {
24+
panic!("task with same ID already in tasks");
25+
}
26+
self.task_queue.push(task_id).expect("queue full");
27+
}
28+
29+
pub fn run(&mut self) -> ! {
30+
loop {
31+
self.run_ready_tasks();
32+
self.sleep_if_idle();
33+
}
34+
}
35+
36+
fn run_ready_tasks(&mut self) {
37+
// destructure `self` to avoid borrow checker errors
38+
let Self {
39+
tasks,
40+
task_queue,
41+
waker_cache,
42+
} = self;
43+
44+
while let Ok(task_id) = task_queue.pop() {
45+
let task = match tasks.get_mut(&task_id) {
46+
Some(task) => task,
47+
None => continue, // task no longer exists
48+
};
49+
let waker = waker_cache
50+
.entry(task_id)
51+
.or_insert_with(|| TaskWaker::new(task_id, task_queue.clone()));
52+
let mut context = Context::from_waker(waker);
53+
match task.poll(&mut context) {
54+
Poll::Ready(()) => {
55+
// task done -> remove it and its cached waker
56+
tasks.remove(&task_id);
57+
waker_cache.remove(&task_id);
58+
}
59+
Poll::Pending => {}
60+
}
61+
}
62+
}
63+
64+
fn sleep_if_idle(&self) {
65+
use x86_64::instructions::interrupts::{self, enable_and_hlt};
66+
// Sleep until an interrupt happens, so we aren't spinning the CPU
67+
// Need to disable interrupts before hlt to prevent a race condition.
68+
interrupts::disable();
69+
if self.task_queue.is_empty() {
70+
enable_and_hlt();
71+
} else {
72+
interrupts::enable();
73+
}
74+
}
75+
}
76+
77+
struct TaskWaker {
78+
task_id: TaskId,
79+
task_queue: Arc<ArrayQueue<TaskId>>,
80+
}
81+
82+
impl TaskWaker {
83+
fn new(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
84+
Waker::from(Arc::new(TaskWaker {
85+
task_id,
86+
task_queue,
87+
}))
88+
}
89+
90+
fn wake_task(&self) {
91+
self.task_queue.push(self.task_id).expect("task_queue full");
92+
}
93+
}
94+
95+
impl Wake for TaskWaker {
96+
fn wake(self: Arc<Self>) {
97+
self.wake_task();
98+
}
99+
100+
fn wake_by_ref(self: &Arc<Self>) {
101+
self.wake_task();
102+
}
103+
}

0 commit comments

Comments
 (0)