From 5bffce787b87f88600937d6956588399cf60f5f1 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 24 Jan 2019 19:03:45 +0800 Subject: [PATCH 1/4] rename 'process' crate to 'thread' --- crate/{process => thread}/Cargo.toml | 3 +- crate/{process => thread}/build.rs | 0 crate/{process => thread}/src/event_hub.rs | 0 crate/{process => thread}/src/interrupt.rs | 2 + crate/{process => thread}/src/lib.rs | 6 +- crate/{process => thread}/src/processor.rs | 16 +- crate/{process => thread}/src/scheduler.rs | 0 .../thread.rs => thread/src/std_thread.rs} | 8 +- .../src/thread_pool.rs} | 141 +++++++++--------- kernel/Cargo.lock | 4 +- kernel/Cargo.toml | 2 +- kernel/src/lib.rs | 2 +- kernel/src/process/context.rs | 2 +- kernel/src/process/mod.rs | 4 +- kernel/src/shell.rs | 1 + kernel/src/trap.rs | 2 +- 16 files changed, 97 insertions(+), 96 deletions(-) rename crate/{process => thread}/Cargo.toml (57%) rename crate/{process => thread}/build.rs (100%) rename crate/{process => thread}/src/event_hub.rs (100%) rename crate/{process => thread}/src/interrupt.rs (96%) rename crate/{process => thread}/src/lib.rs (81%) rename crate/{process => thread}/src/processor.rs (89%) rename crate/{process => thread}/src/scheduler.rs (100%) rename crate/{process/src/thread.rs => thread/src/std_thread.rs} (98%) rename crate/{process/src/process_manager.rs => thread/src/thread_pool.rs} (53%) diff --git a/crate/process/Cargo.toml b/crate/thread/Cargo.toml similarity index 57% rename from crate/process/Cargo.toml rename to crate/thread/Cargo.toml index 29d4995..a228acf 100644 --- a/crate/process/Cargo.toml +++ b/crate/thread/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "rcore-process" +name = "rcore-thread" version = "0.1.0" authors = ["WangRunji "] +description = "Bare-metal thread scheduler and executor" edition = "2018" [dependencies] diff --git a/crate/process/build.rs b/crate/thread/build.rs similarity index 100% rename from crate/process/build.rs rename to crate/thread/build.rs diff --git a/crate/process/src/event_hub.rs b/crate/thread/src/event_hub.rs similarity index 100% rename from crate/process/src/event_hub.rs rename to crate/thread/src/event_hub.rs diff --git a/crate/process/src/interrupt.rs b/crate/thread/src/interrupt.rs similarity index 96% rename from crate/process/src/interrupt.rs rename to crate/thread/src/interrupt.rs index 6e238be..e23eb12 100644 --- a/crate/process/src/interrupt.rs +++ b/crate/thread/src/interrupt.rs @@ -1,3 +1,5 @@ +//! Enable and disable interrupt for each architecture. + #[inline(always)] #[cfg(target_arch = "x86_64")] pub unsafe fn disable_and_store() -> usize { diff --git a/crate/process/src/lib.rs b/crate/thread/src/lib.rs similarity index 81% rename from crate/process/src/lib.rs rename to crate/thread/src/lib.rs index 764cdb6..bd0d394 100644 --- a/crate/process/src/lib.rs +++ b/crate/thread/src/lib.rs @@ -9,12 +9,12 @@ extern crate alloc; -mod process_manager; +mod thread_pool; mod processor; pub mod scheduler; -pub mod thread; +pub mod std_thread; mod event_hub; mod interrupt; -pub use crate::process_manager::*; +pub use crate::thread_pool::*; pub use crate::processor::Processor; diff --git a/crate/process/src/processor.rs b/crate/thread/src/processor.rs similarity index 89% rename from crate/process/src/processor.rs rename to crate/thread/src/processor.rs index e316324..27a3ca2 100644 --- a/crate/process/src/processor.rs +++ b/crate/thread/src/processor.rs @@ -2,10 +2,10 @@ use alloc::boxed::Box; use alloc::sync::Arc; use log::*; use core::cell::UnsafeCell; -use crate::process_manager::*; +use crate::thread_pool::*; use crate::interrupt; -/// Process executor +/// Thread executor /// /// Per-CPU struct. Defined at global. /// Only accessed by associated CPU with interrupt disabled. @@ -18,9 +18,9 @@ unsafe impl Sync for Processor {} struct ProcessorInner { id: usize, - proc: Option<(Pid, Box)>, + proc: Option<(Tid, Box)>, loop_context: Box, - manager: Arc, + manager: Arc, } impl Processor { @@ -28,7 +28,7 @@ impl Processor { Processor { inner: UnsafeCell::new(None) } } - pub unsafe fn init(&self, id: usize, context: Box, manager: Arc) { + pub unsafe fn init(&self, id: usize, context: Box, manager: Arc) { *self.inner.get() = Some(ProcessorInner { id, proc: None, @@ -76,7 +76,7 @@ impl Processor { } } - pub fn pid(&self) -> Pid { + pub fn tid(&self) -> Tid { self.inner().proc.as_ref().unwrap().0 } @@ -84,13 +84,13 @@ impl Processor { &*self.inner().proc.as_ref().unwrap().1 } - pub fn manager(&self) -> &ProcessManager { + pub fn manager(&self) -> &ThreadPool { &*self.inner().manager } pub fn tick(&self) { let flags = unsafe { interrupt::disable_and_store() }; - let need_reschedule = self.manager().tick(self.pid()); + let need_reschedule = self.manager().tick(self.tid()); unsafe { interrupt::restore(flags); } if need_reschedule { diff --git a/crate/process/src/scheduler.rs b/crate/thread/src/scheduler.rs similarity index 100% rename from crate/process/src/scheduler.rs rename to crate/thread/src/scheduler.rs diff --git a/crate/process/src/thread.rs b/crate/thread/src/std_thread.rs similarity index 98% rename from crate/process/src/thread.rs rename to crate/thread/src/std_thread.rs index 7657492..e59a559 100644 --- a/crate/process/src/thread.rs +++ b/crate/thread/src/std_thread.rs @@ -1,4 +1,4 @@ -//! Thread std-like interface +//! `std::thread`-like interface //! //! Based on Processor. Used in kernel. //! @@ -11,7 +11,7 @@ use core::marker::PhantomData; use core::time::Duration; use log::*; use crate::processor::*; -use crate::process_manager::*; +use crate::thread_pool::*; #[linkage = "weak"] #[no_mangle] @@ -30,7 +30,7 @@ fn new_kernel_context(_entry: extern fn(usize) -> !, _arg: usize) -> Box Thread { - Thread { pid: processor().pid() } + Thread { pid: processor().tid() } } /// Puts the current thread to sleep for the specified amount of time. @@ -160,7 +160,7 @@ impl JoinHandle { } } /// Force construct a JoinHandle struct - pub unsafe fn _of(pid: Pid) -> JoinHandle { + pub unsafe fn _of(pid: Tid) -> JoinHandle { JoinHandle { thread: Thread { pid }, mark: PhantomData, diff --git a/crate/process/src/process_manager.rs b/crate/thread/src/thread_pool.rs similarity index 53% rename from crate/process/src/process_manager.rs rename to crate/thread/src/thread_pool.rs index 2ca17b5..7043b55 100644 --- a/crate/process/src/process_manager.rs +++ b/crate/thread/src/thread_pool.rs @@ -5,17 +5,15 @@ use log::*; use crate::scheduler::Scheduler; use crate::event_hub::EventHub; -struct Process { - #[allow(dead_code)] - id: Pid, +struct Thread { status: Status, status_after_stop: Status, context: Option>, - parent: Pid, - children: Vec, + parent: Tid, + children: Vec, } -pub type Pid = usize; +pub type Tid = usize; type ExitCode = usize; #[derive(Debug, Clone, Eq, PartialEq)] @@ -23,37 +21,37 @@ pub enum Status { Ready, Running(usize), Sleeping, - Waiting(Pid), + Waiting(Tid), /// aka ZOMBIE. Its context was dropped. Exited(ExitCode), } #[derive(Eq, PartialEq)] enum Event { - Wakeup(Pid), + Wakeup(Tid), } pub trait Context { unsafe fn switch_to(&mut self, target: &mut Context); } -pub struct ProcessManager { - procs: Vec>>, +pub struct ThreadPool { + threads: Vec>>, scheduler: Mutex>, event_hub: Mutex>, } -impl ProcessManager { +impl ThreadPool { pub fn new(scheduler: Box, max_proc_num: usize) -> Self { - ProcessManager { - procs: new_vec_default(max_proc_num), + ThreadPool { + threads: new_vec_default(max_proc_num), scheduler: Mutex::new(scheduler), event_hub: Mutex::new(EventHub::new()), } } - fn alloc_pid(&self) -> Pid { - for (i, proc) in self.procs.iter().enumerate() { + fn alloc_tid(&self) -> Tid { + for (i, proc) in self.threads.iter().enumerate() { if proc.lock().is_none() { return i; } @@ -62,82 +60,81 @@ impl ProcessManager { } /// Add a new process - pub fn add(&self, context: Box, parent: Pid) -> Pid { - let pid = self.alloc_pid(); - *(&self.procs[pid]).lock() = Some(Process { - id: pid, + pub fn add(&self, context: Box, parent: Tid) -> Tid { + let tid = self.alloc_tid(); + *(&self.threads[tid]).lock() = Some(Thread { status: Status::Ready, status_after_stop: Status::Ready, context: Some(context), parent, children: Vec::new(), }); - self.scheduler.lock().insert(pid); - self.procs[parent].lock().as_mut().expect("invalid parent proc") - .children.push(pid); - pid + self.scheduler.lock().insert(tid); + self.threads[parent].lock().as_mut().expect("invalid parent proc") + .children.push(tid); + tid } - /// Make process `pid` time slice -= 1. + /// Make process `tid` time slice -= 1. /// Return true if time slice == 0. /// Called by timer interrupt handler. - pub fn tick(&self, pid: Pid) -> bool { + pub fn tick(&self, tid: Tid) -> bool { let mut event_hub = self.event_hub.lock(); event_hub.tick(); while let Some(event) = event_hub.pop() { match event { - Event::Wakeup(pid) => self.set_status(pid, Status::Ready), + Event::Wakeup(tid) => self.set_status(tid, Status::Ready), } } - self.scheduler.lock().tick(pid) + self.scheduler.lock().tick(tid) } - /// Set the priority of process `pid` - pub fn set_priority(&self, pid: Pid, priority: u8) { - self.scheduler.lock().set_priority(pid, priority); + /// Set the priority of process `tid` + pub fn set_priority(&self, tid: Tid, priority: u8) { + self.scheduler.lock().set_priority(tid, priority); } /// Called by Processor to get a process to run. /// The manager first mark it `Running`, /// then take out and return its Context. - pub fn run(&self, cpu_id: usize) -> (Pid, Box) { + pub fn run(&self, cpu_id: usize) -> (Tid, Box) { let mut scheduler = self.scheduler.lock(); - let pid = scheduler.select() + let tid = scheduler.select() .expect("failed to select a runnable process"); - scheduler.remove(pid); - let mut proc_lock = self.procs[pid].lock(); + scheduler.remove(tid); + let mut proc_lock = self.threads[tid].lock(); let mut proc = proc_lock.as_mut().expect("process not exist"); proc.status = Status::Running(cpu_id); - (pid, proc.context.take().expect("context not exist")) + (tid, proc.context.take().expect("context not exist")) } /// Called by Processor to finish running a process /// and give its context back. - pub fn stop(&self, pid: Pid, context: Box) { - let mut proc_lock = self.procs[pid].lock(); + pub fn stop(&self, tid: Tid, context: Box) { + let mut proc_lock = self.threads[tid].lock(); let mut proc = proc_lock.as_mut().expect("process not exist"); proc.status = proc.status_after_stop.clone(); proc.status_after_stop = Status::Ready; proc.context = Some(context); match proc.status { - Status::Ready => self.scheduler.lock().insert(pid), - Status::Exited(_) => self.exit_handler(pid, proc), + Status::Ready => self.scheduler.lock().insert(tid), + Status::Exited(_) => self.exit_handler(tid, proc), _ => {} } } /// Switch the status of a process. /// Insert/Remove it to/from scheduler if necessary. - fn set_status(&self, pid: Pid, status: Status) { - let mut proc_lock = self.procs[pid].lock(); + fn set_status(&self, tid: Tid, status: Status) { + let mut proc_lock = self.threads[tid].lock(); let mut proc = proc_lock.as_mut().expect("process not exist"); - trace!("process {} {:?} -> {:?}", pid, proc.status, status); + trace!("process {} {:?} -> {:?}", tid, proc.status, status); match (&proc.status, &status) { (Status::Ready, Status::Ready) => return, - (Status::Ready, _) => self.scheduler.lock().remove(pid), + (Status::Ready, _) => self.scheduler.lock().remove(tid), (Status::Exited(_), _) => panic!("can not set status for a exited process"), - (Status::Sleeping, Status::Exited(_)) => self.event_hub.lock().remove(Event::Wakeup(pid)), - (_, Status::Ready) => self.scheduler.lock().insert(pid), + (Status::Sleeping, Status::Exited(_)) => self.event_hub.lock().remove(Event::Wakeup(tid)), + (_, Status::Ready) => self.scheduler.lock().insert(tid), _ => {} } match proc.status { @@ -145,19 +142,19 @@ impl ProcessManager { _ => proc.status = status, } match proc.status { - Status::Exited(_) => self.exit_handler(pid, proc), + Status::Exited(_) => self.exit_handler(tid, proc), _ => {} } } - pub fn get_status(&self, pid: Pid) -> Option { - self.procs[pid].lock().as_ref().map(|p| p.status.clone()) + pub fn get_status(&self, tid: Tid) -> Option { + self.threads[tid].lock().as_ref().map(|p| p.status.clone()) } - /// Remove an exited proc `pid`. + /// Remove an exited proc `tid`. /// Its all children will be set parent to 0. - pub fn remove(&self, pid: Pid) { - let mut proc_lock = self.procs[pid].lock(); + pub fn remove(&self, tid: Tid) { + let mut proc_lock = self.threads[tid].lock(); let proc = proc_lock.as_ref().expect("process not exist"); match proc.status { Status::Exited(_) => {} @@ -165,49 +162,49 @@ impl ProcessManager { } // orphan procs for child in proc.children.iter() { - (&self.procs[*child]).lock().as_mut().expect("process not exist").parent = 0; + (&self.threads[*child]).lock().as_mut().expect("process not exist").parent = 0; } // remove self from parent's children list - self.procs[proc.parent].lock().as_mut().expect("process not exist") - .children.retain(|&i| i != pid); - // release the pid + self.threads[proc.parent].lock().as_mut().expect("process not exist") + .children.retain(|&i| i != tid); + // release the tid *proc_lock = None; } - /// Sleep `pid` for `time` ticks. + /// Sleep `tid` for `time` ticks. /// `time` == 0 means sleep forever - pub fn sleep(&self, pid: Pid, time: usize) { - self.set_status(pid, Status::Sleeping); + pub fn sleep(&self, tid: Tid, time: usize) { + self.set_status(tid, Status::Sleeping); if time != 0 { - self.event_hub.lock().push(time, Event::Wakeup(pid)); + self.event_hub.lock().push(time, Event::Wakeup(tid)); } } - pub fn wakeup(&self, pid: Pid) { - self.set_status(pid, Status::Ready); + pub fn wakeup(&self, tid: Tid) { + self.set_status(tid, Status::Ready); } - pub fn wait(&self, pid: Pid, target: Pid) { - self.set_status(pid, Status::Waiting(target)); + pub fn wait(&self, tid: Tid, target: Tid) { + self.set_status(tid, Status::Waiting(target)); } - pub fn wait_child(&self, pid: Pid) { - self.set_status(pid, Status::Waiting(0)); + pub fn wait_child(&self, tid: Tid) { + self.set_status(tid, Status::Waiting(0)); } - pub fn get_children(&self, pid: Pid) -> Vec { - self.procs[pid].lock().as_ref().expect("process not exist").children.clone() + pub fn get_children(&self, tid: Tid) -> Vec { + self.threads[tid].lock().as_ref().expect("process not exist").children.clone() } - pub fn exit(&self, pid: Pid, code: ExitCode) { - // NOTE: if `pid` is running, status change will be deferred. - self.set_status(pid, Status::Exited(code)); + pub fn exit(&self, tid: Tid, code: ExitCode) { + // NOTE: if `tid` is running, status change will be deferred. + self.set_status(tid, Status::Exited(code)); } /// Called when a process exit - fn exit_handler(&self, pid: Pid, proc: &mut Process) { + fn exit_handler(&self, tid: Tid, proc: &mut Thread) { // wakeup parent if waiting let parent = proc.parent; match self.get_status(parent).expect("process not exist") { - Status::Waiting(target) if target == pid || target == 0 => self.wakeup(parent), + Status::Waiting(target) if target == tid || target == 0 => self.wakeup(parent), _ => {} } // drop its context diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 0d41c34..bb25132 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -248,7 +248,7 @@ dependencies = [ "pc-keyboard 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rcore-memory 0.1.0", - "rcore-process 0.1.0", + "rcore-thread 0.1.0", "riscv 0.3.0 (git+https://github.com/riscv-and-rust-and-decaf/riscv)", "simple-filesystem 0.1.0 (git+https://github.com/wangrunji0408/SimpleFileSystem-Rust)", "smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -267,7 +267,7 @@ dependencies = [ ] [[package]] -name = "rcore-process" +name = "rcore-thread" version = "0.1.0" dependencies = [ "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 66a24ec..eefcc75 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -47,7 +47,7 @@ lazy_static = { version = "1.2", features = ["spin_no_std"] } smoltcp = { version = "0.5.0", default-features = false, features = ["alloc", "log", "proto-ipv4", "proto-igmp", "socket-icmp", "socket-udp", "socket-tcp"] } bit-allocator = { path = "../crate/bit-allocator" } rcore-memory = { path = "../crate/memory" } -rcore-process = { path = "../crate/process" } +rcore-thread = { path = "../crate/thread" } simple-filesystem = { git = "https://github.com/wangrunji0408/SimpleFileSystem-Rust" } [target.'cfg(target_arch = "x86_64")'.dependencies] diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 53a0129..3ea683e 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -13,7 +13,7 @@ extern crate alloc; pub use crate::process::{processor, new_kernel_context}; -use rcore_process::thread; +use rcore_thread::std_thread as thread; use linked_list_allocator::LockedHeap; #[macro_use] // print! diff --git a/kernel/src/process/context.rs b/kernel/src/process/context.rs index 71f63d5..ac6dbca 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/context.rs @@ -3,7 +3,7 @@ use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec::V use log::*; use simple_filesystem::file::File; use spin::Mutex; -use rcore_process::Context; +use rcore_thread::Context; use xmas_elf::{ElfFile, header, program::{Flags, Type}}; use crate::arch::interrupt::{Context as ArchContext, TrapFrame}; diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 2f59994..73cbb23 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -1,5 +1,5 @@ pub use self::context::Process; -pub use rcore_process::*; +pub use rcore_thread::*; use crate::consts::{MAX_CPU_NUM, MAX_PROCESS_NUM}; use crate::arch::cpu; use alloc::{boxed::Box, sync::Arc}; @@ -10,7 +10,7 @@ pub mod context; pub fn init() { // NOTE: max_time_slice <= 5 to ensure 'priority' test pass let scheduler = Box::new(scheduler::RRScheduler::new(5)); - let manager = Arc::new(ProcessManager::new(scheduler, MAX_PROCESS_NUM)); + let manager = Arc::new(ThreadPool::new(scheduler, MAX_PROCESS_NUM)); unsafe { for cpu_id in 0..MAX_CPU_NUM { diff --git a/kernel/src/shell.rs b/kernel/src/shell.rs index 4fa2057..bdfea25 100644 --- a/kernel/src/shell.rs +++ b/kernel/src/shell.rs @@ -3,6 +3,7 @@ use alloc::string::String; use crate::fs::{ROOT_INODE, INodeExt}; use crate::process::*; +use crate::thread; pub fn run_user_shell() { if let Ok(inode) = ROOT_INODE.lookup("sh") { diff --git a/kernel/src/trap.rs b/kernel/src/trap.rs index c5f1e08..61e2495 100644 --- a/kernel/src/trap.rs +++ b/kernel/src/trap.rs @@ -14,7 +14,7 @@ pub fn timer() { pub fn error(tf: &TrapFrame) -> ! { error!("{:#x?}", tf); - let pid = processor().pid(); + let pid = processor().tid(); error!("On CPU{} Process {}", cpu::id(), pid); processor().manager().exit(pid, 0x100); From 1541282ad76c41be80e92e4d3ebe50c4b890db12 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 24 Jan 2019 22:39:52 +0800 Subject: [PATCH 2/4] add example for 'thread' crate --- crate/thread/example/Cargo.toml | 19 +++ crate/thread/example/README.md | 5 + crate/thread/example/src/main.rs | 199 +++++++++++++++++++++++ crate/thread/example/x86_64-blog_os.json | 15 ++ 4 files changed, 238 insertions(+) create mode 100644 crate/thread/example/Cargo.toml create mode 100644 crate/thread/example/README.md create mode 100644 crate/thread/example/src/main.rs create mode 100644 crate/thread/example/x86_64-blog_os.json diff --git a/crate/thread/example/Cargo.toml b/crate/thread/example/Cargo.toml new file mode 100644 index 0000000..0ecc867 --- /dev/null +++ b/crate/thread/example/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "example" +version = "0.1.0" +authors = ["WangRunji "] +edition = "2018" + +[dependencies] +log = "0.4" +linked_list_allocator = "0.6" +blog_os = { git = "https://github.com/phil-opp/blog_os.git" } +rcore-thread = { path = ".." } + +[package.metadata.bootimage] +default-target = "x86_64-blog_os.json" +run-command = ["qemu-system-x86_64", + "-drive", "format=raw,file={}", + "-serial", "mon:stdio", + "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04" +] \ No newline at end of file diff --git a/crate/thread/example/README.md b/crate/thread/example/README.md new file mode 100644 index 0000000..2b14623 --- /dev/null +++ b/crate/thread/example/README.md @@ -0,0 +1,5 @@ +# Example of `rcore-thread` crate + +```bash +bootimage run --release +``` \ No newline at end of file diff --git a/crate/thread/example/src/main.rs b/crate/thread/example/src/main.rs new file mode 100644 index 0000000..2c7ada9 --- /dev/null +++ b/crate/thread/example/src/main.rs @@ -0,0 +1,199 @@ +#![no_std] +#![no_main] +#![feature(asm)] +#![feature(alloc)] +#![feature(naked_functions)] +#![feature(panic_info_message)] +#![feature(lang_items)] + +extern crate alloc; + +use core::alloc::Layout; +use core::panic::PanicInfo; +use alloc::{boxed::Box, sync::Arc}; + +use blog_os::{exit_qemu, gdt, interrupts::init_idt, serial_println}; +use linked_list_allocator::LockedHeap; +use rcore_thread::{*, std_thread as thread}; + +const STACK_SIZE: usize = 0x2000; +const HEAP_SIZE: usize = 0x100000; +const MAX_CPU_NUM: usize = 1; +const MAX_PROC_NUM: usize = 32; + + +/// The entry of the kernel +#[no_mangle] +pub extern "C" fn _start() -> ! { + // init x86 + gdt::init(); + init_idt(); + // init log + init_log(); + // init heap + unsafe { HEAP_ALLOCATOR.lock().init(HEAP.as_ptr() as usize, HEAP_SIZE); } + // init processor + let scheduler = Box::new(scheduler::RRScheduler::new(5)); + let thread_pool = Arc::new(ThreadPool::new(scheduler, MAX_PROC_NUM)); + unsafe { processor().init(0, Thread::init(), thread_pool); } + // init threads + thread::spawn(|| { + let tid = processor().tid(); + serial_println!("I'm thread {}! yield...", tid); + thread::yield_now(); + serial_println!("I'm thread {}! exit...", tid); + }); + thread::spawn(|| { + let tid = processor().tid(); + serial_println!("I'm thread {}! yield...", tid); + thread::yield_now(); + serial_println!("I'm thread {}! exit...", tid); + }); + // run threads + processor().run(); +} + +fn init_log() { + use log::*; + struct SimpleLogger; + impl Log for SimpleLogger { + fn enabled(&self, _metadata: &Metadata) -> bool { + true + } + fn log(&self, record: &Record) { + serial_println!("[{:>5}] {}", record.level(), record.args()); + } + fn flush(&self) {} + } + static LOGGER: SimpleLogger = SimpleLogger; + set_logger(&LOGGER).unwrap(); + set_max_level(LevelFilter::Trace); +} + +/// The context of a thread. +/// +/// When a thread yield, its context will be stored at its stack. +#[derive(Debug, Default)] +#[repr(C)] +struct ContextData { + rdi: usize, // arg0 + r15: usize, + r14: usize, + r13: usize, + r12: usize, + rbp: usize, + rbx: usize, + rip: usize, +} + +impl ContextData { + fn new(entry: extern fn(usize) -> !, arg0: usize) -> Self { + ContextData { + rip: entry as usize, + rdi: arg0, + ..ContextData::default() + } + } +} + +#[repr(C)] +struct Thread { + rsp: usize, + stack: [u8; STACK_SIZE], +} + +impl Thread { + unsafe fn init() -> Box { + Box::new(core::mem::uninitialized()) + } + fn new(entry: extern fn(usize) -> !, arg0: usize) -> Box { + let mut thread = unsafe { Thread::init() }; + let rsp = thread.stack.as_ptr() as usize + STACK_SIZE - core::mem::size_of::(); + // push a Context at stack top + let init_context = ContextData::new(entry, arg0); + unsafe { (rsp as *mut ContextData).write(init_context); } + thread.rsp = rsp; + thread + } +} + +/// Implement `switch_to` for a thread +impl Context for Thread { + /// Switch to another thread. + unsafe fn switch_to(&mut self, target: &mut Context) { + let (to, _): (*mut Thread, usize) = core::mem::transmute(target); + inner(self, to); + + #[naked] + #[inline(never)] + unsafe extern "C" fn inner(_from: *mut Thread, _to: *mut Thread) { + asm!( + " + // push rip (by caller) + + // Save self callee-save registers + push rbx + push rbp + push r12 + push r13 + push r14 + push r15 + push rdi + + // Switch stacks + mov [rdi], rsp // *rdi = from_rsp + mov rsp, [rsi] // *rsi = to_rsp + + // Restore target callee-save registers + pop rdi + pop r15 + pop r14 + pop r13 + pop r12 + pop rbp + pop rbx + + // pop rip + ret" + : : : : "intel" "volatile" ) + } + } +} + +/// Define global `Processor` for each core. +static PROCESSORS: [Processor; MAX_CPU_NUM] = [Processor::new()]; + +/// Now we only have one core. +fn cpu_id() -> usize { 0 } + +/// Implement dependency for `rcore_thread::std_thread` +#[no_mangle] +pub fn processor() -> &'static Processor { + &PROCESSORS[cpu_id()] +} + +/// Implement dependency for `rcore_thread::std_thread` +#[no_mangle] +pub fn new_kernel_context(entry: extern fn(usize) -> !, arg0: usize) -> Box { + Thread::new(entry, arg0) +} + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + let location = info.location().unwrap(); + let message = info.message().unwrap(); + serial_println!("\nPANIC @ {}\n\t{}", location, message); + + unsafe { exit_qemu(); } + loop {} +} + +#[global_allocator] +static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); + +static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE]; + +#[lang = "oom"] +fn oom(_: Layout) -> ! { + panic!("out of memory"); +} \ No newline at end of file diff --git a/crate/thread/example/x86_64-blog_os.json b/crate/thread/example/x86_64-blog_os.json new file mode 100644 index 0000000..7d2110d --- /dev/null +++ b/crate/thread/example/x86_64-blog_os.json @@ -0,0 +1,15 @@ +{ + "llvm-target": "x86_64-unknown-none", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "arch": "x86_64", + "target-endian": "little", + "target-pointer-width": "64", + "target-c-int-width": "32", + "os": "none", + "executables": true, + "linker-flavor": "ld.lld", + "linker": "rust-lld", + "panic-strategy": "abort", + "disable-redzone": true, + "features": "-mmx,-sse,+soft-float" +} From 0ec5ad80562c8a030554d0448c5d7bc299707f05 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Fri, 25 Jan 2019 22:58:49 +0800 Subject: [PATCH 3/4] split Process and Thread --- docs/2_OSLab/g2/context.md | 8 +- kernel/src/process/mod.rs | 22 +++-- kernel/src/process/{context.rs => structs.rs} | 88 +++++++++++-------- kernel/src/shell.rs | 6 +- kernel/src/syscall.rs | 35 ++++---- 5 files changed, 92 insertions(+), 67 deletions(-) rename kernel/src/process/{context.rs => structs.rs} (75%) diff --git a/docs/2_OSLab/g2/context.md b/docs/2_OSLab/g2/context.md index 1372c65..6805901 100644 --- a/docs/2_OSLab/g2/context.md +++ b/docs/2_OSLab/g2/context.md @@ -1,6 +1,6 @@ # 上下文切换 -平台无关的代码位于 [kernel/src/process/context.rs](../../../kernel/src/process/context.rs) 中,而平台相关(aarch64)的代码位于 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs) 中。 +平台无关的代码位于 [kernel/src/process/context.rs](../../../kernel/src/process/structs.rs) 中,而平台相关(aarch64)的代码位于 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs) 中。 ## 相关数据结构 @@ -56,7 +56,7 @@ } ``` - 每个进程控制块 `Process` ([kernel/src/process/context.rs](../../../kernel/src/process/context.rs#L13)) 都会维护一个平台相关的 `Context` 对象,在 AArch64 中包含下列信息: + 每个进程控制块 `Process` ([kernel/src/process/context.rs](../../../kernel/src/process/structs.rs#L13)) 都会维护一个平台相关的 `Context` 对象,在 AArch64 中包含下列信息: 1. `stack_top`:内核栈顶地址 2. `ttbr`:页表基址 @@ -64,7 +64,7 @@ ## 切换流程 -在 [kernel/src/process/context.rs](../../../kernel/src/process/context.rs#L22) 里,`switch_to()` 是平台无关的切换函数,最终会调用 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L129) 里平台相关的切换函数 `Context::switch()`: +在 [kernel/src/process/context.rs](../../../kernel/src/process/structs.rs#L22) 里,`switch_to()` 是平台无关的切换函数,最终会调用 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L129) 里平台相关的切换函数 `Context::switch()`: ```rust pub unsafe fn switch(&mut self, target: &mut Self) { @@ -190,7 +190,7 @@ ret 2. 创建新的**用户线程**:解析 ELF 文件。 3. 从一个线程 **fork** 出一个新线程:通过 `fork` 系统调用。 -三种线程的平台无关创建流程实现在 [kernel/src/process/context.rs](../../../kernel/src/process/context.rs#L40) 里,最终会分别调用 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L146) 里的 `new_kernel_thread()`、`new_user_thread()` 和 `new_fork()` 这三个函数创建平台相关的 `Context` 结构。 +三种线程的平台无关创建流程实现在 [kernel/src/process/context.rs](../../../kernel/src/process/structs.rs#L40) 里,最终会分别调用 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L146) 里的 `new_kernel_thread()`、`new_user_thread()` 和 `new_fork()` 这三个函数创建平台相关的 `Context` 结构。 在这三个函数里,会构造 `ContextData` 与 `TrapFrame` 结构,构成一个 `InitStack`,并向新线程的内核栈压入 `InitStack` 结构,最后将新内核栈顶地址、页表基址等信息构成 `Context` 结构返回。这两个结构的构造方式如下: diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 73cbb23..fe5dd21 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -1,11 +1,12 @@ -pub use self::context::Process; +pub use self::structs::*; pub use rcore_thread::*; use crate::consts::{MAX_CPU_NUM, MAX_PROCESS_NUM}; use crate::arch::cpu; use alloc::{boxed::Box, sync::Arc}; +use spin::MutexGuard; use log::*; -pub mod context; +pub mod structs; pub fn init() { // NOTE: max_time_slice <= 5 to ensure 'priority' test pass @@ -14,7 +15,7 @@ pub fn init() { unsafe { for cpu_id in 0..MAX_CPU_NUM { - PROCESSORS[cpu_id].init(cpu_id, Process::new_init(), manager.clone()); + PROCESSORS[cpu_id].init(cpu_id, Thread::new_init(), manager.clone()); } } @@ -25,7 +26,7 @@ pub fn init() { use core::str::FromStr; let cores = usize::from_str(env!("SMP")).unwrap(); for i in 0..cores { - manager.add(Process::new_kernel(idle, i), 0); + manager.add(Thread::new_kernel(idle, i), 0); } crate::shell::run_user_shell(); @@ -34,12 +35,17 @@ pub fn init() { static PROCESSORS: [Processor; MAX_CPU_NUM] = [Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new()]; -/// Get current thread struct +/// Get current process +pub fn process() -> MutexGuard<'static, Process> { + current_thread().proc.lock() +} + +/// Get current thread /// /// FIXME: It's obviously unsafe to get &mut ! -pub fn process() -> &'static mut Process { +pub fn current_thread() -> &'static mut Thread { use core::mem::transmute; - let (process, _): (&mut Process, *const ()) = unsafe { + let (process, _): (&mut Thread, *const ()) = unsafe { transmute(processor().context()) }; process @@ -55,5 +61,5 @@ pub fn processor() -> &'static Processor { #[no_mangle] pub fn new_kernel_context(entry: extern fn(usize) -> !, arg: usize) -> Box { - Process::new_kernel(entry, arg) + Thread::new_kernel(entry, arg) } diff --git a/kernel/src/process/context.rs b/kernel/src/process/structs.rs similarity index 75% rename from kernel/src/process/context.rs rename to kernel/src/process/structs.rs index ac6dbca..7a66a4c 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/structs.rs @@ -3,54 +3,64 @@ use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec::V use log::*; use simple_filesystem::file::File; use spin::Mutex; -use rcore_thread::Context; use xmas_elf::{ElfFile, header, program::{Flags, Type}}; -use crate::arch::interrupt::{Context as ArchContext, TrapFrame}; +use crate::arch::interrupt::{Context, TrapFrame}; use crate::memory::{ByFrame, GlobalFrameAlloc, KernelStack, MemoryAttr, MemorySet}; // TODO: avoid pub +pub struct Thread { + pub context: Context, + pub kstack: KernelStack, + pub proc: Arc>, +} + pub struct Process { - pub arch: ArchContext, pub memory_set: MemorySet, - pub kstack: KernelStack, pub files: BTreeMap>>, pub cwd: String, } -impl Context for Process { - unsafe fn switch_to(&mut self, target: &mut Context) { +/// Let `rcore_thread` can switch between our `Thread` +impl rcore_thread::Context for Thread { + unsafe fn switch_to(&mut self, target: &mut rcore_thread::Context) { use core::mem::transmute; - let (target, _): (&mut Process, *const ()) = transmute(target); - self.arch.switch(&mut target.arch); + let (target, _): (&mut Thread, *const ()) = transmute(target); + self.context.switch(&mut target.context); } } -impl Process { - pub unsafe fn new_init() -> Box { - Box::new(Process { - arch: ArchContext::null(), - memory_set: MemorySet::new(), +impl Thread { + /// Make a struct for the init thread + pub unsafe fn new_init() -> Box { + Box::new(Thread { + context: Context::null(), kstack: KernelStack::new(), - files: BTreeMap::default(), - cwd: String::new(), + proc: Arc::new(Mutex::new(Process { + memory_set: MemorySet::new(), + files: BTreeMap::default(), + cwd: String::new(), + })), }) } - pub fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Box { + /// Make a new kernel thread starting from `entry` with `arg` + pub fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Box { let memory_set = MemorySet::new(); let kstack = KernelStack::new(); - Box::new(Process { - arch: unsafe { ArchContext::new_kernel_thread(entry, arg, kstack.top(), memory_set.token()) }, - memory_set, + Box::new(Thread { + context: unsafe { Context::new_kernel_thread(entry, arg, kstack.top(), memory_set.token()) }, kstack, - files: BTreeMap::default(), - cwd: String::new(), + proc: Arc::new(Mutex::new(Process { + memory_set, + files: BTreeMap::default(), + cwd: String::new(), + })), }) } - /// Make a new user thread from ELF data - pub fn new_user<'a, Iter>(data: &[u8], args: Iter) -> Box + /// Make a new user process from ELF `data` + pub fn new_user<'a, Iter>(data: &[u8], args: Iter) -> Box where Iter: Iterator { // Parse elf @@ -94,23 +104,25 @@ impl Process { let kstack = KernelStack::new(); - Box::new(Process { - arch: unsafe { - ArchContext::new_user_thread( + Box::new(Thread { + context: unsafe { + Context::new_user_thread( entry_addr, ustack_top, kstack.top(), is32, memory_set.token()) }, - memory_set, kstack, - files: BTreeMap::default(), - cwd: String::new(), + proc: Arc::new(Mutex::new(Process { + memory_set, + files: BTreeMap::default(), + cwd: String::new(), + })), }) } - /// Fork - pub fn fork(&self, tf: &TrapFrame) -> Box { + /// Fork a new process from current one + pub fn fork(&self, tf: &TrapFrame) -> Box { info!("COME into fork!"); // Clone memory set, make a new page table - let memory_set = self.memory_set.clone(); + let memory_set = self.proc.lock().memory_set.clone(); info!("finish mmset clone in fork!"); // MMU: copy data to the new space @@ -126,12 +138,14 @@ impl Process { info!("temporary copy data!"); let kstack = KernelStack::new(); - Box::new(Process { - arch: unsafe { ArchContext::new_fork(tf, kstack.top(), memory_set.token()) }, - memory_set, + Box::new(Thread { + context: unsafe { Context::new_fork(tf, kstack.top(), memory_set.token()) }, kstack, - files: BTreeMap::default(), - cwd: String::new(), + proc: Arc::new(Mutex::new(Process { + memory_set, + files: BTreeMap::default(), + cwd: String::new(), + })), }) } } diff --git a/kernel/src/shell.rs b/kernel/src/shell.rs index bdfea25..e2ed691 100644 --- a/kernel/src/shell.rs +++ b/kernel/src/shell.rs @@ -10,9 +10,9 @@ pub fn run_user_shell() { println!("Going to user mode shell."); println!("Use 'ls' to list available programs."); let data = inode.read_as_vec().unwrap(); - processor().manager().add(Process::new_user(data.as_slice(), "sh".split(' ')), 0); + processor().manager().add(Thread::new_user(data.as_slice(), "sh".split(' ')), 0); } else { - processor().manager().add(Process::new_kernel(shell, 0), 0); + processor().manager().add(Thread::new_kernel(shell, 0), 0); } } @@ -29,7 +29,7 @@ pub extern fn shell(_arg: usize) -> ! { let name = cmd.split(' ').next().unwrap(); if let Ok(file) = ROOT_INODE.lookup(name) { let data = file.read_as_vec().unwrap(); - let pid = processor().manager().add(Process::new_user(data.as_slice(), cmd.split(' ')), thread::current().id()); + let pid = processor().manager().add(Thread::new_user(data.as_slice(), cmd.split(' ')), thread::current().id()); unsafe { thread::JoinHandle::<()>::_of(pid) }.join().unwrap(); } else { println!("Program not exist"); diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index a9efc21..cad1e50 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -3,7 +3,7 @@ use simple_filesystem::{INode, file::File, FileInfo, FileType, FsError}; use core::{slice, str}; use alloc::{sync::Arc, vec::Vec, string::String}; -use spin::Mutex; +use spin::{Mutex, MutexGuard}; use log::*; use bitflags::bitflags; use crate::arch::interrupt::TrapFrame; @@ -61,7 +61,8 @@ fn sys_read(fd: usize, base: *mut u8, len: usize) -> SysResult { // TODO: check ptr info!("read: fd: {}, base: {:?}, len: {:#x}", fd, base, len); let slice = unsafe { slice::from_raw_parts_mut(base, len) }; - let len = get_file(fd)?.lock().read(slice)?; + let proc = process(); + let len = get_file(&proc, fd)?.lock().read(slice)?; Ok(len as isize) } @@ -69,7 +70,8 @@ fn sys_write(fd: usize, base: *const u8, len: usize) -> SysResult { // TODO: check ptr info!("write: fd: {}, base: {:?}, len: {:#x}", fd, base, len); let slice = unsafe { slice::from_raw_parts(base, len) }; - let len = get_file(fd)?.lock().write(slice)?; + let proc = process(); + let len = get_file(&proc, fd)?.lock().write(slice)?; Ok(len as isize) } @@ -103,7 +105,8 @@ fn sys_close(fd: usize) -> SysResult { fn sys_fstat(fd: usize, stat_ptr: *mut Stat) -> SysResult { // TODO: check ptr info!("fstat: {}", fd); - let file = get_file(fd)?; + let proc = process(); + let file = get_file(&proc, fd)?; let stat = Stat::from(file.lock().info()?); unsafe { stat_ptr.write(stat); } Ok(0) @@ -115,7 +118,8 @@ fn sys_fstat(fd: usize, stat_ptr: *mut Stat) -> SysResult { fn sys_getdirentry(fd: usize, dentry_ptr: *mut DirEntry) -> SysResult { // TODO: check ptr info!("getdirentry: {}", fd); - let file = get_file(fd)?; + let proc = process(); + let file = get_file(&proc, fd)?; let dentry = unsafe { &mut *dentry_ptr }; if !dentry.check() { return Err(SysError::Inval); @@ -131,7 +135,8 @@ fn sys_getdirentry(fd: usize, dentry_ptr: *mut DirEntry) -> SysResult { fn sys_dup(fd1: usize, fd2: usize) -> SysResult { info!("dup: {} {}", fd1, fd2); - let file = get_file(fd1)?; + let proc = process(); + let file = get_file(&proc, fd1)?; if process().files.contains_key(&fd2) { return Err(SysError::Inval); } @@ -141,7 +146,7 @@ fn sys_dup(fd1: usize, fd2: usize) -> SysResult { /// Fork the current process. Return the child's PID. fn sys_fork(tf: &TrapFrame) -> SysResult { - let context = process().fork(tf); + let context = current_thread().fork(tf); let pid = processor().manager().add(context, thread::current().id()); info!("fork: {} -> {}", thread::current().id(), pid); Ok(pid as isize) @@ -207,19 +212,19 @@ fn sys_exec(name: *const u8, argc: usize, argv: *const *const u8, tf: &mut TrapF unsafe { buf.set_len(size); } inode.read_at(0, buf.as_mut_slice())?; - // Make new Context + // Make new Thread let iter = args.iter().map(|s| s.as_str()); - let mut context = Process::new_user(buf.as_slice(), iter); + let mut thread = Thread::new_user(buf.as_slice(), iter); // Activate new page table - unsafe { context.memory_set.activate(); } + unsafe { thread.proc.lock().memory_set.activate(); } // Modify the TrapFrame - *tf = unsafe { context.arch.get_init_tf() }; + *tf = unsafe { thread.context.get_init_tf() }; // Swap Context but keep KStack - ::core::mem::swap(&mut process().kstack, &mut context.kstack); - ::core::mem::swap(process(), &mut *context); + ::core::mem::swap(&mut current_thread().kstack, &mut thread.kstack); + ::core::mem::swap(current_thread(), &mut *thread); Ok(0) } @@ -278,8 +283,8 @@ fn sys_putc(c: char) -> SysResult { Ok(0) } -fn get_file(fd: usize) -> Result<&'static Arc>, SysError> { - process().files.get(&fd).ok_or(SysError::Inval) +fn get_file<'a>(proc: &'a MutexGuard<'static, Process>, fd: usize) -> Result<&'a Arc>, SysError> { + proc.files.get(&fd).ok_or(SysError::Inval) } pub type SysResult = Result; From 086fcd4079204690f3b07002e466ed1ba05449bd Mon Sep 17 00:00:00 2001 From: WangRunji Date: Mon, 28 Jan 2019 13:47:51 +0800 Subject: [PATCH 4/4] rename 'event_hub' to 'timer', add doc and simplify --- crate/thread/src/event_hub.rs | 93 --------------------------------- crate/thread/src/lib.rs | 2 +- crate/thread/src/thread_pool.rs | 16 +++--- crate/thread/src/timer.rs | 65 +++++++++++++++++++++++ 4 files changed, 74 insertions(+), 102 deletions(-) delete mode 100644 crate/thread/src/event_hub.rs create mode 100644 crate/thread/src/timer.rs diff --git a/crate/thread/src/event_hub.rs b/crate/thread/src/event_hub.rs deleted file mode 100644 index 62d2cec..0000000 --- a/crate/thread/src/event_hub.rs +++ /dev/null @@ -1,93 +0,0 @@ -use alloc::collections::VecDeque; -use core::cmp::{Ordering, PartialOrd}; - -type Time = usize; - -struct Timer { - time: Time, - data: T, -} - -impl PartialEq for Timer { - fn eq(&self, other: &Self) -> bool { - self.time.eq(&other.time) - } -} - -impl Eq for Timer {} - -impl PartialOrd for Timer { - fn partial_cmp(&self, other: &Self) -> Option { - other.time.partial_cmp(&self.time) - } -} - -impl Ord for Timer { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(&other).unwrap() - } -} - -pub struct EventHub { - tick: Time, - timers: VecDeque>, -} - -impl EventHub { - pub fn new() -> Self { - EventHub { - tick: 0, - timers: VecDeque::new(), - } - } - pub fn tick(&mut self) { - self.tick += 1; - } - pub fn pop(&mut self) -> Option { - match self.timers.front() { - None => return None, - Some(timer) if timer.time != self.tick => return None, - _ => {} - }; - self.timers.pop_front().map(|t| t.data) - } - pub fn push(&mut self, time_after: Time, data: T) { - //debug!("{:?} {:?}", self.tick, time_after); - let time = self.tick + time_after; - let timer = Timer { time, data }; - let mut it = self.timers.iter(); - let mut i : usize = 0; - loop { - let now = it.next(); - if now == None { - break - }; - if now.unwrap() < &timer { - break - }; - i += 1; - } - self.timers.insert(i, timer); - } - #[allow(dead_code)] - pub fn get_time(&self) -> Time { - self.tick - } - pub fn remove(&mut self, data: T) { - let mut it = self.timers.iter(); - let mut i : usize = 0; - loop { - let now = it.next(); - if now == None { - break - }; - if now.map(|t| &t.data).unwrap() == &data { - break - }; - i += 1; - } - if i < self.timers.len() { - self.timers.remove(i); - } - } -} \ No newline at end of file diff --git a/crate/thread/src/lib.rs b/crate/thread/src/lib.rs index bd0d394..af97063 100644 --- a/crate/thread/src/lib.rs +++ b/crate/thread/src/lib.rs @@ -13,7 +13,7 @@ mod thread_pool; mod processor; pub mod scheduler; pub mod std_thread; -mod event_hub; +mod timer; mod interrupt; pub use crate::thread_pool::*; diff --git a/crate/thread/src/thread_pool.rs b/crate/thread/src/thread_pool.rs index 7043b55..8d16de6 100644 --- a/crate/thread/src/thread_pool.rs +++ b/crate/thread/src/thread_pool.rs @@ -3,7 +3,7 @@ use alloc::vec::Vec; use spin::Mutex; use log::*; use crate::scheduler::Scheduler; -use crate::event_hub::EventHub; +use crate::timer::Timer; struct Thread { status: Status, @@ -38,7 +38,7 @@ pub trait Context { pub struct ThreadPool { threads: Vec>>, scheduler: Mutex>, - event_hub: Mutex>, + timer: Mutex>, } impl ThreadPool { @@ -46,7 +46,7 @@ impl ThreadPool { ThreadPool { threads: new_vec_default(max_proc_num), scheduler: Mutex::new(scheduler), - event_hub: Mutex::new(EventHub::new()), + timer: Mutex::new(Timer::new()), } } @@ -79,9 +79,9 @@ impl ThreadPool { /// Return true if time slice == 0. /// Called by timer interrupt handler. pub fn tick(&self, tid: Tid) -> bool { - let mut event_hub = self.event_hub.lock(); - event_hub.tick(); - while let Some(event) = event_hub.pop() { + let mut timer = self.timer.lock(); + timer.tick(); + while let Some(event) = timer.pop() { match event { Event::Wakeup(tid) => self.set_status(tid, Status::Ready), } @@ -133,7 +133,7 @@ impl ThreadPool { (Status::Ready, Status::Ready) => return, (Status::Ready, _) => self.scheduler.lock().remove(tid), (Status::Exited(_), _) => panic!("can not set status for a exited process"), - (Status::Sleeping, Status::Exited(_)) => self.event_hub.lock().remove(Event::Wakeup(tid)), + (Status::Sleeping, Status::Exited(_)) => self.timer.lock().stop(Event::Wakeup(tid)), (_, Status::Ready) => self.scheduler.lock().insert(tid), _ => {} } @@ -176,7 +176,7 @@ impl ThreadPool { pub fn sleep(&self, tid: Tid, time: usize) { self.set_status(tid, Status::Sleeping); if time != 0 { - self.event_hub.lock().push(time, Event::Wakeup(tid)); + self.timer.lock().start(time, Event::Wakeup(tid)); } } diff --git a/crate/thread/src/timer.rs b/crate/thread/src/timer.rs new file mode 100644 index 0000000..9854f14 --- /dev/null +++ b/crate/thread/src/timer.rs @@ -0,0 +1,65 @@ +//! A simple timer + +use alloc::collections::VecDeque; + +type Time = usize; + +struct Event { + time: Time, + data: T, +} + +/// A simple timer using ordered dequeue +pub struct Timer { + tick: Time, + timers: VecDeque>, +} + +impl Timer { + /// Create a new timer. + pub fn new() -> Self { + Timer { + tick: 0, + timers: VecDeque::new(), + } + } + /// Called on each tick. + pub fn tick(&mut self) { + self.tick += 1; + } + /// Pop an expired timer after `tick`. + /// + /// This must be called after calling `tick`, + /// and should be called multiple times until return `None`. + pub fn pop(&mut self) -> Option { + match self.timers.front() { + None => return None, + Some(timer) if timer.time != self.tick => return None, + _ => {} + }; + self.timers.pop_front().map(|t| t.data) + } + /// Start a timer with given time interval + pub fn start(&mut self, time_after: Time, data: T) { + //debug!("{:?} {:?}", self.tick, time_after); + let time = self.tick + time_after; + let event = Event { time, data }; + let mut it = self.timers.iter(); + let mut i : usize = 0; + loop { + match it.next() { + None => break, + Some(e) if e.time >= time => break, + _ => {} + } + i += 1; + } + self.timers.insert(i, event); + } + /// Stop a timer + pub fn stop(&mut self, data: T) { + if let Some(i) = self.timers.iter().position(|t| t.data == data) { + self.timers.remove(i); + } + } +} \ No newline at end of file