parent
c9a68634f5
commit
b77a52dccb
@ -1,8 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"crate/bit-allocator",
|
||||
"crate/memory",
|
||||
"crate/sync",
|
||||
"crate/thread",
|
||||
]
|
||||
exclude = ["kernel", "bootloader", "user/rust"]
|
||||
|
@ -1,7 +0,0 @@
|
||||
[package]
|
||||
name = "bit-allocator"
|
||||
version = "0.1.0"
|
||||
authors = ["WangRunji <wangrunji0408@163.com>"]
|
||||
|
||||
[dependencies]
|
||||
bit_field = "0.9"
|
@ -1,221 +0,0 @@
|
||||
#![no_std]
|
||||
#![feature(asm)]
|
||||
|
||||
extern crate bit_field;
|
||||
|
||||
use bit_field::BitField;
|
||||
use core::ops::Range;
|
||||
|
||||
/// Allocator of a bitmap, able to allocate / free bits.
|
||||
///
|
||||
/// CAP: the bitmap has a total of CAP bits, numbered from 0 to CAP-1 inclusively.
|
||||
///
|
||||
/// alloc: allocate a free bit.
|
||||
/// dealloc: free an allocated bit.
|
||||
///
|
||||
/// insert: mark bits in the range as allocated (available)
|
||||
/// remove: reverse of insert
|
||||
///
|
||||
/// any: whether there are free bits remaining
|
||||
/// test: whether a specific bit is free
|
||||
pub trait BitAlloc: Default {
|
||||
const CAP: usize;
|
||||
fn alloc(&mut self) -> Option<usize>;
|
||||
fn dealloc(&mut self, key: usize);
|
||||
fn insert(&mut self, range: Range<usize>);
|
||||
fn remove(&mut self, range: Range<usize>);
|
||||
fn any(&self) -> bool;
|
||||
fn test(&self, key: usize) -> bool;
|
||||
}
|
||||
|
||||
pub type BitAlloc256 = BitAllocCascade16<BitAlloc16>;
|
||||
pub type BitAlloc4K = BitAllocCascade16<BitAlloc256>;
|
||||
pub type BitAlloc64K = BitAllocCascade16<BitAlloc4K>;
|
||||
pub type BitAlloc1M = BitAllocCascade16<BitAlloc64K>;
|
||||
pub type BitAlloc16M = BitAllocCascade16<BitAlloc1M>;
|
||||
pub type BitAlloc256M = BitAllocCascade16<BitAlloc16M>;
|
||||
|
||||
/// Implement the bit allocator by segment tree algorithm.
|
||||
#[derive(Default)]
|
||||
pub struct BitAllocCascade16<T: BitAlloc> {
|
||||
bitset: u16, // for each bit, 1 indicates available, 0 indicates inavailable
|
||||
sub: [T; 16],
|
||||
}
|
||||
|
||||
impl<T: BitAlloc> BitAlloc for BitAllocCascade16<T> {
|
||||
const CAP: usize = T::CAP * 16;
|
||||
|
||||
fn alloc(&mut self) -> Option<usize> {
|
||||
if self.any() {
|
||||
let i = log2(self.bitset);
|
||||
let res = self.sub[i].alloc().unwrap() + i * T::CAP;
|
||||
self.bitset.set_bit(i, self.sub[i].any());
|
||||
Some(res)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn dealloc(&mut self, key: usize) {
|
||||
let i = key / T::CAP;
|
||||
self.sub[i].dealloc(key % T::CAP);
|
||||
self.bitset.set_bit(i, true);
|
||||
}
|
||||
fn insert(&mut self, range: Range<usize>) {
|
||||
self.for_range(range, |sub: &mut T, range| sub.insert(range));
|
||||
}
|
||||
fn remove(&mut self, range: Range<usize>) {
|
||||
self.for_range(range, |sub: &mut T, range| sub.remove(range));
|
||||
}
|
||||
fn any(&self) -> bool {
|
||||
self.bitset != 0
|
||||
}
|
||||
fn test(&self, key: usize) -> bool {
|
||||
self.sub[key / T::CAP].test(key % T::CAP)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BitAlloc> BitAllocCascade16<T> {
|
||||
fn for_range(&mut self, range: Range<usize>, f: impl Fn(&mut T, Range<usize>)) {
|
||||
let Range { start, end } = range;
|
||||
assert!(start <= end);
|
||||
assert!(end <= Self::CAP);
|
||||
for i in start / T::CAP..=(end - 1) / T::CAP {
|
||||
let begin = if start / T::CAP == i {
|
||||
start % T::CAP
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let end = if end / T::CAP == i {
|
||||
end % T::CAP
|
||||
} else {
|
||||
T::CAP
|
||||
};
|
||||
f(&mut self.sub[i], begin..end);
|
||||
self.bitset.set_bit(i, self.sub[i].any());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BitAlloc16(u16);
|
||||
|
||||
/// BitAlloc16 acts as the leaf (except the leaf bits of course) nodes
|
||||
/// in the segment trees.
|
||||
impl BitAlloc for BitAlloc16 {
|
||||
const CAP: usize = 16;
|
||||
|
||||
fn alloc(&mut self) -> Option<usize> {
|
||||
if self.any() {
|
||||
let i = log2(self.0);
|
||||
self.0.set_bit(i, false);
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn dealloc(&mut self, key: usize) {
|
||||
assert!(!self.test(key));
|
||||
self.0.set_bit(key, true);
|
||||
}
|
||||
fn insert(&mut self, range: Range<usize>) {
|
||||
self.0.set_bits(range.clone(), 0xffff.get_bits(range));
|
||||
}
|
||||
fn remove(&mut self, range: Range<usize>) {
|
||||
self.0.set_bits(range, 0);
|
||||
}
|
||||
fn any(&self) -> bool {
|
||||
self.0 != 0
|
||||
}
|
||||
fn test(&self, key: usize) -> bool {
|
||||
self.0.get_bit(key)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn log2(x: u16) -> usize {
|
||||
assert_ne!(x, 0);
|
||||
let pos: u16;
|
||||
unsafe { asm!("bsrw $1, $0" :"=r"(pos) :"r"(x) : :"volatile") };
|
||||
pos as usize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
fn log2(x: u16) -> usize {
|
||||
log2_naive(x)
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[inline(always)]
|
||||
fn log2_naive(mut x: u16) -> usize {
|
||||
//a naive implement
|
||||
assert_ne!(x, 0);
|
||||
let mut pos = -1;
|
||||
while x != 0 {
|
||||
pos += 1;
|
||||
x >>= 1;
|
||||
}
|
||||
pos as usize
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
#[test]
|
||||
fn log2_() {
|
||||
for x in 1..=0xffff {
|
||||
assert_eq!(log2(x), log2_naive(x), "log2 failed: {}", x);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitalloc16() {
|
||||
let mut ba = BitAlloc16::default();
|
||||
assert_eq!(BitAlloc16::CAP, 16);
|
||||
ba.insert(0..16);
|
||||
for i in 0..16 {
|
||||
assert_eq!(ba.test(i), true);
|
||||
}
|
||||
ba.remove(8..14);
|
||||
assert_eq!(ba.alloc(), Some(15));
|
||||
assert_eq!(ba.alloc(), Some(14));
|
||||
assert_eq!(ba.alloc(), Some(7));
|
||||
ba.dealloc(14);
|
||||
ba.dealloc(15);
|
||||
ba.dealloc(7);
|
||||
|
||||
for _ in 0..10 {
|
||||
assert!(ba.alloc().is_some());
|
||||
}
|
||||
assert!(!ba.any());
|
||||
assert!(ba.alloc().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bitalloc4k() {
|
||||
let mut ba = BitAlloc4K::default();
|
||||
assert_eq!(BitAlloc4K::CAP, 4096);
|
||||
ba.insert(0..4096);
|
||||
for i in 0..4096 {
|
||||
assert_eq!(ba.test(i), true);
|
||||
}
|
||||
ba.remove(8..4094);
|
||||
for i in 0..4096 {
|
||||
assert_eq!(ba.test(i), i < 8 || i >= 4094);
|
||||
}
|
||||
assert_eq!(ba.alloc(), Some(4095));
|
||||
assert_eq!(ba.alloc(), Some(4094));
|
||||
assert_eq!(ba.alloc(), Some(7));
|
||||
ba.dealloc(4095);
|
||||
ba.dealloc(4094);
|
||||
ba.dealloc(7);
|
||||
|
||||
for _ in 0..10 {
|
||||
assert!(ba.alloc().is_some());
|
||||
}
|
||||
assert!(ba.alloc().is_none());
|
||||
}
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
[package]
|
||||
name = "rcore-thread"
|
||||
version = "0.1.0"
|
||||
authors = ["WangRunji <wangrunji0408@163.com>"]
|
||||
description = "Bare-metal thread scheduler and executor"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
spin = "0.5"
|
||||
deque = { git = "https://github.com/rcore-os/deque.git", branch = "no_std" }
|
@ -1 +0,0 @@
|
||||
fn main() {}
|
@ -1,19 +0,0 @@
|
||||
[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"
|
||||
]
|
@ -1,5 +0,0 @@
|
||||
# Example of `rcore-thread` crate
|
||||
|
||||
```bash
|
||||
bootimage run --release
|
||||
```
|
@ -1,204 +0,0 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(asm)]
|
||||
#![feature(alloc)]
|
||||
#![feature(naked_functions)]
|
||||
#![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 = 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!("[{}] yield", tid);
|
||||
thread::yield_now();
|
||||
serial_println!("[{}] spawn", tid);
|
||||
let t2 = thread::spawn(|| {
|
||||
let tid = processor().tid();
|
||||
serial_println!("[{}] yield", tid);
|
||||
thread::yield_now();
|
||||
serial_println!("[{}] return 8", tid);
|
||||
8
|
||||
});
|
||||
serial_println!("[{}] join", tid);
|
||||
let ret = t2.join();
|
||||
serial_println!("[{}] get {:?}", tid, ret);
|
||||
serial_println!("[{}] 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" )
|
||||
}
|
||||
}
|
||||
|
||||
fn set_tid(&mut self, _tid: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) -> ! {
|
||||
serial_println!("\n{}", info);
|
||||
|
||||
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");
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"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,63 +0,0 @@
|
||||
//! Enable and disable interrupt for each architecture.
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub unsafe fn disable_and_store() -> usize {
|
||||
let rflags: usize;
|
||||
asm!("pushfq; popq $0; cli" : "=r"(rflags) ::: "volatile");
|
||||
rflags & (1 << 9)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub unsafe fn restore(flags: usize) {
|
||||
if flags != 0 {
|
||||
asm!("sti" :::: "volatile");
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub unsafe fn enable_and_wfi() {
|
||||
asm!("sti; hlt" :::: "volatile");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
|
||||
pub unsafe fn disable_and_store() -> usize {
|
||||
let sstatus: usize;
|
||||
asm!("csrci sstatus, 1 << 1" : "=r"(sstatus) ::: "volatile");
|
||||
sstatus & (1 << 1)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
|
||||
pub unsafe fn restore(flags: usize) {
|
||||
asm!("csrs sstatus, $0" :: "r"(flags) :: "volatile");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
|
||||
pub unsafe fn enable_and_wfi() {
|
||||
asm!("csrsi sstatus, 1 << 1; wfi" :::: "volatile");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub unsafe fn disable_and_store() -> usize {
|
||||
let daif: u32;
|
||||
asm!("mrs $0, DAIF; msr daifset, #2": "=r"(daif) ::: "volatile");
|
||||
daif as usize
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub unsafe fn restore(flags: usize) {
|
||||
asm!("msr DAIF, $0" :: "r"(flags as u32) :: "volatile");
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub unsafe fn enable_and_wfi() {
|
||||
asm!("msr daifclr, #2; wfi" :::: "volatile");
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![feature(alloc)]
|
||||
#![feature(const_fn)]
|
||||
#![feature(linkage)]
|
||||
#![feature(vec_resize_default)]
|
||||
#![feature(asm)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod interrupt;
|
||||
mod processor;
|
||||
pub mod scheduler;
|
||||
pub mod std_thread;
|
||||
mod thread_pool;
|
||||
mod timer;
|
||||
|
||||
pub use crate::processor::Processor;
|
||||
pub use crate::thread_pool::*;
|
@ -1,127 +0,0 @@
|
||||
use crate::interrupt;
|
||||
use crate::thread_pool::*;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::sync::Arc;
|
||||
use core::cell::UnsafeCell;
|
||||
use log::*;
|
||||
|
||||
/// Thread executor
|
||||
///
|
||||
/// Per-CPU struct. Defined at global.
|
||||
/// Only accessed by associated CPU with interrupt disabled.
|
||||
#[derive(Default)]
|
||||
pub struct Processor {
|
||||
inner: UnsafeCell<Option<ProcessorInner>>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for Processor {}
|
||||
|
||||
struct ProcessorInner {
|
||||
id: usize,
|
||||
proc: Option<(Tid, Box<Context>)>,
|
||||
loop_context: Box<Context>,
|
||||
manager: Arc<ThreadPool>,
|
||||
}
|
||||
|
||||
impl Processor {
|
||||
pub const fn new() -> Self {
|
||||
Processor {
|
||||
inner: UnsafeCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn init(&self, id: usize, context: Box<Context>, manager: Arc<ThreadPool>) {
|
||||
*self.inner.get() = Some(ProcessorInner {
|
||||
id,
|
||||
proc: None,
|
||||
loop_context: context,
|
||||
manager,
|
||||
});
|
||||
}
|
||||
|
||||
fn inner(&self) -> &mut ProcessorInner {
|
||||
unsafe { &mut *self.inner.get() }
|
||||
.as_mut()
|
||||
.expect("Processor is not initialized")
|
||||
}
|
||||
|
||||
/// Begin running processes after CPU setup.
|
||||
///
|
||||
/// This function never returns. It loops, doing:
|
||||
/// - choose a process to run
|
||||
/// - switch to start running that process
|
||||
/// - eventually that process transfers control
|
||||
/// via switch back to the scheduler.
|
||||
pub fn run(&self) -> ! {
|
||||
let inner = self.inner();
|
||||
unsafe {
|
||||
interrupt::disable_and_store();
|
||||
}
|
||||
loop {
|
||||
if let Some(proc) = inner.manager.run(inner.id) {
|
||||
trace!("CPU{} begin running thread {}", inner.id, proc.0);
|
||||
inner.proc = Some(proc);
|
||||
unsafe {
|
||||
inner
|
||||
.loop_context
|
||||
.switch_to(&mut *inner.proc.as_mut().unwrap().1);
|
||||
}
|
||||
let (tid, context) = inner.proc.take().unwrap();
|
||||
trace!("CPU{} stop running thread {}", inner.id, tid);
|
||||
inner.manager.stop(tid, context);
|
||||
} else {
|
||||
trace!("CPU{} idle", inner.id);
|
||||
unsafe {
|
||||
interrupt::enable_and_wfi();
|
||||
}
|
||||
// wait for a timer interrupt
|
||||
unsafe {
|
||||
interrupt::disable_and_store();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by process running on this Processor.
|
||||
/// Yield and reschedule.
|
||||
///
|
||||
/// The interrupt may be enabled.
|
||||
pub fn yield_now(&self) {
|
||||
let inner = self.inner();
|
||||
unsafe {
|
||||
let flags = interrupt::disable_and_store();
|
||||
inner
|
||||
.proc
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.1
|
||||
.switch_to(&mut *inner.loop_context);
|
||||
interrupt::restore(flags);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tid(&self) -> Tid {
|
||||
self.inner().proc.as_ref().unwrap().0
|
||||
}
|
||||
|
||||
pub fn context(&self) -> &Context {
|
||||
&*self.inner().proc.as_ref().unwrap().1
|
||||
}
|
||||
|
||||
pub fn manager(&self) -> &ThreadPool {
|
||||
&*self.inner().manager
|
||||
}
|
||||
|
||||
/// Called by timer interrupt handler.
|
||||
///
|
||||
/// The interrupt should be disabled in the handler.
|
||||
pub fn tick(&self) {
|
||||
// If I'm idle, tid == None, need_reschedule == false.
|
||||
// Will go back to `run()` after interrupt return.
|
||||
let tid = self.inner().proc.as_ref().map(|p| p.0);
|
||||
let need_reschedule = self.manager().tick(self.inner().id, tid);
|
||||
if need_reschedule {
|
||||
self.yield_now();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
use alloc::{collections::BinaryHeap, vec::Vec};
|
||||
|
||||
use log::*;
|
||||
use spin::Mutex;
|
||||
|
||||
pub use self::o1::O1Scheduler;
|
||||
pub use self::rr::RRScheduler;
|
||||
pub use self::stride::StrideScheduler;
|
||||
pub use self::work_stealing::WorkStealingScheduler;
|
||||
|
||||
mod o1;
|
||||
mod rr;
|
||||
mod stride;
|
||||
mod work_stealing;
|
||||
|
||||
type Tid = usize;
|
||||
|
||||
/// The scheduler for a ThreadPool
|
||||
pub trait Scheduler: 'static {
|
||||
/// Push a thread to the back of ready queue.
|
||||
fn push(&self, tid: Tid);
|
||||
/// Select a thread to run, pop it from the queue.
|
||||
fn pop(&self, cpu_id: usize) -> Option<Tid>;
|
||||
/// Got a tick from CPU.
|
||||
/// Return true if need reschedule.
|
||||
fn tick(&self, current_tid: Tid) -> bool;
|
||||
/// Set priority of a thread.
|
||||
fn set_priority(&self, tid: Tid, priority: u8);
|
||||
}
|
||||
|
||||
fn expand<T: Default + Clone>(vec: &mut Vec<T>, id: usize) {
|
||||
let len = vec.len();
|
||||
vec.resize(len.max(id + 1), T::default());
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
//! O(1) scheduler introduced in Linux 2.6
|
||||
//!
|
||||
//! Two queues are maintained, one is active, another is inactive.
|
||||
//! Take the first task from the active queue to run. When it is empty, swap active and inactive queues.
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct O1Scheduler {
|
||||
inner: Mutex<O1SchedulerInner>,
|
||||
}
|
||||
|
||||
struct O1SchedulerInner {
|
||||
active_queue: usize,
|
||||
queues: [Vec<Tid>; 2],
|
||||
}
|
||||
|
||||
impl Scheduler for O1Scheduler {
|
||||
fn push(&self, tid: usize) {
|
||||
self.inner.lock().push(tid);
|
||||
}
|
||||
fn pop(&self, _cpu_id: usize) -> Option<usize> {
|
||||
self.inner.lock().pop()
|
||||
}
|
||||
fn tick(&self, current_tid: usize) -> bool {
|
||||
self.inner.lock().tick(current_tid)
|
||||
}
|
||||
fn set_priority(&self, _tid: usize, _priority: u8) {}
|
||||
}
|
||||
|
||||
impl O1Scheduler {
|
||||
pub fn new() -> Self {
|
||||
let inner = O1SchedulerInner {
|
||||
active_queue: 0,
|
||||
queues: [Vec::new(), Vec::new()],
|
||||
};
|
||||
O1Scheduler {
|
||||
inner: Mutex::new(inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl O1SchedulerInner {
|
||||
fn push(&mut self, tid: Tid) {
|
||||
let inactive_queue = 1 - self.active_queue;
|
||||
self.queues[inactive_queue].push(tid);
|
||||
trace!("o1 push {}", tid - 1);
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<Tid> {
|
||||
let ret = match self.queues[self.active_queue].pop() {
|
||||
Some(tid) => return Some(tid),
|
||||
None => {
|
||||
// active queue is empty, swap 'em
|
||||
self.active_queue = 1 - self.active_queue;
|
||||
self.queues[self.active_queue].pop()
|
||||
}
|
||||
};
|
||||
trace!("o1 pop {:?}", ret);
|
||||
ret
|
||||
}
|
||||
|
||||
fn tick(&mut self, _current: Tid) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
pub struct RRScheduler {
|
||||
inner: Mutex<RRSchedulerInner>,
|
||||
}
|
||||
|
||||
struct RRSchedulerInner {
|
||||
max_time_slice: usize,
|
||||
infos: Vec<RRProcInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
struct RRProcInfo {
|
||||
present: bool,
|
||||
rest_slice: usize,
|
||||
prev: Tid,
|
||||
next: Tid,
|
||||
}
|
||||
|
||||
impl Scheduler for RRScheduler {
|
||||
fn push(&self, tid: usize) {
|
||||
self.inner.lock().push(tid);
|
||||
}
|
||||
fn pop(&self, _cpu_id: usize) -> Option<usize> {
|
||||
self.inner.lock().pop()
|
||||
}
|
||||
fn tick(&self, current_tid: usize) -> bool {
|
||||
self.inner.lock().tick(current_tid)
|
||||
}
|
||||
fn set_priority(&self, _tid: usize, _priority: u8) {}
|
||||
}
|
||||
|
||||
impl RRScheduler {
|
||||
pub fn new(max_time_slice: usize) -> Self {
|
||||
let inner = RRSchedulerInner {
|
||||
max_time_slice,
|
||||
infos: Vec::default(),
|
||||
};
|
||||
RRScheduler {
|
||||
inner: Mutex::new(inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RRSchedulerInner {
|
||||
fn push(&mut self, tid: Tid) {
|
||||
let tid = tid + 1;
|
||||
expand(&mut self.infos, tid);
|
||||
{
|
||||
let info = &mut self.infos[tid];
|
||||
assert!(!info.present);
|
||||
info.present = true;
|
||||
if info.rest_slice == 0 {
|
||||
info.rest_slice = self.max_time_slice;
|
||||
}
|
||||
}
|
||||
self._list_add_before(tid, 0);
|
||||
trace!("rr push {}", tid - 1);
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<Tid> {
|
||||
let ret = match self.infos[0].next {
|
||||
0 => None,
|
||||
tid => {
|
||||
self.infos[tid].present = false;
|
||||
self._list_remove(tid);
|
||||
Some(tid - 1)
|
||||
}
|
||||
};
|
||||
trace!("rr pop {:?}", ret);
|
||||
ret
|
||||
}
|
||||
|
||||
fn tick(&mut self, current: Tid) -> bool {
|
||||
let current = current + 1;
|
||||
expand(&mut self.infos, current);
|
||||
assert!(!self.infos[current].present);
|
||||
|
||||
let rest = &mut self.infos[current].rest_slice;
|
||||
if *rest > 0 {
|
||||
*rest -= 1;
|
||||
} else {
|
||||
warn!("current process rest_slice = 0, need reschedule")
|
||||
}
|
||||
*rest == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl RRSchedulerInner {
|
||||
fn _list_add_before(&mut self, i: Tid, at: Tid) {
|
||||
let prev = self.infos[at].prev;
|
||||
self.infos[i].next = at;
|
||||
self.infos[i].prev = prev;
|
||||
self.infos[prev].next = i;
|
||||
self.infos[at].prev = i;
|
||||
}
|
||||
fn _list_add_after(&mut self, i: Tid, at: Tid) {
|
||||
let next = self.infos[at].next;
|
||||
self._list_add_before(i, next);
|
||||
}
|
||||
fn _list_remove(&mut self, i: Tid) {
|
||||
let next = self.infos[i].next;
|
||||
let prev = self.infos[i].prev;
|
||||
self.infos[next].prev = prev;
|
||||
self.infos[prev].next = next;
|
||||
self.infos[i].next = 0;
|
||||
self.infos[i].prev = 0;
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
//! Stride scheduler
|
||||
//!
|
||||
//! Each task is assigned a priority. Each task has a running stride.
|
||||
//! The task with least stride is selected to run.
|
||||
//! When a task is rescheduled, its stride is added to proportional to 1 / priority.
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct StrideScheduler {
|
||||
inner: Mutex<StrideSchedulerInner>,
|
||||
}
|
||||
|
||||
pub struct StrideSchedulerInner {
|
||||
max_time_slice: usize,
|
||||
infos: Vec<StrideProcInfo>,
|
||||
queue: BinaryHeap<(Stride, Tid)>, // It's max heap, so pass < 0
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
struct StrideProcInfo {
|
||||
present: bool,
|
||||
rest_slice: usize,
|
||||
stride: Stride,
|
||||
priority: u8,
|
||||
}
|
||||
|
||||
impl StrideProcInfo {
|
||||
fn pass(&mut self) {
|
||||
const BIG_STRIDE: Stride = 1 << 20;
|
||||
let pass = if self.priority == 0 {
|
||||
BIG_STRIDE
|
||||
} else {
|
||||
BIG_STRIDE / self.priority as Stride
|
||||
};
|
||||
// FIXME: overflowing_add is not working ???
|
||||
// self.stride.overflowing_add(pass);
|
||||
self.stride += pass;
|
||||
}
|
||||
}
|
||||
|
||||
type Stride = i32;
|
||||
|
||||
impl Scheduler for StrideScheduler {
|
||||
fn push(&self, tid: usize) {
|
||||
self.inner.lock().push(tid);
|
||||
}
|
||||
fn pop(&self, _cpu_id: usize) -> Option<usize> {
|
||||
self.inner.lock().pop()
|
||||
}
|
||||
fn tick(&self, current_tid: usize) -> bool {
|
||||
self.inner.lock().tick(current_tid)
|
||||
}
|
||||
fn set_priority(&self, tid: usize, priority: u8) {
|
||||
self.inner.lock().set_priority(tid, priority);
|
||||
}
|
||||
}
|
||||
|
||||
impl StrideScheduler {
|
||||
pub fn new(max_time_slice: usize) -> Self {
|
||||
let inner = StrideSchedulerInner {
|
||||
max_time_slice,
|
||||
infos: Vec::default(),
|
||||
queue: BinaryHeap::default(),
|
||||
};
|
||||
StrideScheduler {
|
||||
inner: Mutex::new(inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StrideSchedulerInner {
|
||||
fn push(&mut self, tid: Tid) {
|
||||
expand(&mut self.infos, tid);
|
||||
let info = &mut self.infos[tid];
|
||||
assert!(!info.present);
|
||||
info.present = true;
|
||||
if info.rest_slice == 0 {
|
||||
info.rest_slice = self.max_time_slice;
|
||||
}
|
||||
self.queue.push((-info.stride, tid));
|
||||
trace!("stride push {}", tid);
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<Tid> {
|
||||
let ret = self.queue.pop().map(|(_, tid)| tid);
|
||||
if let Some(tid) = ret {
|
||||
let old_stride = self.infos[tid].stride;
|
||||
self.infos[tid].pass();
|
||||
let stride = self.infos[tid].stride;
|
||||
trace!("stride {} {:#x} -> {:#x}", tid, old_stride, stride);
|
||||
}
|
||||
trace!("stride pop {:?}", ret);
|
||||
ret
|
||||
}
|
||||
|
||||
fn tick(&mut self, current: Tid) -> bool {
|
||||
expand(&mut self.infos, current);
|
||||
assert!(!self.infos[current].present);
|
||||
|
||||
let rest = &mut self.infos[current].rest_slice;
|
||||
if *rest > 0 {
|
||||
*rest -= 1;
|
||||
} else {
|
||||
warn!("current process rest_slice = 0, need reschedule")
|
||||
}
|
||||
*rest == 0
|
||||
}
|
||||
|
||||
fn set_priority(&mut self, tid: Tid, priority: u8) {
|
||||
self.infos[tid].priority = priority;
|
||||
trace!("stride {} priority = {}", tid, priority);
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
//! Work stealing scheduler
|
||||
//!
|
||||
//! Each CPU has its own queue, and each CPU takes new jobs from its own queue.
|
||||
//! When its queue is empty, steal jobs from other CPU's queue.
|
||||
|
||||
use super::*;
|
||||
use deque::{self, Stealer, Stolen, Worker};
|
||||
|
||||
pub struct WorkStealingScheduler {
|
||||
/// The ready queue of each processors
|
||||
workers: Vec<Worker<Tid>>,
|
||||
/// Stealers to all processors' queue
|
||||
stealers: Vec<Stealer<Tid>>,
|
||||
}
|
||||
|
||||
impl WorkStealingScheduler {
|
||||
pub fn new(core_num: usize) -> Self {
|
||||
let (workers, stealers) = (0..core_num).map(|_| deque::new()).unzip();
|
||||
WorkStealingScheduler { workers, stealers }
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheduler for WorkStealingScheduler {
|
||||
fn push(&self, tid: usize) {
|
||||
// not random, but uniform
|
||||
// no sync, because we don't need to
|
||||
static mut WORKER_CPU: usize = 0;
|
||||
let n = self.workers.len();
|
||||
let mut cpu = unsafe {
|
||||
WORKER_CPU = WORKER_CPU + 1;
|
||||
if WORKER_CPU >= n {
|
||||
WORKER_CPU -= n;
|
||||
}
|
||||
WORKER_CPU
|
||||
};
|
||||
|
||||
// potential racing, so we just check once more
|
||||
if cpu >= n {
|
||||
cpu -= n;
|
||||
}
|
||||
self.workers[cpu].push(tid);
|
||||
trace!("work-stealing: cpu{} push thread {}", cpu, tid);
|
||||
}
|
||||
|
||||
fn pop(&self, cpu_id: usize) -> Option<usize> {
|
||||
if let Some(tid) = self.workers[cpu_id].pop() {
|
||||
trace!("work-stealing: cpu{} pop thread {}", cpu_id, tid);
|
||||
return Some(tid);
|
||||
}
|
||||
let n = self.workers.len();
|
||||
for i in 1..n {
|
||||
let mut other_id = cpu_id + i;
|
||||
if other_id >= n {
|
||||
other_id -= n;
|
||||
}
|
||||
loop {
|
||||
match self.stealers[other_id].steal() {
|
||||
Stolen::Abort => {} // retry
|
||||
Stolen::Empty => break,
|
||||
Stolen::Data(tid) => {
|
||||
trace!(
|
||||
"work-stealing: cpu{} steal thread {} from cpu{}",
|
||||
cpu_id,
|
||||
tid,
|
||||
other_id
|
||||
);
|
||||
return Some(tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn tick(&self, _current_tid: usize) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_priority(&self, _tid: usize, _priority: u8) {}
|
||||
}
|
@ -1,221 +0,0 @@
|
||||
use crate::scheduler::Scheduler;
|
||||
use crate::timer::Timer;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use log::*;
|
||||
use spin::{Mutex, MutexGuard};
|
||||
|
||||
struct Thread {
|
||||
status: Status,
|
||||
status_after_stop: Status,
|
||||
waiter: Option<Tid>,
|
||||
context: Option<Box<Context>>,
|
||||
}
|
||||
|
||||
pub type Tid = usize;
|
||||
type ExitCode = usize;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Status {
|
||||
Ready,
|
||||
Running(usize),
|
||||
Sleeping,
|
||||
/// aka ZOMBIE. Its context was dropped.
|
||||
Exited(ExitCode),
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum Event {
|
||||
Wakeup(Tid),
|
||||
}
|
||||
|
||||
pub trait Context {
|
||||
/// Switch to target context
|
||||
unsafe fn switch_to(&mut self, target: &mut Context);
|
||||
|
||||
/// A tid is allocated for this context
|
||||
fn set_tid(&mut self, tid: Tid);
|
||||
}
|
||||
|
||||
pub struct ThreadPool {
|
||||
threads: Vec<Mutex<Option<Thread>>>,
|
||||
scheduler: Box<Scheduler>,
|
||||
timer: Mutex<Timer<Event>>,
|
||||
}
|
||||
|
||||
impl ThreadPool {
|
||||
pub fn new(scheduler: impl Scheduler, max_proc_num: usize) -> Self {
|
||||
ThreadPool {
|
||||
threads: new_vec_default(max_proc_num),
|
||||
scheduler: Box::new(scheduler),
|
||||
timer: Mutex::new(Timer::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_tid(&self) -> (Tid, MutexGuard<Option<Thread>>) {
|
||||
for (i, proc) in self.threads.iter().enumerate() {
|
||||
let thread = proc.lock();
|
||||
if thread.is_none() {
|
||||
return (i, thread);
|
||||
}
|
||||
}
|
||||
panic!("Thread number exceeded");
|
||||
}
|
||||
|
||||
/// Add a new thread
|
||||
/// Calls action with tid and thread context
|
||||
pub fn add(&self, mut context: Box<Context>) -> Tid {
|
||||
let (tid, mut thread) = self.alloc_tid();
|
||||
context.set_tid(tid);
|
||||
*thread = Some(Thread {
|
||||
status: Status::Ready,
|
||||
status_after_stop: Status::Ready,
|
||||
waiter: None,
|
||||
context: Some(context),
|
||||
});
|
||||
self.scheduler.push(tid);
|
||||
tid
|
||||
}
|
||||
|
||||
/// Make thread `tid` time slice -= 1.
|
||||
/// Return true if time slice == 0.
|
||||
/// Called by timer interrupt handler.
|
||||
pub(crate) fn tick(&self, cpu_id: usize, tid: Option<Tid>) -> bool {
|
||||
if cpu_id == 0 {
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
match tid {
|
||||
Some(tid) => self.scheduler.tick(tid),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the priority of thread `tid`
|
||||
pub fn set_priority(&self, tid: Tid, priority: u8) {
|
||||
self.scheduler.set_priority(tid, priority);
|
||||
}
|
||||
|
||||
/// Called by Processor to get a thread to run.
|
||||
/// The manager first mark it `Running`,
|
||||
/// then take out and return its Context.
|
||||
pub(crate) fn run(&self, cpu_id: usize) -> Option<(Tid, Box<Context>)> {
|
||||
self.scheduler.pop(cpu_id).map(|tid| {
|
||||
let mut proc_lock = self.threads[tid].lock();
|
||||
let mut proc = proc_lock.as_mut().expect("thread not exist");
|
||||
proc.status = Status::Running(cpu_id);
|
||||
(tid, proc.context.take().expect("context not exist"))
|
||||
})
|
||||
}
|
||||
|
||||
/// Called by Processor to finish running a thread
|
||||
/// and give its context back.
|
||||
pub(crate) fn stop(&self, tid: Tid, context: Box<Context>) {
|
||||
let mut proc_lock = self.threads[tid].lock();
|
||||
let proc = proc_lock.as_mut().expect("thread 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.push(tid),
|
||||
Status::Exited(_) => self.exit_handler(proc),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by `JoinHandle` to let thread `tid` wait for `target`.
|
||||
/// The `tid` is going to sleep, and will be woke up when `target` exit.
|
||||
/// (see `exit_handler()`)
|
||||
pub(crate) fn wait(&self, tid: Tid, target: Tid) {
|
||||
self.set_status(tid, Status::Sleeping);
|
||||
let mut target_lock = self.threads[target].lock();
|
||||
let target = target_lock.as_mut().expect("thread not exist");
|
||||
target.waiter = Some(tid);
|
||||
}
|
||||
|
||||
/// Switch the status of a thread.
|
||||
/// Insert/Remove it to/from scheduler if necessary.
|
||||
fn set_status(&self, tid: Tid, status: Status) {
|
||||
let mut proc_lock = self.threads[tid].lock();
|
||||
if let Some(mut proc) = proc_lock.as_mut() {
|
||||
trace!("thread {} {:?} -> {:?}", tid, proc.status, status);
|
||||
match (&proc.status, &status) {
|
||||
(Status::Ready, Status::Ready) => return,
|
||||
(Status::Ready, _) => panic!("can not remove a thread from ready queue"),
|
||||
(Status::Exited(_), _) => panic!("can not set status for a exited thread"),
|
||||
(Status::Sleeping, Status::Exited(_)) => self.timer.lock().stop(Event::Wakeup(tid)),
|
||||
(Status::Running(_), Status::Ready) => {} // thread will be added to scheduler in stop()
|
||||
(_, Status::Ready) => self.scheduler.push(tid),
|
||||
_ => {}
|
||||
}
|
||||
match proc.status {
|
||||
Status::Running(_) => proc.status_after_stop = status,
|
||||
_ => proc.status = status,
|
||||
}
|
||||
match proc.status {
|
||||
Status::Exited(_) => self.exit_handler(proc),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to remove an exited thread `tid`.
|
||||
/// Return its exit code if success.
|
||||
pub fn try_remove(&self, tid: Tid) -> Option<ExitCode> {
|
||||
let mut proc_lock = self.threads[tid].lock();
|
||||
let proc = proc_lock.as_ref().expect("thread not exist");
|
||||
match proc.status {
|
||||
Status::Exited(code) => {
|
||||
// release the tid
|
||||
*proc_lock = None;
|
||||
Some(code)
|
||||
}
|
||||
_ => 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) {
|
||||
let mut proc_lock = self.threads[tid].lock();
|
||||
if let Some(mut proc) = proc_lock.as_mut() {
|
||||
trace!("thread {} {:?} -> {:?}", tid, proc.status, Status::Ready);
|
||||
if let Status::Sleeping = proc.status {
|
||||
proc.status = Status::Ready;
|
||||
self.scheduler.push(tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 thread exit
|
||||
fn exit_handler(&self, proc: &mut Thread) {
|
||||
// wake up waiter
|
||||
if let Some(waiter) = proc.waiter {
|
||||
self.wakeup(waiter);
|
||||
}
|
||||
// drop its context
|
||||
proc.context = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn new_vec_default<T: Default>(size: usize) -> Vec<T> {
|
||||
let mut vec = Vec::new();
|
||||
vec.resize_with(size, Default::default);
|
||||
vec
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
//! 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