RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / src / task / mod.rs
2305 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//! Cooperative multitasking with async/await.
//!
//! An `async fn` compiles to a state machine implementing `Future`. Calling
//! `poll` on it runs it until it either finishes (`Poll::Ready`) or has to
//! wait (`Poll::Pending`) -- e.g. for a key press. Before returning Pending,
//! the future hands the `Waker` from its `Context` to whoever will make
//! progress possible (here: the keyboard interrupt). When that happens,
//! `waker.wake()` tells the executor to poll the task again.
//!
//! "Cooperative": a task only gives up the CPU at an `.await`. A task that
//! loops forever without awaiting blocks every other task. (Compare the
//! preemptive threads in `thread.rs`.)

use alloc::boxed::Box;
use core::{
    future::Future,
    pin::Pin,
    sync::atomic::{AtomicU64, Ordering},
    task::{Context, Poll},
};

pub mod executor;
pub mod keyboard;
pub mod simple_executor;

/// A task = a heap-allocated, pinned future with no output.
/// `Pin` because async state machines may contain pointers into themselves,
/// so they must never move in memory once polled.
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)
    }
}

/// Unique id per task, so wakers can say *which* task to run again.
#[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))
    }
}

/// A future that returns Pending once (waking itself straight away), then
/// Ready. `yield_now().await` lets other tasks run -- handy for demos/tests.
pub async fn yield_now() {
    struct YieldNow(bool);
    impl Future for YieldNow {
        type Output = ();
        fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
            if self.0 {
                return Poll::Ready(());
            }
            self.0 = true;
            cx.waker().wake_by_ref(); // "poll me again soon"
            Poll::Pending
        }
    }
    YieldNow(false).await
}