So I have two news for you. One good and one bad.
We have async, but we got rid of the preëmptive sched... wait, that's two good news, anyways, have a nice day. — Erin
This commit is contained in:
parent
8e09af7f15
commit
cd8e6e4b3b
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -203,6 +203,25 @@ name = "cpuio"
|
|||
version = "0.3.2"
|
||||
source = "git+https://git.ablecorp.us/ondra05/cpuio.git#093cc103101b4ba4abd02d77c884113a376cdc64"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.0"
|
||||
|
@ -295,8 +314,10 @@ dependencies = [
|
|||
name = "kernel"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"crossbeam-queue",
|
||||
"linked_list_allocator",
|
||||
"log",
|
||||
"slab",
|
||||
"versioning",
|
||||
]
|
||||
|
||||
|
@ -647,6 +668,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
|
|
|
@ -98,34 +98,11 @@ extern "x86-interrupt" fn double_fault_handler(
|
|||
* As long as nothing in this function does something that would
|
||||
* normally trigger an error, this function is relatively safe.
|
||||
*/
|
||||
#[naked]
|
||||
extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
|
||||
use super::task_switcher;
|
||||
crate::kmain::tick();
|
||||
unsafe {
|
||||
asm!(
|
||||
|
||||
|
||||
// "call {disable}",
|
||||
// Kernel tick
|
||||
"call {tick}",
|
||||
|
||||
// Push task's state onto stack
|
||||
// and save task pointer into scheduler
|
||||
task_switcher::save_tasks_state!(),
|
||||
"mov rdi, rsp",
|
||||
"call {save}",
|
||||
|
||||
// "call {enable}",
|
||||
// Switch to next task (interrupt'll be returned there)
|
||||
"jmp {switch_to_next}",
|
||||
|
||||
tick = sym crate::kmain::tick,
|
||||
save = sym task_switcher::save_and_enqueue,
|
||||
// disable = sym x86_64::instructions::interrupts::disable,
|
||||
// enable = sym x86_64::instructions::interrupts::enable,
|
||||
switch_to_next = sym task_switcher::switch_to_next,
|
||||
options(noreturn),
|
||||
);
|
||||
PICS.lock()
|
||||
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,6 @@ pub mod init;
|
|||
pub mod interrupts;
|
||||
pub mod memory;
|
||||
|
||||
mod task_switcher;
|
||||
|
||||
use crate::arch::drivers::allocator;
|
||||
use bootloader::{entry_point, BootInfo};
|
||||
use x86_64::{instructions::hlt, VirtAddr};
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
use crate::scheduler::{Task, SCHEDULER};
|
||||
|
||||
/// Saves task's state onto stack
|
||||
macro_rules! save_tasks_state {
|
||||
() => {
|
||||
"
|
||||
// Copy task's state
|
||||
push [rsp + 32] // SS
|
||||
push [rsp + 32] // RSP
|
||||
push [rsp + 32] // RFLAGS
|
||||
push [rsp + 32] // CS
|
||||
push [rsp + 32] // RIP
|
||||
|
||||
// Save task's registers
|
||||
push r15
|
||||
push r14
|
||||
push r13
|
||||
push r12
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
push rdi
|
||||
push rsi
|
||||
push rdx
|
||||
push rcx
|
||||
push rbx
|
||||
push rax
|
||||
"
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) use save_tasks_state;
|
||||
|
||||
/// Save provided stack pointer into scheduler's queue
|
||||
pub extern "C" fn save_and_enqueue(sp: u64) {
|
||||
SCHEDULER.lock().enqueue_suspended(sp);
|
||||
}
|
||||
|
||||
// Fetch and load next task
|
||||
pub unsafe extern "C" fn switch_to_next() -> ! {
|
||||
// Fetch next task
|
||||
let next = SCHEDULER.lock().pop().expect("no task in the task queue");
|
||||
match next {
|
||||
Task::Suspended(sp) => asm!(
|
||||
// Restore task's registers
|
||||
"mov rsp, {}",
|
||||
"pop rax",
|
||||
"pop rbx",
|
||||
"pop rcx",
|
||||
"pop rdx",
|
||||
"pop rsi",
|
||||
"pop rdi",
|
||||
"pop r8",
|
||||
"pop r9",
|
||||
"pop r10",
|
||||
"pop r11",
|
||||
"pop r12",
|
||||
"pop r13",
|
||||
"pop r14",
|
||||
"pop r15",
|
||||
|
||||
// Copy things up the stack
|
||||
"add rsp, 80", // Move 8 bytes above exception stack frame
|
||||
"push [rsp - 48]", // SS
|
||||
"push [rsp - 48]", // RSP
|
||||
"push [rsp - 48]", // RFLAGS
|
||||
"push [rsp - 48]", // CS
|
||||
"push [rsp - 48]", // RIP
|
||||
|
||||
// Signalise end of the interrupt and return
|
||||
"push rax",
|
||||
"mov al, 32",
|
||||
"out 20h, al",
|
||||
"pop rax",
|
||||
"iretq",
|
||||
|
||||
in(reg) sp,
|
||||
options(noreturn),
|
||||
),
|
||||
Task::Spawn {
|
||||
function,
|
||||
stack_start,
|
||||
} => asm!(
|
||||
"add rsp, 160", // Move above saved registries
|
||||
"mov [rsp], {ip}", // Set task's instruction pointer
|
||||
"mov [rsp + 24], {sp}", // Set task's stack pointer
|
||||
|
||||
// Signalise the end of the interrupt and return
|
||||
"mov al, 32",
|
||||
"out 20h, al",
|
||||
"iretq",
|
||||
|
||||
ip = in(reg) function,
|
||||
sp = in(reg) stack_start,
|
||||
options(noreturn),
|
||||
),
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ use crate::arch::{drivers::sysinfo::master, init, sloop};
|
|||
use crate::hardware;
|
||||
use crate::relib::network::socket::{SimpleSock, Socket};
|
||||
use crate::{
|
||||
boot_conf::KernelConfig, scheduler::SCHEDULER, scratchpad, systeminfo::RELEASE_TYPE, TERM,
|
||||
boot_conf::KernelConfig, scratchpad, systeminfo::RELEASE_TYPE, TERM,
|
||||
};
|
||||
use kernel::KERNEL_VERSION;
|
||||
use spin::Lazy;
|
||||
|
@ -66,16 +66,7 @@ pub fn kernel_main() -> ! {
|
|||
/*
|
||||
log_version_data();
|
||||
// */
|
||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||
let mut scheduler = SCHEDULER.lock();
|
||||
// comment this out to resume normal use
|
||||
// scheduler.enqueue_spawn(traceloop);
|
||||
|
||||
scheduler.enqueue_spawn(scratchpad);
|
||||
});
|
||||
|
||||
// scratchpad();
|
||||
|
||||
scratchpad();
|
||||
sloop()
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,6 @@ pub mod prelude;
|
|||
pub mod ps2_mouse;
|
||||
pub mod relib;
|
||||
pub mod rhai_shell;
|
||||
pub mod scheduler;
|
||||
pub mod scratchpad;
|
||||
pub mod stdio;
|
||||
pub mod time;
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
use alloc::collections::VecDeque;
|
||||
use spin::{Lazy, Mutex};
|
||||
|
||||
pub static SCHEDULER: Lazy<Mutex<Scheduler>> = Lazy::new(|| Mutex::new(Scheduler::default()));
|
||||
|
||||
/// Task's stack size (in kiB)
|
||||
const STACK_SIZE: usize = 512;
|
||||
|
||||
pub enum Task {
|
||||
Suspended(u64),
|
||||
Spawn { function: fn(), stack_start: usize },
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Scheduler {
|
||||
task_queue: VecDeque<Task>,
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub fn enqueue_spawn(&mut self, function: fn()) {
|
||||
self.task_queue.push_back(Task::Spawn {
|
||||
function,
|
||||
stack_start: unsafe {
|
||||
let size = STACK_SIZE * 1024;
|
||||
alloc::alloc::alloc(
|
||||
alloc::alloc::Layout::from_size_align(size, 1).expect("invalid layout"),
|
||||
)
|
||||
.add(size - 1) as _
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn enqueue_suspended(&mut self, sp: u64) {
|
||||
self.task_queue.push_back(Task::Suspended(sp));
|
||||
}
|
||||
|
||||
pub fn pop(&mut self) -> Option<Task> {
|
||||
self.task_queue.pop_front()
|
||||
}
|
||||
}
|
||||
|
||||
pub enum WakeConditions {
|
||||
Time,
|
||||
ReceivedMessageInChannel,
|
||||
GainedAccessToChannel,
|
||||
}
|
|
@ -6,6 +6,12 @@ version = "0.1.2"
|
|||
[dependencies]
|
||||
linked_list_allocator = "0.9"
|
||||
log = "0.4.14"
|
||||
slab = { version = "0.4", default-features = false }
|
||||
|
||||
[dependencies.crossbeam-queue]
|
||||
version = "0.3"
|
||||
default-features = false
|
||||
features = ["alloc"]
|
||||
|
||||
[dependencies.versioning]
|
||||
git = "https://git.ablecorp.us/able/aos_userland"
|
||||
|
|
|
@ -16,6 +16,7 @@ pub mod messaging;
|
|||
// pub mod panic;
|
||||
pub mod proccess;
|
||||
pub mod syscalls;
|
||||
pub mod task;
|
||||
pub mod time;
|
||||
|
||||
use core::arch::asm;
|
||||
|
|
101
kernel/src/task.rs
Normal file
101
kernel/src/task.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
//! Async task and executor
|
||||
|
||||
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, task::Wake};
|
||||
use core::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll, Waker},
|
||||
};
|
||||
use crossbeam_queue::SegQueue;
|
||||
use slab::Slab;
|
||||
|
||||
type TaskQueue = Arc<SegQueue<TaskId>>;
|
||||
|
||||
/// Tasks executor
|
||||
#[derive(Default)]
|
||||
pub struct Executor {
|
||||
/// All spawned tasks' stash
|
||||
tasks: Slab<Task>,
|
||||
|
||||
/// Awake tasks' queue
|
||||
queue: TaskQueue,
|
||||
|
||||
/// Wakers
|
||||
wakers: BTreeMap<TaskId, Waker>,
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
/// Spawn a future
|
||||
pub fn spawn(&mut self, future: impl Future<Output = ()> + 'static) {
|
||||
self.queue
|
||||
.push(TaskId(self.tasks.insert(Task::new(future))));
|
||||
}
|
||||
|
||||
/// Run tasks
|
||||
pub fn run(&mut self) -> ! {
|
||||
loop {
|
||||
while let Some(id) = self.queue.pop() {
|
||||
let task = match self.tasks.get_mut(id.0) {
|
||||
Some(t) => t,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let mut cx = Context::from_waker(
|
||||
self.wakers
|
||||
.entry(id)
|
||||
.or_insert_with(|| TaskWaker::new(id, Arc::clone(&self.queue))),
|
||||
);
|
||||
|
||||
match task.poll(&mut cx) {
|
||||
Poll::Ready(()) => {
|
||||
self.tasks.remove(id.0);
|
||||
self.wakers.remove(&id);
|
||||
}
|
||||
Poll::Pending => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct TaskId(usize);
|
||||
|
||||
/// Async task
|
||||
struct Task {
|
||||
future: Pin<Box<dyn Future<Output = ()>>>,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
/// Create a new task from a future
|
||||
fn new(future: impl Future<Output = ()> + 'static) -> Self {
|
||||
Self {
|
||||
future: Box::pin(future),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&mut self, cx: &mut Context) -> Poll<()> {
|
||||
self.future.as_mut().poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
struct TaskWaker {
|
||||
id: TaskId,
|
||||
queue: TaskQueue,
|
||||
}
|
||||
|
||||
impl TaskWaker {
|
||||
fn new(id: TaskId, queue: TaskQueue) -> Waker {
|
||||
Waker::from(Arc::new(Self { id, queue }))
|
||||
}
|
||||
}
|
||||
|
||||
impl Wake for TaskWaker {
|
||||
fn wake(self: Arc<Self>) {
|
||||
self.wake_by_ref();
|
||||
}
|
||||
|
||||
fn wake_by_ref(self: &Arc<Self>) {
|
||||
self.queue.push(self.id);
|
||||
}
|
||||
}
|
|
@ -29,8 +29,8 @@ impl Subcommand {
|
|||
match str.as_ref() {
|
||||
"doc" => Subcommand::Doc,
|
||||
"help" => Subcommand::Help,
|
||||
"run" => Subcommand::Run,
|
||||
"test" => Subcommand::Test,
|
||||
"run" | "r" => Subcommand::Run,
|
||||
"test" | "t" => Subcommand::Test,
|
||||
"" => Subcommand::Empty,
|
||||
unknown => Subcommand::Unknown(unknown.to_string()),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue