feat: Simple executor
This commit is contained in:
parent
593713e1a9
commit
af04b184f4
69
Cargo.lock
generated
69
Cargo.lock
generated
@ -110,6 +110,21 @@ version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
||||
|
||||
[[package]]
|
||||
name = "conquer-once"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d008a441c0f269f36ca13712528069a86a3e60dffee1d98b976eb3b0b2160b4"
|
||||
dependencies = [
|
||||
"conquer-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "conquer-util"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e763eef8846b13b380f37dfecda401770b0ca4e56e95170237bd7c25c7db3582"
|
||||
|
||||
[[package]]
|
||||
name = "crc"
|
||||
version = "3.3.0"
|
||||
@ -125,6 +140,21 @@ version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "cure-builder"
|
||||
version = "0.1.0"
|
||||
@ -139,6 +169,9 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"acpi",
|
||||
"bootloader_api",
|
||||
"conquer-once",
|
||||
"crossbeam-queue",
|
||||
"futures-util",
|
||||
"lazy_static",
|
||||
"linked_list_allocator",
|
||||
"raw-cpuid",
|
||||
@ -179,6 +212,30 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.3"
|
||||
@ -305,6 +362,18 @@ dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.101"
|
||||
|
||||
@ -12,6 +12,20 @@ raw-cpuid = "11.6.0"
|
||||
acpi = "6.0.1"
|
||||
linked_list_allocator = "0.10.5"
|
||||
|
||||
[dependencies.crossbeam-queue]
|
||||
version = "0.3.12"
|
||||
default-features = false
|
||||
features = ["alloc"]
|
||||
|
||||
[dependencies.conquer-once]
|
||||
version = "0.4.0"
|
||||
default-features = false
|
||||
|
||||
[dependencies.futures-util]
|
||||
version = "0.3.31"
|
||||
default-features = false
|
||||
features = ["alloc"]
|
||||
|
||||
#[package.metadata.bootloader]
|
||||
#map-physical-memory = true
|
||||
#physical-memory-offset = "0xFFFF800000000000"
|
||||
@ -9,12 +9,13 @@ use crate::tty::tty;
|
||||
use crate::kprintln;
|
||||
use crate::kernel::k_main;
|
||||
use crate::klibc::logger::{init, LogLevel, LoggerConfig};
|
||||
use crate::klibc::malloc;
|
||||
use crate::klibc::mem;
|
||||
use crate::{log_debug, log_error, log_info, log_trace, log_warn};
|
||||
use crate::drivers::keyboard;
|
||||
use crate::hal::{acpi, cpu, rtc, timer};
|
||||
use crate::hal::apic::{ioapic, lapic};
|
||||
use crate::hal::cpu::cpu_enable_interrupts;
|
||||
use crate::task::executor;
|
||||
|
||||
fn _logger_init() {
|
||||
init(
|
||||
@ -64,7 +65,7 @@ fn _memory_init(
|
||||
let bitmap_size = ((usable_end - usable_start) / 4096 + 7) / 8;
|
||||
let bitmap_pages = (bitmap_size as usize + 4095) / 4096;
|
||||
|
||||
if let Some(bitmap_addr) = malloc::kmalloc(
|
||||
if let Some(bitmap_addr) = mem::kmalloc(
|
||||
bitmap_pages * 4096,
|
||||
&mut mapper,
|
||||
&mut frame_allocator
|
||||
@ -284,6 +285,8 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
|
||||
log_info!("System initialization complete!");
|
||||
kprintln!();
|
||||
|
||||
let mut executor = executor::Executor::new();
|
||||
|
||||
k_main::_kernel_main();
|
||||
|
||||
} else {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
pub mod print;
|
||||
pub mod logger;
|
||||
pub mod string;
|
||||
pub mod malloc;
|
||||
pub mod mem;
|
||||
@ -15,8 +15,8 @@ use x86_64::{
|
||||
VirtAddr,
|
||||
};
|
||||
|
||||
mod hal;
|
||||
mod klibc;
|
||||
pub mod hal;
|
||||
pub mod klibc;
|
||||
pub mod arch;
|
||||
pub mod mm;
|
||||
pub mod tty;
|
||||
@ -44,10 +44,10 @@ fn panic(info: &PanicInfo) -> ! {
|
||||
kprintln!();
|
||||
log_fatal!("{}", info);
|
||||
kprintln!();
|
||||
log_fatal!(" CR0: 0x{:016x}", cpu::cpu_r_cr0());
|
||||
log_fatal!(" CR2: 0x{:016x}", cpu::cpu_r_cr2());
|
||||
log_fatal!(" CR3: 0x{:016x}", cpu::cpu_r_cr3());
|
||||
log_fatal!(" CR4: 0x{:016x}", cpu::cpu_r_cr4());
|
||||
log_fatal!("CR0: 0x{:016x}", cpu::cpu_r_cr0());
|
||||
log_fatal!("CR2: 0x{:016x}", cpu::cpu_r_cr2());
|
||||
log_fatal!("CR3: 0x{:016x}", cpu::cpu_r_cr3());
|
||||
log_fatal!("CR4: 0x{:016x}", cpu::cpu_r_cr4());
|
||||
|
||||
loop {
|
||||
cpu::cpu_halt();
|
||||
|
||||
135
kernel/src/task/executor.rs
Normal file
135
kernel/src/task/executor.rs
Normal file
@ -0,0 +1,135 @@
|
||||
use alloc::{collections::BTreeMap, sync::Arc, task::Wake};
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
|
||||
use super::{Task, TaskId};
|
||||
use crate::hal::cpu;
|
||||
|
||||
/// Task executor that drives tasks to completion
|
||||
pub struct Executor {
|
||||
/// Binary tree of tasks
|
||||
tasks: BTreeMap<TaskId, Task>,
|
||||
/// Queue of tasks ready to run
|
||||
task_queue: Arc<ArrayQueue<TaskId>>,
|
||||
/// Cache of wakers for tasks
|
||||
waker_cache: BTreeMap<TaskId, Waker>,
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
/// Create a new executor
|
||||
pub fn new() -> Self {
|
||||
Executor {
|
||||
tasks: BTreeMap::new(),
|
||||
task_queue: Arc::new(ArrayQueue::new(100)),
|
||||
waker_cache: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawn a new task
|
||||
///
|
||||
/// Adds the provided task to the executor
|
||||
///
|
||||
/// # Safety
|
||||
/// * panics if the task ID is already in the executor
|
||||
/// * panics if the task queue is full
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `task` - [`Task`] to spawn
|
||||
pub fn spawn(&mut self, task: Task) {
|
||||
let task_id = task.id;
|
||||
if self.tasks.insert(task.id, task).is_some() {
|
||||
panic!("task with same ID already in tasks");
|
||||
}
|
||||
self.task_queue.push(task_id).expect("queue full");
|
||||
}
|
||||
|
||||
/// Run the executor
|
||||
///
|
||||
/// Continuously runs tasks until there are no more tasks to run
|
||||
/// and then sleeps until an interrupt is received
|
||||
pub fn run(&mut self) -> ! {
|
||||
loop {
|
||||
self.run_ready_tasks();
|
||||
self.sleep_if_idle();
|
||||
}
|
||||
}
|
||||
|
||||
/// Run all tasks that are ready to run
|
||||
fn run_ready_tasks(&mut self) {
|
||||
// destructure `self` to avoid borrow checker errors
|
||||
let Self {
|
||||
tasks,
|
||||
task_queue,
|
||||
waker_cache,
|
||||
} = self;
|
||||
|
||||
while let Some(task_id) = task_queue.pop() {
|
||||
let task = match tasks.get_mut(&task_id) {
|
||||
Some(task) => task,
|
||||
None => continue, // task no longer exists
|
||||
};
|
||||
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 done -> remove it and its cached waker
|
||||
tasks.remove(&task_id);
|
||||
waker_cache.remove(&task_id);
|
||||
}
|
||||
Poll::Pending => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sleep if there are no tasks to run
|
||||
///
|
||||
/// If there are no tasks to run, disable interrupts and halt the CPU until
|
||||
/// an interrupt is received.
|
||||
fn sleep_if_idle(&self) {
|
||||
cpu::cpu_disable_interrupts();
|
||||
if self.task_queue.is_empty() {
|
||||
cpu::cpu_halt()
|
||||
} else {
|
||||
cpu::cpu_enable_interrupts()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Waker for a task
|
||||
struct TaskWaker {
|
||||
/// Task ID
|
||||
task_id: TaskId,
|
||||
/// Task queue
|
||||
task_queue: Arc<ArrayQueue<TaskId>>,
|
||||
}
|
||||
|
||||
impl TaskWaker {
|
||||
/// Create a new task waker
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
fn new(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
|
||||
Waker::from(Arc::new(TaskWaker {
|
||||
task_id,
|
||||
task_queue,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wake the task
|
||||
fn wake_task(&self) {
|
||||
self.task_queue.push(self.task_id).expect("task_queue full");
|
||||
}
|
||||
}
|
||||
|
||||
impl Wake for TaskWaker {
|
||||
/// Wake the task
|
||||
fn wake(self: Arc<Self>) {
|
||||
self.wake_task();
|
||||
}
|
||||
|
||||
/// Wake the task by reference
|
||||
fn wake_by_ref(self: &Arc<Self>) {
|
||||
self.wake_task();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
//! A simple notion of a task, which is a future that can be polled by an
|
||||
//! executor.
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
use core::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::atomic::{AtomicU64, Ordering},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
pub mod executor;
|
||||
|
||||
/// A unique identifier for a task.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct TaskId(u64);
|
||||
|
||||
impl TaskId {
|
||||
/// Create a new, unique task ID.
|
||||
fn new() -> Self {
|
||||
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
|
||||
TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
|
||||
}
|
||||
}
|
||||
|
||||
/// A task that can be executed by an executor.
|
||||
pub struct Task {
|
||||
/// A unique identifier for the task.
|
||||
id: TaskId,
|
||||
/// The future that the task will execute
|
||||
future: Pin<Box<dyn Future<Output = ()>>>,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
/// Create a new task from a future.
|
||||
pub fn new(future: impl Future<Output = ()> + 'static) -> Task {
|
||||
Task {
|
||||
id: TaskId::new(),
|
||||
future: Box::pin(future),
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll the task.
|
||||
fn poll(&mut self, context: &mut Context) -> Poll<()> {
|
||||
self.future.as_mut().poll(context)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user