parent
c9a68634f5
commit
b77a52dccb
@ -1,8 +1,6 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crate/bit-allocator",
|
|
||||||
"crate/memory",
|
"crate/memory",
|
||||||
"crate/sync",
|
"crate/sync",
|
||||||
"crate/thread",
|
|
||||||
]
|
]
|
||||||
exclude = ["kernel", "bootloader", "user/rust"]
|
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