# Conflicts: # kernel/src/process/mod.rs # kernel/src/process/structs.rs # kernel/src/syscall.rsmaster
commit
7927c7c168
@ -1,93 +0,0 @@
|
||||
use alloc::collections::VecDeque;
|
||||
use core::cmp::{Ordering, PartialOrd};
|
||||
|
||||
type Time = usize;
|
||||
|
||||
struct Timer<T> {
|
||||
time: Time,
|
||||
data: T,
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Timer<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.time.eq(&other.time)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Timer<T> {}
|
||||
|
||||
impl<T> PartialOrd for Timer<T> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
other.time.partial_cmp(&self.time)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Ord for Timer<T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.partial_cmp(&other).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventHub<T> {
|
||||
tick: Time,
|
||||
timers: VecDeque<Timer<T>>,
|
||||
}
|
||||
|
||||
impl<T: PartialEq> EventHub<T> {
|
||||
pub fn new() -> Self {
|
||||
EventHub {
|
||||
tick: 0,
|
||||
timers: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
pub fn tick(&mut self) {
|
||||
self.tick += 1;
|
||||
}
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use spin::Mutex;
|
||||
use log::*;
|
||||
use crate::scheduler::Scheduler;
|
||||
use crate::event_hub::EventHub;
|
||||
|
||||
struct Process {
|
||||
#[allow(dead_code)]
|
||||
id: Pid,
|
||||
status: Status,
|
||||
status_after_stop: Status,
|
||||
context: Option<Box<Context>>,
|
||||
parent: Pid,
|
||||
children: Vec<Pid>,
|
||||
}
|
||||
|
||||
pub type Pid = usize;
|
||||
type ExitCode = usize;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Status {
|
||||
Ready,
|
||||
Running(usize),
|
||||
Sleeping,
|
||||
Waiting(Pid),
|
||||
/// aka ZOMBIE. Its context was dropped.
|
||||
Exited(ExitCode),
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum Event {
|
||||
Wakeup(Pid),
|
||||
}
|
||||
|
||||
pub trait Context {
|
||||
unsafe fn switch_to(&mut self, target: &mut Context);
|
||||
}
|
||||
|
||||
pub struct ProcessManager {
|
||||
procs: Vec<Mutex<Option<Process>>>,
|
||||
scheduler: Mutex<Box<Scheduler>>,
|
||||
event_hub: Mutex<EventHub<Event>>,
|
||||
}
|
||||
|
||||
impl ProcessManager {
|
||||
pub fn new(scheduler: Box<Scheduler>, max_proc_num: usize) -> Self {
|
||||
ProcessManager {
|
||||
procs: 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() {
|
||||
if proc.lock().is_none() {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
panic!("Process number exceeded");
|
||||
}
|
||||
|
||||
/// Add a new process
|
||||
pub fn add(&self, context: Box<Context>, parent: Pid) -> Pid {
|
||||
let pid = self.alloc_pid();
|
||||
*(&self.procs[pid]).lock() = Some(Process {
|
||||
id: pid,
|
||||
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
|
||||
}
|
||||
|
||||
/// Make process `pid` time slice -= 1.
|
||||
/// Return true if time slice == 0.
|
||||
/// Called by timer interrupt handler.
|
||||
pub fn tick(&self, pid: Pid) -> 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),
|
||||
}
|
||||
}
|
||||
self.scheduler.lock().tick(pid)
|
||||
}
|
||||
|
||||
/// Set the priority of process `pid`
|
||||
pub fn set_priority(&self, pid: Pid, priority: u8) {
|
||||
self.scheduler.lock().set_priority(pid, 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<Context>) {
|
||||
let mut scheduler = self.scheduler.lock();
|
||||
let pid = scheduler.select()
|
||||
.expect("failed to select a runnable process");
|
||||
scheduler.remove(pid);
|
||||
let mut proc_lock = self.procs[pid].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"))
|
||||
}
|
||||
|
||||
/// Called by Processor to finish running a process
|
||||
/// and give its context back.
|
||||
pub fn stop(&self, pid: Pid, context: Box<Context>) {
|
||||
let mut proc_lock = self.procs[pid].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),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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();
|
||||
let mut proc = proc_lock.as_mut().expect("process not exist");
|
||||
trace!("process {} {:?} -> {:?}", pid, proc.status, status);
|
||||
match (&proc.status, &status) {
|
||||
(Status::Ready, Status::Ready) => return,
|
||||
(Status::Ready, _) => self.scheduler.lock().remove(pid),
|
||||
(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),
|
||||
_ => {}
|
||||
}
|
||||
match proc.status {
|
||||
Status::Running(_) => proc.status_after_stop = status,
|
||||
_ => proc.status = status,
|
||||
}
|
||||
match proc.status {
|
||||
Status::Exited(_) => self.exit_handler(pid, proc),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_status(&self, pid: Pid) -> Option<Status> {
|
||||
self.procs[pid].lock().as_ref().map(|p| p.status.clone())
|
||||
}
|
||||
|
||||
/// Remove an exited proc `pid`.
|
||||
/// Its all children will be set parent to 0.
|
||||
pub fn remove(&self, pid: Pid) {
|
||||
let mut proc_lock = self.procs[pid].lock();
|
||||
let proc = proc_lock.as_ref().expect("process not exist");
|
||||
match proc.status {
|
||||
Status::Exited(_) => {}
|
||||
_ => panic!("can not remove non-exited process"),
|
||||
}
|
||||
// orphan procs
|
||||
for child in proc.children.iter() {
|
||||
(&self.procs[*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
|
||||
*proc_lock = None;
|
||||
}
|
||||
|
||||
/// Sleep `pid` for `time` ticks.
|
||||
/// `time` == 0 means sleep forever
|
||||
pub fn sleep(&self, pid: Pid, time: usize) {
|
||||
self.set_status(pid, Status::Sleeping);
|
||||
if time != 0 {
|
||||
self.event_hub.lock().push(time, Event::Wakeup(pid));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wakeup(&self, pid: Pid) {
|
||||
self.set_status(pid, Status::Ready);
|
||||
}
|
||||
|
||||
pub fn wait(&self, pid: Pid, target: Pid) {
|
||||
self.set_status(pid, Status::Waiting(target));
|
||||
}
|
||||
pub fn wait_child(&self, pid: Pid) {
|
||||
self.set_status(pid, Status::Waiting(0));
|
||||
}
|
||||
|
||||
pub fn get_children(&self, pid: Pid) -> Vec<Pid> {
|
||||
self.procs[pid].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));
|
||||
}
|
||||
/// Called when a process exit
|
||||
fn exit_handler(&self, pid: Pid, proc: &mut Process) {
|
||||
// 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),
|
||||
_ => {}
|
||||
}
|
||||
// drop its context
|
||||
proc.context = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn new_vec_default<T: Default>(size: usize) -> Vec<T> {
|
||||
let mut vec = Vec::new();
|
||||
vec.resize_default(size);
|
||||
vec
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
[package]
|
||||
name = "rcore-process"
|
||||
name = "rcore-thread"
|
||||
version = "0.1.0"
|
||||
authors = ["WangRunji <wangrunji0408@163.com>"]
|
||||
description = "Bare-metal thread scheduler and executor"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
authors = ["WangRunji <wangrunji0408@163.com>"]
|
||||
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"
|
||||
]
|
@ -0,0 +1,5 @@
|
||||
# Example of `rcore-thread` crate
|
||||
|
||||
```bash
|
||||
bootimage run --release
|
||||
```
|
@ -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<Self> {
|
||||
Box::new(core::mem::uninitialized())
|
||||
}
|
||||
fn new(entry: extern fn(usize) -> !, arg0: usize) -> Box<Self> {
|
||||
let mut thread = unsafe { Thread::init() };
|
||||
let rsp = thread.stack.as_ptr() as usize + STACK_SIZE - core::mem::size_of::<ContextData>();
|
||||
// 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<Context> {
|
||||
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");
|
||||
}
|
@ -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"
|
||||
}
|
@ -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 {
|
@ -0,0 +1,219 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use spin::Mutex;
|
||||
use log::*;
|
||||
use crate::scheduler::Scheduler;
|
||||
use crate::timer::Timer;
|
||||
|
||||
struct Thread {
|
||||
status: Status,
|
||||
status_after_stop: Status,
|
||||
context: Option<Box<Context>>,
|
||||
parent: Tid,
|
||||
children: Vec<Tid>,
|
||||
}
|
||||
|
||||
pub type Tid = usize;
|
||||
type ExitCode = usize;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Status {
|
||||
Ready,
|
||||
Running(usize),
|
||||
Sleeping,
|
||||
Waiting(Tid),
|
||||
/// aka ZOMBIE. Its context was dropped.
|
||||
Exited(ExitCode),
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum Event {
|
||||
Wakeup(Tid),
|
||||
}
|
||||
|
||||
pub trait Context {
|
||||
unsafe fn switch_to(&mut self, target: &mut Context);
|
||||
}
|
||||
|
||||
pub struct ThreadPool {
|
||||
threads: Vec<Mutex<Option<Thread>>>,
|
||||
scheduler: Mutex<Box<Scheduler>>,
|
||||
timer: Mutex<Timer<Event>>,
|
||||
}
|
||||
|
||||
impl ThreadPool {
|
||||
pub fn new(scheduler: Box<Scheduler>, max_proc_num: usize) -> Self {
|
||||
ThreadPool {
|
||||
threads: new_vec_default(max_proc_num),
|
||||
scheduler: Mutex::new(scheduler),
|
||||
timer: Mutex::new(Timer::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_tid(&self) -> Tid {
|
||||
for (i, proc) in self.threads.iter().enumerate() {
|
||||
if proc.lock().is_none() {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
panic!("Process number exceeded");
|
||||
}
|
||||
|
||||
/// Add a new process
|
||||
pub fn add(&self, context: Box<Context>, 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(tid);
|
||||
self.threads[parent].lock().as_mut().expect("invalid parent proc")
|
||||
.children.push(tid);
|
||||
tid
|
||||
}
|
||||
|
||||
/// Make process `tid` time slice -= 1.
|
||||
/// Return true if time slice == 0.
|
||||
/// Called by timer interrupt handler.
|
||||
pub fn tick(&self, tid: Tid) -> bool {
|
||||
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),
|
||||
}
|
||||
}
|
||||
self.scheduler.lock().tick(tid)
|
||||
}
|
||||
|
||||
/// 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) -> (Tid, Box<Context>) {
|
||||
let mut scheduler = self.scheduler.lock();
|
||||
let tid = scheduler.select()
|
||||
.expect("failed to select a runnable process");
|
||||
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);
|
||||
(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, tid: Tid, context: Box<Context>) {
|
||||
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(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, 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 {} {:?} -> {:?}", tid, proc.status, status);
|
||||
match (&proc.status, &status) {
|
||||
(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.timer.lock().stop(Event::Wakeup(tid)),
|
||||
(_, Status::Ready) => self.scheduler.lock().insert(tid),
|
||||
_ => {}
|
||||
}
|
||||
match proc.status {
|
||||
Status::Running(_) => proc.status_after_stop = status,
|
||||
_ => proc.status = status,
|
||||
}
|
||||
match proc.status {
|
||||
Status::Exited(_) => self.exit_handler(tid, proc),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_status(&self, tid: Tid) -> Option<Status> {
|
||||
self.threads[tid].lock().as_ref().map(|p| p.status.clone())
|
||||
}
|
||||
|
||||
/// Remove an exited proc `tid`.
|
||||
/// Its all children will be set parent to 0.
|
||||
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(_) => {}
|
||||
_ => panic!("can not remove non-exited process"),
|
||||
}
|
||||
// orphan procs
|
||||
for child in proc.children.iter() {
|
||||
(&self.threads[*child]).lock().as_mut().expect("process not exist").parent = 0;
|
||||
}
|
||||
// remove self from parent's children list
|
||||
self.threads[proc.parent].lock().as_mut().expect("process not exist")
|
||||
.children.retain(|&i| i != tid);
|
||||
// release the tid
|
||||
*proc_lock = None;
|
||||
}
|
||||
|
||||
/// Sleep `tid` for `time` ticks.
|
||||
/// `time` == 0 means sleep forever
|
||||
pub fn sleep(&self, tid: Tid, time: usize) {
|
||||
self.set_status(tid, Status::Sleeping);
|
||||
if time != 0 {
|
||||
self.timer.lock().start(time, Event::Wakeup(tid));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wakeup(&self, tid: Tid) {
|
||||
self.set_status(tid, Status::Ready);
|
||||
}
|
||||
|
||||
pub fn wait(&self, tid: Tid, target: Tid) {
|
||||
self.set_status(tid, Status::Waiting(target));
|
||||
}
|
||||
pub fn wait_child(&self, tid: Tid) {
|
||||
self.set_status(tid, Status::Waiting(0));
|
||||
}
|
||||
|
||||
pub fn get_children(&self, tid: Tid) -> Vec<Tid> {
|
||||
self.threads[tid].lock().as_ref().expect("process not exist").children.clone()
|
||||
}
|
||||
|
||||
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, 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 == tid || target == 0 => self.wakeup(parent),
|
||||
_ => {}
|
||||
}
|
||||
// drop its context
|
||||
proc.context = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn new_vec_default<T: Default>(size: usize) -> Vec<T> {
|
||||
let mut vec = Vec::new();
|
||||
vec.resize_default(size);
|
||||
vec
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
//! A simple timer
|
||||
|
||||
use alloc::collections::VecDeque;
|
||||
|
||||
type Time = usize;
|
||||
|
||||
struct Event<T> {
|
||||
time: Time,
|
||||
data: T,
|
||||
}
|
||||
|
||||
/// A simple timer using ordered dequeue
|
||||
pub struct Timer<T> {
|
||||
tick: Time,
|
||||
timers: VecDeque<Event<T>>,
|
||||
}
|
||||
|
||||
impl<T: PartialEq> Timer<T> {
|
||||
/// 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<T> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue