mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
Small fixes to text and code examples
This commit is contained in:
@@ -1037,7 +1037,7 @@ default-features = false
|
|||||||
features = ["alloc"]
|
features = ["alloc"]
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, the crate depends on the standard library. To make it `no_std` compatible, we need to disable its default features and instead enable the `alloc` feature. <span class="gray">(Note that depending on the main `crossbeam` crate does not work here because it is missing an export of the `queue` module for `no_std`. I filed a [pull request](https://github.com/crossbeam-rs/crossbeam/pull/480) to fix this.)</span>
|
By default, the crate depends on the standard library. To make it `no_std` compatible, we need to disable its default features and instead enable the `alloc` feature. <span class="gray">(Note that depending on the main `crossbeam` crate does not work here because it is missing an export of the `queue` module for `no_std`. I filed a [pull request](https://github.com/crossbeam-rs/crossbeam/pull/480) to fix this, but it wasn't released on crates.io yet.)</span>
|
||||||
|
|
||||||
##### Queue Implementation
|
##### Queue Implementation
|
||||||
|
|
||||||
@@ -1084,19 +1084,23 @@ To fill the scancode queue, we create a new `add_scancode` function that we will
|
|||||||
```rust
|
```rust
|
||||||
// in src/task/keyboard.rs
|
// in src/task/keyboard.rs
|
||||||
|
|
||||||
|
use crate::println;
|
||||||
|
|
||||||
/// Called by the keyboard interrupt handler
|
/// Called by the keyboard interrupt handler
|
||||||
///
|
///
|
||||||
/// Must not block or allocate.
|
/// Must not block or allocate.
|
||||||
pub(crate) add_scancode(scancode: u8) {
|
pub(crate) fn add_scancode(scancode: u8) {
|
||||||
if let Ok(queue) = SCANCODE_QUEUE.try_get() {
|
if let Ok(queue) = SCANCODE_QUEUE.try_get() {
|
||||||
if let Err(_) = scancode_queue.push(scancode) {
|
if let Err(_) = queue.push(scancode) {
|
||||||
println!("WARNING: scancode queue full; dropping keyboard input");
|
println!("WARNING: scancode queue full; dropping keyboard input");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
println!("WARNING: scancode queue uninitialized");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We use the [`OnceCell::try_get`] to get a reference to the initialized queue. If the queue is not initialized yet, we do nothing and ignore the keyboard scancode. It's important that we don't try to initialize the queue in this function because it will be called by the interrupt handler, which should not perform heap allocations. Since this function should not be callable from our `main.rs`, we use the `pub(crate)` visibility to make it only available to our `lib.rs`.
|
We use the [`OnceCell::try_get`] to get a reference to the initialized queue. If the queue is not initialized yet, we ignore the keyboard scancode and print a warning. It's important that we don't try to initialize the queue in this function because it will be called by the interrupt handler, which should not perform heap allocations. Since this function should not be callable from our `main.rs`, we use the `pub(crate)` visibility to make it only available to our `lib.rs`.
|
||||||
|
|
||||||
[`OnceCell::try_get`]: https://docs.rs/conquer-once/0.2.0/conquer_once/raw/struct.OnceCell.html#method.try_get
|
[`OnceCell::try_get`]: https://docs.rs/conquer-once/0.2.0/conquer_once/raw/struct.OnceCell.html#method.try_get
|
||||||
|
|
||||||
@@ -1127,11 +1131,11 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(
|
|||||||
|
|
||||||
We removed all the keyboard handling code from this function and instead added a call to the `add_scancode` function. The rest of the function stays the same as before.
|
We removed all the keyboard handling code from this function and instead added a call to the `add_scancode` function. The rest of the function stays the same as before.
|
||||||
|
|
||||||
As expected, keypresses are no longer printed to the screen when we run our project using `cargo xrun` now. Instead, the scancodes are added to the `SCANCODE_QUEUE`. After 100 keystrokes, the queue becomes full and we see the warning about dropped keyboard input on the screen.
|
As expected, keypresses are no longer printed to the screen when we run our project using `cargo xrun` now. Instead, we see the warning that the scancode queue is uninitialized for every keystroke.
|
||||||
|
|
||||||
#### Scancode Stream
|
#### Scancode Stream
|
||||||
|
|
||||||
To read the scancodes from the queue in an asynchronous way, we create a new `ScancodeStream` type:
|
To initialize the `SCANCODE_QUEUE` and read the scancodes from the queue in an asynchronous way, we create a new `ScancodeStream` type:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// in src/task/keyboard.rs
|
// in src/task/keyboard.rs
|
||||||
@@ -1148,6 +1152,7 @@ impl ScancodeStream {
|
|||||||
_private: (),
|
_private: (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The purpose of the `_private` field is to prevent construction of the struct from outside of the module. This makes the `new` function the only way to construct the type. In the function, we first try to initialize the `SCANCODE_QUEUE` static. We panic if it is already initialized to ensure that only a single `ScancodeStream` type can be created.
|
The purpose of the `_private` field is to prevent construction of the struct from outside of the module. This makes the `new` function the only way to construct the type. In the function, we first try to initialize the `SCANCODE_QUEUE` static. We panic if it is already initialized to ensure that only a single `ScancodeStream` type can be created.
|
||||||
@@ -1198,6 +1203,7 @@ Now we can import and implement the `Stream` trait:
|
|||||||
```rust
|
```rust
|
||||||
// in src/task/keyboard.rs
|
// in src/task/keyboard.rs
|
||||||
|
|
||||||
|
use core::{pin::Pin, task::{Poll, Context}};
|
||||||
use futures_util::stream::Stream;
|
use futures_util::stream::Stream;
|
||||||
|
|
||||||
impl Stream for ScancodeStream {
|
impl Stream for ScancodeStream {
|
||||||
@@ -1234,6 +1240,8 @@ Let's use the [`AtomicWaker`] type to define a static `WAKER`:
|
|||||||
```rust
|
```rust
|
||||||
// in src/task/keyboard.rs
|
// in src/task/keyboard.rs
|
||||||
|
|
||||||
|
use futures_util::task::AtomicWaker;
|
||||||
|
|
||||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
static WAKER: AtomicWaker = AtomicWaker::new();
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -1249,7 +1257,7 @@ The contract defined by `poll`/`poll_next` requires that the task registers a wa
|
|||||||
impl Stream for ScancodeStream {
|
impl Stream for ScancodeStream {
|
||||||
type Item = u8;
|
type Item = u8;
|
||||||
|
|
||||||
fn poll_next(self: Pin<&mut Self>, context: &mut Context) -> Poll<Option<u8>> {
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<u8>> {
|
||||||
let queue = SCANCODE_QUEUE
|
let queue = SCANCODE_QUEUE
|
||||||
.try_get()
|
.try_get()
|
||||||
.expect("scancode queue not initialized");
|
.expect("scancode queue not initialized");
|
||||||
@@ -1260,8 +1268,8 @@ impl Stream for ScancodeStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WAKER.register(&cx.waker());
|
WAKER.register(&cx.waker());
|
||||||
match scancodes.pop() {
|
match queue.pop() {
|
||||||
Ok(scancode) => Poll::Ready(scancode),
|
Ok(scancode) => Poll::Ready(Some(scancode)),
|
||||||
Err(crossbeam_queue::PopError) => Poll::Pending,
|
Err(crossbeam_queue::PopError) => Poll::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1344,14 +1352,14 @@ Let's add the `print_keypresses` task to our executor in our `main.rs` to get wo
|
|||||||
```rust
|
```rust
|
||||||
// in src/main.rs
|
// in src/main.rs
|
||||||
|
|
||||||
use blog_os::task::keyboard;
|
|
||||||
|
|
||||||
fn kernel_main(boot_info: &'static BootInfo) -> ! {
|
fn kernel_main(boot_info: &'static BootInfo) -> ! {
|
||||||
|
use blog_os::task::keyboard;
|
||||||
|
|
||||||
// […] initialization routines, including `init_heap`
|
// […] initialization routines, including `init_heap`
|
||||||
|
|
||||||
let mut executor = SimpleExecutor::new();
|
let mut executor = SimpleExecutor::new();
|
||||||
executor.spawn(Task::new(example_task()));
|
executor.spawn(Task::new(example_task()));
|
||||||
executor.spawn(Task::new(keyboard::print_keypresses()));
|
executor.spawn(Task::new(keyboard::print_keypresses())); // new
|
||||||
executor.run();
|
executor.run();
|
||||||
|
|
||||||
// […] test_main, "it did not crash" message, hlt_loop
|
// […] test_main, "it did not crash" message, hlt_loop
|
||||||
|
|||||||
Reference in New Issue
Block a user