Merge branch 'main' into fix/kstack-leak
This commit is contained in:
commit
2dc89534a4
106 changed files with 3974 additions and 2260 deletions
|
@ -3,5 +3,5 @@ target = "riscv64gc-unknown-none-elf"
|
|||
|
||||
[target.riscv64gc-unknown-none-elf]
|
||||
rustflags = [
|
||||
"-Clink-args=-Tsrc/linker.ld",
|
||||
"-Clink-args=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes"
|
||||
]
|
||||
|
|
|
@ -10,10 +10,10 @@ edition = "2018"
|
|||
buddy_system_allocator = "0.6"
|
||||
bitflags = "1.2.1"
|
||||
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
|
||||
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
|
||||
embedded-graphics = "0.7.1"
|
||||
oorandom ="11"
|
||||
virtio-input-decoder = "0.1.4"
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
[features]
|
||||
board_qemu = []
|
||||
board_k210 = []
|
|
@ -19,7 +19,7 @@ ifeq ($(TEST), 1)
|
|||
endif
|
||||
|
||||
binary: elf
|
||||
$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
|
||||
@$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
|
||||
|
||||
build: binary
|
||||
|
||||
|
|
55
user/src/bin/adder.rs
Normal file
55
user/src/bin/adder.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid};
|
||||
|
||||
static mut A: usize = 0;
|
||||
const PER_THREAD_DEFAULT: usize = 10000;
|
||||
const THREAD_COUNT_DEFAULT: usize = 16;
|
||||
static mut PER_THREAD: usize = 0;
|
||||
|
||||
unsafe fn critical_section(t: &mut usize) {
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
*t = (*t) * (*t) % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
}
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
critical_section(&mut t);
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(argc: usize, argv: &[&str]) -> i32 {
|
||||
let mut thread_count = THREAD_COUNT_DEFAULT;
|
||||
let mut per_thread = PER_THREAD_DEFAULT;
|
||||
if argc >= 2 {
|
||||
thread_count = argv[1].parse().unwrap();
|
||||
if argc >= 3 {
|
||||
per_thread = argv[2].parse().unwrap();
|
||||
}
|
||||
}
|
||||
unsafe { PER_THREAD = per_thread; }
|
||||
let start = get_time();
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..thread_count {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
for tid in v.into_iter() {
|
||||
waittid(tid);
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
|
||||
0
|
||||
}
|
72
user/src/bin/adder_atomic.rs
Normal file
72
user/src/bin/adder_atomic.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use user_lib::{exit, get_time, thread_create, waittid, yield_};
|
||||
|
||||
static mut A: usize = 0;
|
||||
static OCCUPIED: AtomicBool = AtomicBool::new(false);
|
||||
const PER_THREAD_DEFAULT: usize = 10000;
|
||||
const THREAD_COUNT_DEFAULT: usize = 16;
|
||||
static mut PER_THREAD: usize = 0;
|
||||
|
||||
unsafe fn critical_section(t: &mut usize) {
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
*t = (*t) * (*t) % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
}
|
||||
|
||||
fn lock() {
|
||||
while OCCUPIED
|
||||
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
yield_();
|
||||
}
|
||||
}
|
||||
|
||||
fn unlock() {
|
||||
OCCUPIED.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
lock();
|
||||
critical_section(&mut t);
|
||||
unlock();
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(argc: usize, argv: &[&str]) -> i32 {
|
||||
let mut thread_count = THREAD_COUNT_DEFAULT;
|
||||
let mut per_thread = PER_THREAD_DEFAULT;
|
||||
if argc >= 2 {
|
||||
thread_count = argv[1].parse().unwrap();
|
||||
if argc >= 3 {
|
||||
per_thread = argv[2].parse().unwrap();
|
||||
}
|
||||
}
|
||||
unsafe { PER_THREAD = per_thread; }
|
||||
let start = get_time();
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..thread_count {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
for tid in v.into_iter() {
|
||||
waittid(tid);
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
|
||||
0
|
||||
}
|
59
user/src/bin/adder_mutex_blocking.rs
Normal file
59
user/src/bin/adder_mutex_blocking.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid};
|
||||
use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock};
|
||||
|
||||
static mut A: usize = 0;
|
||||
const PER_THREAD_DEFAULT: usize = 10000;
|
||||
const THREAD_COUNT_DEFAULT: usize = 16;
|
||||
static mut PER_THREAD: usize = 0;
|
||||
|
||||
unsafe fn critical_section(t: &mut usize) {
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
*t = (*t) * (*t) % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
}
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
mutex_lock(0);
|
||||
critical_section(&mut t);
|
||||
mutex_unlock(0);
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(argc: usize, argv: &[&str]) -> i32 {
|
||||
let mut thread_count = THREAD_COUNT_DEFAULT;
|
||||
let mut per_thread = PER_THREAD_DEFAULT;
|
||||
if argc >= 2 {
|
||||
thread_count = argv[1].parse().unwrap();
|
||||
if argc >= 3 {
|
||||
per_thread = argv[2].parse().unwrap();
|
||||
}
|
||||
}
|
||||
unsafe { PER_THREAD = per_thread; }
|
||||
|
||||
let start = get_time();
|
||||
assert_eq!(mutex_blocking_create(), 0);
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..thread_count {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
for tid in v.into_iter() {
|
||||
waittid(tid);
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
|
||||
0
|
||||
}
|
60
user/src/bin/adder_mutex_spin.rs
Normal file
60
user/src/bin/adder_mutex_spin.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid};
|
||||
use user_lib::{mutex_create, mutex_lock, mutex_unlock};
|
||||
|
||||
static mut A: usize = 0;
|
||||
const PER_THREAD_DEFAULT: usize = 10000;
|
||||
const THREAD_COUNT_DEFAULT: usize = 16;
|
||||
static mut PER_THREAD: usize = 0;
|
||||
|
||||
unsafe fn critical_section(t: &mut usize) {
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
*t = (*t) * (*t) % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
}
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
mutex_lock(0);
|
||||
critical_section(&mut t);
|
||||
mutex_unlock(0);
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(argc: usize, argv: &[&str]) -> i32 {
|
||||
let mut thread_count = THREAD_COUNT_DEFAULT;
|
||||
let mut per_thread = PER_THREAD_DEFAULT;
|
||||
if argc >= 2 {
|
||||
thread_count = argv[1].parse().unwrap();
|
||||
if argc >= 3 {
|
||||
per_thread = argv[2].parse().unwrap();
|
||||
}
|
||||
}
|
||||
unsafe { PER_THREAD = per_thread; }
|
||||
|
||||
let start = get_time();
|
||||
assert_eq!(mutex_create(), 0);
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..thread_count {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
for tid in v.into_iter() {
|
||||
waittid(tid);
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
|
||||
0
|
||||
}
|
90
user/src/bin/adder_peterson_spin.rs
Normal file
90
user/src/bin/adder_peterson_spin.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
//! It only works on a single CPU!
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid};
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
static mut A: usize = 0;
|
||||
static mut FLAG: [bool; 2] = [false; 2];
|
||||
static mut TURN: usize = 0;
|
||||
const PER_THREAD_DEFAULT: usize = 2000;
|
||||
const THREAD_COUNT_DEFAULT: usize = 2;
|
||||
static mut PER_THREAD: usize = 0;
|
||||
|
||||
unsafe fn critical_section(t: &mut usize) {
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
*t = (*t) * (*t) % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
}
|
||||
|
||||
unsafe fn lock(id: usize) {
|
||||
FLAG[id] = true;
|
||||
let j = 1 - id;
|
||||
TURN = j;
|
||||
// Tell the compiler not to reorder memory operations
|
||||
// across this fence.
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
// Why do we need to use volatile_read here?
|
||||
// Otherwise the compiler will assume that they will never
|
||||
// be changed on this thread. Thus, they will be accessed
|
||||
// only once!
|
||||
while vload!(&FLAG[j]) && vload!(&TURN) == j {}
|
||||
}
|
||||
|
||||
unsafe fn unlock(id: usize) {
|
||||
FLAG[id] = false;
|
||||
}
|
||||
|
||||
unsafe fn f(id: usize) -> ! {
|
||||
let mut t = 2usize;
|
||||
for _iter in 0..PER_THREAD {
|
||||
lock(id);
|
||||
critical_section(&mut t);
|
||||
unlock(id);
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(argc: usize, argv: &[&str]) -> i32 {
|
||||
let mut thread_count = THREAD_COUNT_DEFAULT;
|
||||
let mut per_thread = PER_THREAD_DEFAULT;
|
||||
if argc >= 2 {
|
||||
thread_count = argv[1].parse().unwrap();
|
||||
if argc >= 3 {
|
||||
per_thread = argv[2].parse().unwrap();
|
||||
}
|
||||
}
|
||||
unsafe { PER_THREAD = per_thread; }
|
||||
|
||||
// uncomment this if you want to check the assembly
|
||||
// println!(
|
||||
// "addr: lock={:#x}, unlock={:#x}",
|
||||
// lock as usize,
|
||||
// unlock as usize
|
||||
// );
|
||||
let start = get_time();
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(thread_count, 2, "Peterson works when there are only 2 threads.");
|
||||
for id in 0..thread_count {
|
||||
v.push(thread_create(f as usize, id) as usize);
|
||||
}
|
||||
let mut time_cost = Vec::new();
|
||||
for tid in v.iter() {
|
||||
time_cost.push(waittid(*tid));
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
|
||||
0
|
||||
}
|
89
user/src/bin/adder_peterson_yield.rs
Normal file
89
user/src/bin/adder_peterson_yield.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
//! It only works on a single CPU!
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid, yield_};
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
|
||||
static mut A: usize = 0;
|
||||
static mut FLAG: [bool; 2] = [false; 2];
|
||||
static mut TURN: usize = 0;
|
||||
const PER_THREAD_DEFAULT: usize = 2000;
|
||||
const THREAD_COUNT_DEFAULT: usize = 2;
|
||||
static mut PER_THREAD: usize = 0;
|
||||
|
||||
unsafe fn critical_section(t: &mut usize) {
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
*t = (*t) * (*t) % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
}
|
||||
|
||||
unsafe fn lock(id: usize) {
|
||||
FLAG[id] = true;
|
||||
let j = 1 - id;
|
||||
TURN = j;
|
||||
// Tell the compiler not to reorder memory operations
|
||||
// across this fence.
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
while FLAG[j] && TURN == j {
|
||||
yield_();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn unlock(id: usize) {
|
||||
FLAG[id] = false;
|
||||
}
|
||||
|
||||
unsafe fn f(id: usize) -> ! {
|
||||
let mut t = 2usize;
|
||||
for _iter in 0..PER_THREAD {
|
||||
lock(id);
|
||||
critical_section(&mut t);
|
||||
unlock(id);
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(argc: usize, argv: &[&str]) -> i32 {
|
||||
let mut thread_count = THREAD_COUNT_DEFAULT;
|
||||
let mut per_thread = PER_THREAD_DEFAULT;
|
||||
if argc >= 2 {
|
||||
thread_count = argv[1].parse().unwrap();
|
||||
if argc >= 3 {
|
||||
per_thread = argv[2].parse().unwrap();
|
||||
}
|
||||
}
|
||||
unsafe { PER_THREAD = per_thread; }
|
||||
|
||||
// uncomment this if you want to check the assembly
|
||||
// println!(
|
||||
// "addr: lock={:#x}, unlock={:#x}",
|
||||
// lock as usize,
|
||||
// unlock as usize
|
||||
// );
|
||||
|
||||
let start = get_time();
|
||||
let mut v = Vec::new();
|
||||
assert_eq!(thread_count, 2, "Peterson works when there are only 2 threads.");
|
||||
for id in 0..thread_count {
|
||||
v.push(thread_create(f as usize, id) as usize);
|
||||
}
|
||||
let mut time_cost = Vec::new();
|
||||
for tid in v.iter() {
|
||||
time_cost.push(waittid(*tid));
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
|
||||
0
|
||||
}
|
68
user/src/bin/adder_simple_spin.rs
Normal file
68
user/src/bin/adder_simple_spin.rs
Normal file
|
@ -0,0 +1,68 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid};
|
||||
|
||||
static mut A: usize = 0;
|
||||
static mut OCCUPIED: bool = false;
|
||||
const PER_THREAD_DEFAULT: usize = 10000;
|
||||
const THREAD_COUNT_DEFAULT: usize = 16;
|
||||
static mut PER_THREAD: usize = 0;
|
||||
|
||||
unsafe fn critical_section(t: &mut usize) {
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
*t = (*t) * (*t) % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
}
|
||||
|
||||
unsafe fn lock() {
|
||||
while vload!(&OCCUPIED) {}
|
||||
OCCUPIED = true;
|
||||
}
|
||||
|
||||
unsafe fn unlock() {
|
||||
OCCUPIED = false;
|
||||
}
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
lock();
|
||||
critical_section(&mut t);
|
||||
unlock();
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(argc: usize, argv: &[&str]) -> i32 {
|
||||
let mut thread_count = THREAD_COUNT_DEFAULT;
|
||||
let mut per_thread = PER_THREAD_DEFAULT;
|
||||
if argc >= 2 {
|
||||
thread_count = argv[1].parse().unwrap();
|
||||
if argc >= 3 {
|
||||
per_thread = argv[2].parse().unwrap();
|
||||
}
|
||||
}
|
||||
unsafe { PER_THREAD = per_thread; }
|
||||
let start = get_time();
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..thread_count {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
for tid in v.into_iter() {
|
||||
waittid(tid);
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
|
||||
0
|
||||
}
|
70
user/src/bin/adder_simple_yield.rs
Normal file
70
user/src/bin/adder_simple_yield.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid, yield_};
|
||||
|
||||
static mut A: usize = 0;
|
||||
static mut OCCUPIED: bool = false;
|
||||
const PER_THREAD_DEFAULT: usize = 10000;
|
||||
const THREAD_COUNT_DEFAULT: usize = 16;
|
||||
static mut PER_THREAD: usize = 0;
|
||||
|
||||
unsafe fn critical_section(t: &mut usize) {
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
*t = (*t) * (*t) % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
}
|
||||
|
||||
unsafe fn lock() {
|
||||
while OCCUPIED {
|
||||
yield_();
|
||||
}
|
||||
OCCUPIED = true;
|
||||
}
|
||||
|
||||
unsafe fn unlock() {
|
||||
OCCUPIED = false;
|
||||
}
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
lock();
|
||||
critical_section(&mut t);
|
||||
unlock();
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main(argc: usize, argv: &[&str]) -> i32 {
|
||||
let mut thread_count = THREAD_COUNT_DEFAULT;
|
||||
let mut per_thread = PER_THREAD_DEFAULT;
|
||||
if argc >= 2 {
|
||||
thread_count = argv[1].parse().unwrap();
|
||||
if argc >= 3 {
|
||||
per_thread = argv[2].parse().unwrap();
|
||||
}
|
||||
}
|
||||
unsafe { PER_THREAD = per_thread; }
|
||||
let start = get_time();
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..thread_count {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
for tid in v.into_iter() {
|
||||
waittid(tid);
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count);
|
||||
0
|
||||
}
|
72
user/src/bin/barrier_condvar.rs
Normal file
72
user/src/bin/barrier_condvar.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use user_lib::{thread_create, exit, waittid, mutex_create, mutex_lock, mutex_unlock, condvar_create, condvar_signal, condvar_wait};
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::UnsafeCell;
|
||||
use lazy_static::*;
|
||||
|
||||
const THREAD_NUM: usize = 3;
|
||||
|
||||
struct Barrier {
|
||||
mutex_id: usize,
|
||||
condvar_id: usize,
|
||||
count: UnsafeCell<usize>,
|
||||
}
|
||||
|
||||
impl Barrier {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mutex_id: mutex_create() as usize,
|
||||
condvar_id: condvar_create() as usize,
|
||||
count: UnsafeCell::new(0),
|
||||
}
|
||||
}
|
||||
pub fn block(&self) {
|
||||
mutex_lock(self.mutex_id);
|
||||
let count = self.count.get();
|
||||
// SAFETY: Here, the accesses of the count is in the
|
||||
// critical section protected by the mutex.
|
||||
unsafe { *count = *count + 1; }
|
||||
if unsafe { *count } == THREAD_NUM {
|
||||
condvar_signal(self.condvar_id);
|
||||
} else {
|
||||
condvar_wait(self.condvar_id, self.mutex_id);
|
||||
condvar_signal(self.condvar_id);
|
||||
}
|
||||
mutex_unlock(self.mutex_id);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for Barrier {}
|
||||
|
||||
lazy_static! {
|
||||
static ref BARRIER_AB: Barrier = Barrier::new();
|
||||
static ref BARRIER_BC: Barrier = Barrier::new();
|
||||
}
|
||||
|
||||
fn thread_fn() {
|
||||
for _ in 0..300 { print!("a"); }
|
||||
BARRIER_AB.block();
|
||||
for _ in 0..300 { print!("b"); }
|
||||
BARRIER_BC.block();
|
||||
for _ in 0..300 { print!("c"); }
|
||||
exit(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let mut v: Vec<isize> = Vec::new();
|
||||
for _ in 0..THREAD_NUM {
|
||||
v.push(thread_create(thread_fn as usize, 0));
|
||||
}
|
||||
for tid in v.into_iter() {
|
||||
waittid(tid as usize);
|
||||
}
|
||||
println!("\nOK!");
|
||||
0
|
||||
}
|
33
user/src/bin/barrier_fail.rs
Normal file
33
user/src/bin/barrier_fail.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use user_lib::{thread_create, exit, waittid};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
const THREAD_NUM: usize = 3;
|
||||
|
||||
fn thread_fn() {
|
||||
for ch in 'a'..='c' {
|
||||
for _ in 0..300 {
|
||||
print!("{}", ch);
|
||||
}
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let mut v: Vec<isize> = Vec::new();
|
||||
for _ in 0..THREAD_NUM {
|
||||
v.push(thread_create(thread_fn as usize, 0));
|
||||
}
|
||||
for tid in v.into_iter() {
|
||||
waittid(tid as usize);
|
||||
}
|
||||
println!("\nOK!");
|
||||
0
|
||||
}
|
|
@ -1,59 +1,59 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec;
|
||||
use user_lib::exit;
|
||||
use user_lib::{
|
||||
condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock,
|
||||
};
|
||||
use user_lib::{sleep, thread_create, waittid};
|
||||
|
||||
static mut A: usize = 0;
|
||||
|
||||
const CONDVAR_ID: usize = 0;
|
||||
const MUTEX_ID: usize = 0;
|
||||
|
||||
unsafe fn first() -> ! {
|
||||
sleep(10);
|
||||
println!("First work, Change A --> 1 and wakeup Second");
|
||||
mutex_lock(MUTEX_ID);
|
||||
A = 1;
|
||||
condvar_signal(CONDVAR_ID);
|
||||
mutex_unlock(MUTEX_ID);
|
||||
exit(0)
|
||||
}
|
||||
|
||||
unsafe fn second() -> ! {
|
||||
println!("Second want to continue,but need to wait A=1");
|
||||
mutex_lock(MUTEX_ID);
|
||||
while A == 0 {
|
||||
println!("Second: A is {}", A);
|
||||
condvar_wait(CONDVAR_ID, MUTEX_ID);
|
||||
}
|
||||
mutex_unlock(MUTEX_ID);
|
||||
println!("A is {}, Second can work now", A);
|
||||
exit(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
// create condvar & mutex
|
||||
assert_eq!(condvar_create() as usize, CONDVAR_ID);
|
||||
assert_eq!(mutex_blocking_create() as usize, MUTEX_ID);
|
||||
// create threads
|
||||
let threads = vec![
|
||||
thread_create(first as usize, 0),
|
||||
thread_create(second as usize, 0),
|
||||
];
|
||||
// wait for all threads to complete
|
||||
for thread in threads.iter() {
|
||||
waittid(*thread as usize);
|
||||
}
|
||||
println!("test_condvar passed!");
|
||||
0
|
||||
}
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec;
|
||||
use user_lib::exit;
|
||||
use user_lib::{
|
||||
condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock,
|
||||
};
|
||||
use user_lib::{sleep, thread_create, waittid};
|
||||
|
||||
static mut A: usize = 0;
|
||||
|
||||
const CONDVAR_ID: usize = 0;
|
||||
const MUTEX_ID: usize = 0;
|
||||
|
||||
unsafe fn first() -> ! {
|
||||
sleep(10);
|
||||
println!("First work, Change A --> 1 and wakeup Second");
|
||||
mutex_lock(MUTEX_ID);
|
||||
A = 1;
|
||||
condvar_signal(CONDVAR_ID);
|
||||
mutex_unlock(MUTEX_ID);
|
||||
exit(0)
|
||||
}
|
||||
|
||||
unsafe fn second() -> ! {
|
||||
println!("Second want to continue,but need to wait A=1");
|
||||
mutex_lock(MUTEX_ID);
|
||||
while A == 0 {
|
||||
println!("Second: A is {}", A);
|
||||
condvar_wait(CONDVAR_ID, MUTEX_ID);
|
||||
}
|
||||
println!("A is {}, Second can work now", A);
|
||||
mutex_unlock(MUTEX_ID);
|
||||
exit(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
// create condvar & mutex
|
||||
assert_eq!(condvar_create() as usize, CONDVAR_ID);
|
||||
assert_eq!(mutex_blocking_create() as usize, MUTEX_ID);
|
||||
// create threads
|
||||
let threads = vec![
|
||||
thread_create(first as usize, 0),
|
||||
thread_create(second as usize, 0),
|
||||
];
|
||||
// wait for all threads to complete
|
||||
for thread in threads.iter() {
|
||||
waittid(*thread as usize);
|
||||
}
|
||||
println!("test_condvar passed!");
|
||||
0
|
||||
}
|
64
user/src/bin/condsync_sem.rs
Normal file
64
user/src/bin/condsync_sem.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec;
|
||||
use user_lib::exit;
|
||||
use user_lib::{
|
||||
semaphore_create, semaphore_down, semaphore_up, mutex_blocking_create, mutex_lock, mutex_unlock,
|
||||
};
|
||||
use user_lib::{sleep, thread_create, waittid};
|
||||
|
||||
static mut A: usize = 0;
|
||||
|
||||
const SEM_ID: usize = 0;
|
||||
const MUTEX_ID: usize = 0;
|
||||
|
||||
unsafe fn first() -> ! {
|
||||
sleep(10);
|
||||
println!("First work, Change A --> 1 and wakeup Second");
|
||||
mutex_lock(MUTEX_ID);
|
||||
A = 1;
|
||||
semaphore_up(SEM_ID);
|
||||
mutex_unlock(MUTEX_ID);
|
||||
exit(0)
|
||||
}
|
||||
|
||||
unsafe fn second() -> ! {
|
||||
println!("Second want to continue,but need to wait A=1");
|
||||
loop {
|
||||
mutex_lock(MUTEX_ID);
|
||||
if A == 0 {
|
||||
println!("Second: A is {}", A);
|
||||
mutex_unlock(MUTEX_ID);
|
||||
semaphore_down(SEM_ID);
|
||||
} else {
|
||||
mutex_unlock(MUTEX_ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("A is {}, Second can work now", A);
|
||||
exit(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
// create semaphore & mutex
|
||||
assert_eq!(semaphore_create(0) as usize, SEM_ID);
|
||||
assert_eq!(mutex_blocking_create() as usize, MUTEX_ID);
|
||||
// create threads
|
||||
let threads = vec![
|
||||
thread_create(first as usize, 0),
|
||||
thread_create(second as usize, 0),
|
||||
];
|
||||
// wait for all threads to complete
|
||||
for thread in threads.iter() {
|
||||
waittid(*thread as usize);
|
||||
}
|
||||
println!("test_condvar passed!");
|
||||
0
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use user_lib::create_desktop;
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("gui");
|
||||
create_desktop();
|
||||
println!("exit pass.");
|
||||
loop{}
|
||||
0
|
||||
}
|
||||
|
67
user/src/bin/gui_rect.rs
Normal file
67
user/src/bin/gui_rect.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES};
|
||||
|
||||
use embedded_graphics::pixelcolor::Rgb888;
|
||||
use embedded_graphics::prelude::{DrawTarget, Drawable, Point, RgbColor, Size};
|
||||
use embedded_graphics::primitives::{Circle, Primitive, PrimitiveStyle, Rectangle,Triangle};
|
||||
|
||||
const INIT_X: i32 = 80;
|
||||
const INIT_Y: i32 = 400;
|
||||
const RECT_SIZE: u32 = 150;
|
||||
|
||||
pub struct DrawingBoard {
|
||||
disp: Display,
|
||||
latest_pos: Point,
|
||||
}
|
||||
|
||||
impl DrawingBoard {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
disp: Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)),
|
||||
latest_pos: Point::new(INIT_X, INIT_Y),
|
||||
}
|
||||
}
|
||||
fn paint(&mut self) {
|
||||
Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
|
||||
.into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 10))
|
||||
.draw(&mut self.disp)
|
||||
.ok();
|
||||
Circle::new(self.latest_pos + Point::new(-70, -300), 150)
|
||||
.into_styled(PrimitiveStyle::with_fill(Rgb888::BLUE))
|
||||
.draw(&mut self.disp)
|
||||
.ok();
|
||||
Triangle::new(self.latest_pos + Point::new(0, 150), self.latest_pos + Point::new(80, 200), self.latest_pos + Point::new(-120, 300))
|
||||
.into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 10))
|
||||
.draw(&mut self.disp)
|
||||
.ok();
|
||||
}
|
||||
fn unpaint(&mut self) {
|
||||
Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
|
||||
.into_styled(PrimitiveStyle::with_stroke(Rgb888::BLACK, 10))
|
||||
.draw(&mut self.disp)
|
||||
.ok();
|
||||
}
|
||||
pub fn move_rect(&mut self, dx: i32, dy: i32) {
|
||||
self.unpaint();
|
||||
self.latest_pos.x += dx;
|
||||
self.latest_pos.y += dy;
|
||||
self.paint();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let mut board = DrawingBoard::new();
|
||||
let _ = board.disp.clear(Rgb888::BLACK).unwrap();
|
||||
for i in 0..5 {
|
||||
board.latest_pos.x += (RECT_SIZE as i32 + 20);
|
||||
//board.latest_pos.y += i;
|
||||
board.paint();
|
||||
}
|
||||
0
|
||||
}
|
23
user/src/bin/gui_simple.rs
Normal file
23
user/src/bin/gui_simple.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate user_lib;
|
||||
|
||||
use user_lib::{VIRTGPU_XRES, VIRTGPU_YRES, Display};
|
||||
use embedded_graphics::prelude::Size;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES));
|
||||
disp.paint_on_framebuffer(|fb| {
|
||||
for y in 0..VIRTGPU_YRES as usize {
|
||||
for x in 0..VIRTGPU_XRES as usize {
|
||||
let idx = (y * VIRTGPU_XRES as usize + x) * 4;
|
||||
fb[idx] = x as u8;
|
||||
fb[idx + 1] = y as u8;
|
||||
fb[idx + 2] = (x + y) as u8;
|
||||
}
|
||||
}
|
||||
});
|
||||
0
|
||||
}
|
351
user/src/bin/gui_snake.rs
Normal file
351
user/src/bin/gui_snake.rs
Normal file
|
@ -0,0 +1,351 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use user_lib::console::getchar;
|
||||
use user_lib::{Display, key_pressed, sleep, VIRTGPU_XRES, VIRTGPU_YRES};
|
||||
|
||||
use embedded_graphics::pixelcolor::*;
|
||||
use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size};
|
||||
use embedded_graphics::primitives::Primitive;
|
||||
use embedded_graphics::primitives::{PrimitiveStyle, Rectangle};
|
||||
use embedded_graphics::Pixel;
|
||||
use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions};
|
||||
use oorandom; //random generator
|
||||
|
||||
struct Snake<T: PixelColor, const MAX_SIZE: usize> {
|
||||
parts: [Pixel<T>; MAX_SIZE],
|
||||
len: usize,
|
||||
direction: Direction,
|
||||
size_x: u32,
|
||||
size_y: u32,
|
||||
}
|
||||
|
||||
struct SnakeIntoIterator<'a, T: PixelColor, const MAX_SIZE: usize> {
|
||||
snake: &'a Snake<T, MAX_SIZE>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<'a, T: PixelColor, const MAX_SIZE: usize> IntoIterator for &'a Snake<T, MAX_SIZE> {
|
||||
type Item = Pixel<T>;
|
||||
type IntoIter = SnakeIntoIterator<'a, T, MAX_SIZE>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
SnakeIntoIterator {
|
||||
snake: self,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: PixelColor, const MAX_SIZE: usize> Iterator for SnakeIntoIterator<'a, T, MAX_SIZE> {
|
||||
type Item = Pixel<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let cur = self.snake.parts[self.index];
|
||||
if self.index < self.snake.len {
|
||||
self.index += 1;
|
||||
return Some(cur);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PixelColor, const MAX_SIZE: usize> Snake<T, MAX_SIZE> {
|
||||
fn new(color: T, size_x: u32, size_y: u32) -> Snake<T, MAX_SIZE> {
|
||||
Snake {
|
||||
parts: [Pixel::<T>(Point { x: 0, y: 0 }, color); MAX_SIZE],
|
||||
len: 1,
|
||||
direction: Direction::None,
|
||||
size_x,
|
||||
size_y,
|
||||
}
|
||||
}
|
||||
fn set_direction(&mut self, direction: Direction) {
|
||||
self.direction = direction;
|
||||
}
|
||||
fn contains(&self, this: Point) -> bool {
|
||||
for part in self.into_iter() {
|
||||
if part.0 == this {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
false
|
||||
}
|
||||
fn grow(&mut self) {
|
||||
if self.len < MAX_SIZE - 1 {
|
||||
self.len += 1;
|
||||
}
|
||||
}
|
||||
fn make_step(&mut self) {
|
||||
let mut i = self.len;
|
||||
while i > 0 {
|
||||
self.parts[i] = self.parts[i - 1];
|
||||
i -= 1;
|
||||
}
|
||||
match self.direction {
|
||||
Direction::Left => {
|
||||
if self.parts[0].0.x == 0 {
|
||||
self.parts[0].0.x = (self.size_x - 1) as i32;
|
||||
} else {
|
||||
self.parts[0].0.x -= 1;
|
||||
}
|
||||
}
|
||||
Direction::Right => {
|
||||
if self.parts[0].0.x == (self.size_x - 1) as i32 {
|
||||
self.parts[0].0.x = 0;
|
||||
} else {
|
||||
self.parts[0].0.x += 1;
|
||||
}
|
||||
}
|
||||
Direction::Up => {
|
||||
if self.parts[0].0.y == 0 {
|
||||
self.parts[0].0.y = (self.size_y - 1) as i32;
|
||||
} else {
|
||||
self.parts[0].0.y -= 1;
|
||||
}
|
||||
}
|
||||
Direction::Down => {
|
||||
if self.parts[0].0.y == (self.size_y - 1) as i32 {
|
||||
self.parts[0].0.y = 0;
|
||||
} else {
|
||||
self.parts[0].0.y += 1;
|
||||
}
|
||||
}
|
||||
Direction::None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Food<T: PixelColor> {
|
||||
size_x: u32,
|
||||
size_y: u32,
|
||||
place: Pixel<T>,
|
||||
rng: oorandom::Rand32,
|
||||
}
|
||||
|
||||
impl<T: PixelColor> Food<T> {
|
||||
pub fn new(color: T, size_x: u32, size_y: u32) -> Self {
|
||||
let seed = 4;
|
||||
let rng = oorandom::Rand32::new(seed);
|
||||
Food {
|
||||
size_x,
|
||||
size_y,
|
||||
place: Pixel(Point { x: 0, y: 0 }, color),
|
||||
rng,
|
||||
}
|
||||
}
|
||||
fn replace<'a, const MAX_SIZE: usize>(&mut self, iter_source: &Snake<T, MAX_SIZE>) {
|
||||
let mut p: Point;
|
||||
'outer: loop {
|
||||
let random_number = self.rng.rand_u32();
|
||||
let blocked_positions = iter_source.into_iter();
|
||||
p = Point {
|
||||
x: ((random_number >> 24) as u16 % self.size_x as u16).into(),
|
||||
y: ((random_number >> 16) as u16 % self.size_y as u16).into(),
|
||||
};
|
||||
for blocked_position in blocked_positions {
|
||||
if p == blocked_position.0 {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
self.place = Pixel::<T> {
|
||||
0: p,
|
||||
1: self.place.1,
|
||||
}
|
||||
}
|
||||
fn get_pixel(&self) -> Pixel<T> {
|
||||
self.place
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone, Copy)]
|
||||
pub enum Direction {
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
None,
|
||||
}
|
||||
|
||||
pub struct SnakeGame<const MAX_SNAKE_SIZE: usize, T: PixelColor> {
|
||||
snake: Snake<T, MAX_SNAKE_SIZE>,
|
||||
food: Food<T>,
|
||||
food_age: u32,
|
||||
food_lifetime: u32,
|
||||
size_x: u32,
|
||||
size_y: u32,
|
||||
scale_x: u32,
|
||||
scale_y: u32,
|
||||
}
|
||||
|
||||
impl<const MAX_SIZE: usize, T: PixelColor> SnakeGame<MAX_SIZE, T> {
|
||||
pub fn new(
|
||||
size_x: u32,
|
||||
size_y: u32,
|
||||
scale_x: u32,
|
||||
scale_y: u32,
|
||||
snake_color: T,
|
||||
food_color: T,
|
||||
food_lifetime: u32,
|
||||
) -> Self {
|
||||
let snake = Snake::<T, MAX_SIZE>::new(snake_color, size_x / scale_x, size_y / scale_y);
|
||||
let mut food = Food::<T>::new(food_color, size_x / scale_x, size_y / scale_y);
|
||||
food.replace(&snake);
|
||||
SnakeGame {
|
||||
snake,
|
||||
food,
|
||||
food_age: 0,
|
||||
food_lifetime,
|
||||
size_x,
|
||||
size_y,
|
||||
scale_x,
|
||||
scale_y,
|
||||
}
|
||||
}
|
||||
pub fn set_direction(&mut self, direction: Direction) {
|
||||
self.snake.set_direction(direction);
|
||||
}
|
||||
pub fn draw<D>(&mut self, target: &mut D) -> ()
|
||||
where
|
||||
D: DrawTarget<Color = T>,
|
||||
{
|
||||
self.snake.make_step();
|
||||
let hit = self.snake.contains(self.food.get_pixel().0);
|
||||
if hit {
|
||||
self.snake.grow();
|
||||
}
|
||||
self.food_age += 1;
|
||||
if self.food_age >= self.food_lifetime || hit {
|
||||
self.food.replace(&self.snake);
|
||||
self.food_age = 0;
|
||||
}
|
||||
|
||||
let mut scaled_display = ScaledDisplay::<D> {
|
||||
real_display: target,
|
||||
size_x: self.size_x / self.scale_x,
|
||||
size_y: self.size_y / self.scale_y,
|
||||
scale_x: self.scale_x,
|
||||
scale_y: self.scale_y,
|
||||
};
|
||||
|
||||
for part in self.snake.into_iter() {
|
||||
_ = part.draw(&mut scaled_display);
|
||||
}
|
||||
_ = self.food.get_pixel().draw(&mut scaled_display);
|
||||
}
|
||||
}
|
||||
|
||||
/// A dummy DrawTarget implementation that can magnify each pixel so the user code does not need to adapt for scaling things
|
||||
struct ScaledDisplay<'a, T: DrawTarget> {
|
||||
real_display: &'a mut T,
|
||||
size_x: u32,
|
||||
size_y: u32,
|
||||
scale_x: u32,
|
||||
scale_y: u32,
|
||||
}
|
||||
|
||||
impl<'a, T: DrawTarget> DrawTarget for ScaledDisplay<'a, T> {
|
||||
type Color = T::Color;
|
||||
type Error = T::Error;
|
||||
|
||||
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||
where
|
||||
I: IntoIterator<Item = Pixel<Self::Color>>,
|
||||
{
|
||||
for pixel in pixels {
|
||||
let style = PrimitiveStyle::with_fill(pixel.1);
|
||||
Rectangle::new(
|
||||
Point::new(
|
||||
pixel.0.x * self.scale_x as i32,
|
||||
pixel.0.y * self.scale_y as i32,
|
||||
),
|
||||
Size::new(self.scale_x as u32, self.scale_y as u32),
|
||||
)
|
||||
.into_styled(style)
|
||||
.draw(self.real_display)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: DrawTarget> OriginDimensions for ScaledDisplay<'a, T> {
|
||||
fn size(&self) -> Size {
|
||||
Size::new(self.size_x as u32, self.size_y as u32)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::Snake;
|
||||
use embedded_graphics::pixelcolor::*;
|
||||
use embedded_graphics::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn snake_basic() {
|
||||
let mut snake = Snake::<Rgb888, 20>::new(Rgb888::RED, 8, 8);
|
||||
snake.set_direction(crate::Direction::Right);
|
||||
assert_eq!(
|
||||
Pixel::<Rgb888>(Point { x: 0, y: 0 }, Rgb888::RED),
|
||||
snake.into_iter().next().unwrap()
|
||||
);
|
||||
snake.make_step();
|
||||
assert_eq!(
|
||||
Pixel::<Rgb888>(Point { x: 1, y: 0 }, Rgb888::RED),
|
||||
snake.into_iter().nth(0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Pixel::<Rgb888>(Point { x: 0, y: 0 }, Rgb888::RED),
|
||||
snake.into_iter().nth(1).unwrap()
|
||||
);
|
||||
snake.set_direction(crate::Direction::Down);
|
||||
snake.make_step();
|
||||
assert_eq!(
|
||||
Pixel::<Rgb888>(Point { x: 1, y: 1 }, Rgb888::RED),
|
||||
snake.into_iter().nth(0).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Pixel::<Rgb888>(Point { x: 1, y: 0 }, Rgb888::RED),
|
||||
snake.into_iter().nth(1).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
Pixel::<Rgb888>(Point { x: 0, y: 0 }, Rgb888::RED),
|
||||
snake.into_iter().nth(2).unwrap()
|
||||
);
|
||||
assert_eq!(true, snake.contains(Point { x: 0, y: 0 }));
|
||||
assert_eq!(true, snake.contains(Point { x: 1, y: 0 }));
|
||||
assert_eq!(true, snake.contains(Point { x: 1, y: 1 }));
|
||||
}
|
||||
}
|
||||
|
||||
const LF: u8 = 0x0au8;
|
||||
const CR: u8 = 0x0du8;
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES));
|
||||
let mut game = SnakeGame::<20, Rgb888>::new(1280, 800, 20, 20, Rgb888::RED, Rgb888::YELLOW, 50);
|
||||
let _ = disp.clear(Rgb888::BLACK).unwrap();
|
||||
loop {
|
||||
if key_pressed() {
|
||||
let c = getchar();
|
||||
match c {
|
||||
LF => break,
|
||||
CR => break,
|
||||
b'w' => game.set_direction(Direction::Up),
|
||||
b's' => game.set_direction(Direction::Down),
|
||||
b'a' => game.set_direction(Direction::Left),
|
||||
b'd' => game.set_direction(Direction::Right),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
let _ = disp.clear(Rgb888::BLACK).unwrap();
|
||||
game.draw(&mut disp);
|
||||
sleep(10);
|
||||
}
|
||||
0
|
||||
}
|
125
user/src/bin/gui_uart.rs
Normal file
125
user/src/bin/gui_uart.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use user_lib::console::getchar;
|
||||
use user_lib::{framebuffer, framebuffer_flush};
|
||||
|
||||
use embedded_graphics::pixelcolor::Rgb888;
|
||||
use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size};
|
||||
use embedded_graphics::primitives::Primitive;
|
||||
use embedded_graphics::primitives::{PrimitiveStyle, Rectangle};
|
||||
use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions};
|
||||
|
||||
pub const VIRTGPU_XRES: usize = 1280;
|
||||
pub const VIRTGPU_YRES: usize = 800;
|
||||
pub const VIRTGPU_LEN: usize = VIRTGPU_XRES * VIRTGPU_YRES * 4;
|
||||
|
||||
const INIT_X: i32 = 640;
|
||||
const INIT_Y: i32 = 400;
|
||||
const RECT_SIZE: u32 = 40;
|
||||
|
||||
pub struct Display {
|
||||
pub size: Size,
|
||||
pub point: Point,
|
||||
//pub fb: Arc<&'static mut [u8]>,
|
||||
pub fb: &'static mut [u8],
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn new(size: Size, point: Point) -> Self {
|
||||
let fb_ptr = framebuffer() as *mut u8;
|
||||
println!(
|
||||
"Hello world from user mode program! 0x{:X} , len {}",
|
||||
fb_ptr as usize, VIRTGPU_LEN
|
||||
);
|
||||
let fb =
|
||||
unsafe { core::slice::from_raw_parts_mut(fb_ptr as *mut u8, VIRTGPU_LEN as usize) };
|
||||
Self { size, point, fb }
|
||||
}
|
||||
}
|
||||
|
||||
impl OriginDimensions for Display {
|
||||
fn size(&self) -> Size {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl DrawTarget for Display {
|
||||
type Color = Rgb888;
|
||||
|
||||
type Error = core::convert::Infallible;
|
||||
|
||||
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||
where
|
||||
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
|
||||
{
|
||||
pixels.into_iter().for_each(|px| {
|
||||
let idx = ((self.point.y + px.0.y) * VIRTGPU_XRES as i32 + self.point.x + px.0.x)
|
||||
as usize
|
||||
* 4;
|
||||
if idx + 2 >= self.fb.len() {
|
||||
return;
|
||||
}
|
||||
self.fb[idx] = px.1.b();
|
||||
self.fb[idx + 1] = px.1.g();
|
||||
self.fb[idx + 2] = px.1.r();
|
||||
});
|
||||
framebuffer_flush();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawingBoard {
|
||||
disp: Display,
|
||||
latest_pos: Point,
|
||||
}
|
||||
|
||||
impl DrawingBoard {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
disp: Display::new(Size::new(1280, 800), Point::new(0, 0)),
|
||||
latest_pos: Point::new(INIT_X, INIT_Y),
|
||||
}
|
||||
}
|
||||
fn paint(&mut self) {
|
||||
Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
|
||||
.into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, 1))
|
||||
.draw(&mut self.disp)
|
||||
.ok();
|
||||
}
|
||||
fn unpaint(&mut self) {
|
||||
Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE))
|
||||
.into_styled(PrimitiveStyle::with_stroke(Rgb888::BLACK, 1))
|
||||
.draw(&mut self.disp)
|
||||
.ok();
|
||||
}
|
||||
pub fn move_rect(&mut self, dx: i32, dy: i32) {
|
||||
self.unpaint();
|
||||
self.latest_pos.x += dx;
|
||||
self.latest_pos.y += dy;
|
||||
self.paint();
|
||||
}
|
||||
}
|
||||
|
||||
const LF: u8 = 0x0au8;
|
||||
const CR: u8 = 0x0du8;
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
// let fb_ptr = framebuffer() as *mut u8;
|
||||
let mut board = DrawingBoard::new();
|
||||
let _ = board.disp.clear(Rgb888::BLACK).unwrap();
|
||||
for i in 0..20 {
|
||||
let c=getchar();
|
||||
if c == LF || c == CR {
|
||||
break;
|
||||
}
|
||||
board.latest_pos.x += i;
|
||||
board.latest_pos.y += i;
|
||||
board.paint();
|
||||
}
|
||||
0
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::{fmt::format, string::String, vec::Vec};
|
||||
use alloc::{fmt::format, vec::Vec};
|
||||
use user_lib::{close, get_time, gettid, open, write, OpenFlags};
|
||||
use user_lib::{exit, thread_create, waittid};
|
||||
|
||||
|
|
25
user/src/bin/inputdev_event.rs
Normal file
25
user/src/bin/inputdev_event.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use user_lib::{event_get, DecodeType, Key, KeyType};
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("Input device event test");
|
||||
loop {
|
||||
if let Some(event) = event_get() {
|
||||
if let Some(decoder_type) = event.decode() {
|
||||
println!("{:?}", decoder_type);
|
||||
if let DecodeType::Key(key, keytype) = decoder_type {
|
||||
if key == Key::Enter && keytype == KeyType::Press {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
|
@ -14,7 +14,7 @@ use user_lib::{thread_create, waittid};
|
|||
|
||||
const SEM_MUTEX: usize = 0;
|
||||
const SEM_EMPTY: usize = 1;
|
||||
const SEM_EXISTED: usize = 2;
|
||||
const SEM_AVAIL: usize = 2;
|
||||
const BUFFER_SIZE: usize = 8;
|
||||
static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
static mut FRONT: usize = 0;
|
||||
|
@ -27,20 +27,20 @@ unsafe fn producer(id: *const usize) -> ! {
|
|||
for _ in 0..NUMBER_PER_PRODUCER {
|
||||
semaphore_down(SEM_EMPTY);
|
||||
semaphore_down(SEM_MUTEX);
|
||||
BUFFER[FRONT] = id;
|
||||
FRONT = (FRONT + 1) % BUFFER_SIZE;
|
||||
BUFFER[TAIL] = id;
|
||||
TAIL = (TAIL + 1) % BUFFER_SIZE;
|
||||
semaphore_up(SEM_MUTEX);
|
||||
semaphore_up(SEM_EXISTED);
|
||||
semaphore_up(SEM_AVAIL);
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
|
||||
unsafe fn consumer() -> ! {
|
||||
for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER {
|
||||
semaphore_down(SEM_EXISTED);
|
||||
semaphore_down(SEM_AVAIL);
|
||||
semaphore_down(SEM_MUTEX);
|
||||
print!("{} ", BUFFER[TAIL]);
|
||||
TAIL = (TAIL + 1) % BUFFER_SIZE;
|
||||
print!("{} ", BUFFER[FRONT]);
|
||||
FRONT = (FRONT + 1) % BUFFER_SIZE;
|
||||
semaphore_up(SEM_MUTEX);
|
||||
semaphore_up(SEM_EMPTY);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ pub fn main() -> i32 {
|
|||
// create semaphores
|
||||
assert_eq!(semaphore_create(1) as usize, SEM_MUTEX);
|
||||
assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY);
|
||||
assert_eq!(semaphore_create(0) as usize, SEM_EXISTED);
|
||||
assert_eq!(semaphore_create(0) as usize, SEM_AVAIL);
|
||||
// create threads
|
||||
let ids: Vec<_> = (0..PRODUCER_COUNT).collect();
|
||||
let mut threads = Vec::new();
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(asm)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
@ -11,7 +10,7 @@ extern crate core;
|
|||
use alloc::vec::Vec;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use user_lib::{exit, sleep, thread_create, waittid};
|
||||
const N: usize = 3;
|
||||
const N: usize = 1000;
|
||||
|
||||
static mut TURN: usize = 0;
|
||||
static mut FLAG: [bool; 2] = [false; 2];
|
||||
|
@ -30,27 +29,30 @@ fn critical_test_exit() {
|
|||
}
|
||||
|
||||
fn peterson_enter_critical(id: usize, peer_id: usize) {
|
||||
println!("Thread[{}] try enter", id);
|
||||
// println!("Thread[{}] try enter", id);
|
||||
vstore!(&FLAG[id], true);
|
||||
vstore!(&TURN, peer_id);
|
||||
memory_fence!();
|
||||
while vload!(&FLAG[peer_id]) && vload!(&TURN) == peer_id {
|
||||
println!("Thread[{}] enter fail", id);
|
||||
// println!("Thread[{}] enter fail", id);
|
||||
sleep(1);
|
||||
println!("Thread[{}] retry enter", id);
|
||||
// println!("Thread[{}] retry enter", id);
|
||||
}
|
||||
println!("Thread[{}] enter", id);
|
||||
// println!("Thread[{}] enter", id);
|
||||
}
|
||||
|
||||
fn peterson_exit_critical(id: usize) {
|
||||
vstore!(&FLAG[id], false);
|
||||
println!("Thread[{}] exit", id);
|
||||
// println!("Thread[{}] exit", id);
|
||||
}
|
||||
|
||||
pub fn thread_fn(id: usize) -> ! {
|
||||
println!("Thread[{}] init.", id);
|
||||
// println!("Thread[{}] init.", id);
|
||||
let peer_id: usize = id ^ 1;
|
||||
for _ in 0..N {
|
||||
for iter in 0..N {
|
||||
if iter % 10 == 0 {
|
||||
println!("[{}] it={}", id, iter);
|
||||
}
|
||||
peterson_enter_critical(id, peer_id);
|
||||
critical_test_enter();
|
||||
for _ in 0..3 {
|
||||
|
@ -75,4 +77,4 @@ pub fn main() -> i32 {
|
|||
}
|
||||
println!("main thread exited.");
|
||||
0
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid};
|
||||
|
||||
static mut A: usize = 0;
|
||||
const PER_THREAD: usize = 1000;
|
||||
const THREAD_COUNT: usize = 16;
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
t = t * t % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let start = get_time();
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..THREAD_COUNT {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
let mut time_cost = Vec::new();
|
||||
for tid in v.iter() {
|
||||
time_cost.push(waittid(*tid));
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
|
||||
0
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use user_lib::{exit, get_time, thread_create, waittid, yield_};
|
||||
|
||||
static mut A: usize = 0;
|
||||
static OCCUPIED: AtomicBool = AtomicBool::new(false);
|
||||
const PER_THREAD: usize = 1000;
|
||||
const THREAD_COUNT: usize = 16;
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
while OCCUPIED
|
||||
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
yield_();
|
||||
}
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
t = t * t % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
OCCUPIED.store(false, Ordering::Relaxed);
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let start = get_time();
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..THREAD_COUNT {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
let mut time_cost = Vec::new();
|
||||
for tid in v.iter() {
|
||||
time_cost.push(waittid(*tid));
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
|
||||
0
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid, yield_};
|
||||
|
||||
static mut A: usize = 0;
|
||||
static mut OCCUPIED: bool = false;
|
||||
const PER_THREAD: usize = 1000;
|
||||
const THREAD_COUNT: usize = 16;
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
while OCCUPIED {
|
||||
yield_();
|
||||
}
|
||||
OCCUPIED = true;
|
||||
// enter critical section
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
t = t * t % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
// exit critical section
|
||||
OCCUPIED = false;
|
||||
}
|
||||
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let start = get_time();
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..THREAD_COUNT {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
let mut time_cost = Vec::new();
|
||||
for tid in v.iter() {
|
||||
time_cost.push(waittid(*tid));
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
|
||||
0
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid};
|
||||
use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock};
|
||||
|
||||
static mut A: usize = 0;
|
||||
const PER_THREAD: usize = 1000;
|
||||
const THREAD_COUNT: usize = 16;
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
mutex_lock(0);
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
t = t * t % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
mutex_unlock(0);
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let start = get_time();
|
||||
assert_eq!(mutex_blocking_create(), 0);
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..THREAD_COUNT {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
let mut time_cost = Vec::new();
|
||||
for tid in v.iter() {
|
||||
time_cost.push(waittid(*tid));
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
|
||||
0
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
extern crate alloc;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use user_lib::{exit, get_time, thread_create, waittid};
|
||||
use user_lib::{mutex_create, mutex_lock, mutex_unlock};
|
||||
|
||||
static mut A: usize = 0;
|
||||
const PER_THREAD: usize = 1000;
|
||||
const THREAD_COUNT: usize = 16;
|
||||
|
||||
unsafe fn f() -> ! {
|
||||
let mut t = 2usize;
|
||||
for _ in 0..PER_THREAD {
|
||||
mutex_lock(0);
|
||||
let a = &mut A as *mut usize;
|
||||
let cur = a.read_volatile();
|
||||
for _ in 0..500 {
|
||||
t = t * t % 10007;
|
||||
}
|
||||
a.write_volatile(cur + 1);
|
||||
mutex_unlock(0);
|
||||
}
|
||||
exit(t as i32)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
let start = get_time();
|
||||
assert_eq!(mutex_create(), 0);
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..THREAD_COUNT {
|
||||
v.push(thread_create(f as usize, 0) as usize);
|
||||
}
|
||||
let mut time_cost = Vec::new();
|
||||
for tid in v.iter() {
|
||||
time_cost.push(waittid(*tid));
|
||||
}
|
||||
println!("time cost is {}ms", get_time() - start);
|
||||
assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT);
|
||||
0
|
||||
}
|
16
user/src/bin/random_num.rs
Normal file
16
user/src/bin/random_num.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
use oorandom;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("random num program!");
|
||||
let seed = 4;
|
||||
let mut rng = oorandom::Rand32::new(seed);
|
||||
println!("OORandom: Random number 32bit: {}", rng.rand_i32());
|
||||
println!("OORandom: Random number range: {}", rng.rand_range(1..100));
|
||||
0
|
||||
}
|
|
@ -8,7 +8,7 @@ use user_lib::{exec, fork, wait};
|
|||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
for i in 0..50 {
|
||||
for i in 0..5 {
|
||||
if fork() == 0 {
|
||||
exec("pipe_large_test\0", &[core::ptr::null::<u8>()]);
|
||||
} else {
|
||||
|
|
|
@ -4,9 +4,12 @@
|
|||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
fn f(d: usize) {
|
||||
println!("d = {}", d);
|
||||
f(d + 1);
|
||||
#[allow(unconditional_recursion)]
|
||||
fn f(depth: usize) {
|
||||
if depth % 10 == 0 {
|
||||
println!("depth = {}", depth);
|
||||
}
|
||||
f(depth + 1);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
|
350
user/src/bin/stackful_coroutine.rs
Normal file
350
user/src/bin/stackful_coroutine.rs
Normal file
|
@ -0,0 +1,350 @@
|
|||
// we porting below codes to Rcore Tutorial v3
|
||||
// https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/
|
||||
// https://github.com/cfsamson/example-greenthreads
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(naked_functions)]
|
||||
//#![feature(asm)]
|
||||
|
||||
extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
use core::arch::asm;
|
||||
|
||||
//#[macro_use]
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use user_lib::exit;
|
||||
|
||||
// In our simple example we set most constraints here.
|
||||
const DEFAULT_STACK_SIZE: usize = 4096; //128 got SEGFAULT, 256(1024, 4096) got right results.
|
||||
const MAX_TASKS: usize = 5;
|
||||
static mut RUNTIME: usize = 0;
|
||||
|
||||
pub struct Runtime {
|
||||
tasks: Vec<Task>,
|
||||
current: usize,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
enum State {
|
||||
Available,
|
||||
Running,
|
||||
Ready,
|
||||
}
|
||||
|
||||
struct Task {
|
||||
id: usize,
|
||||
stack: Vec<u8>,
|
||||
ctx: TaskContext,
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C)] // not strictly needed but Rust ABI is not guaranteed to be stable
|
||||
pub struct TaskContext {
|
||||
// 15 u64
|
||||
x1: u64, //ra: return addres
|
||||
x2: u64, //sp
|
||||
x8: u64, //s0,fp
|
||||
x9: u64, //s1
|
||||
x18: u64, //x18-27: s2-11
|
||||
x19: u64,
|
||||
x20: u64,
|
||||
x21: u64,
|
||||
x22: u64,
|
||||
x23: u64,
|
||||
x24: u64,
|
||||
x25: u64,
|
||||
x26: u64,
|
||||
x27: u64,
|
||||
nx1: u64, //new return addres
|
||||
}
|
||||
|
||||
impl Task {
|
||||
fn new(id: usize) -> Self {
|
||||
// We initialize each task here and allocate the stack. This is not neccesary,
|
||||
// we can allocate memory for it later, but it keeps complexity down and lets us focus on more interesting parts
|
||||
// to do it here. The important part is that once allocated it MUST NOT move in memory.
|
||||
Task {
|
||||
id:id,
|
||||
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||
ctx: TaskContext::default(),
|
||||
state: State::Available,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Runtime {
|
||||
pub fn new() -> Self {
|
||||
// This will be our base task, which will be initialized in the `running` state
|
||||
let base_task = Task {
|
||||
id: 0,
|
||||
stack: vec![0_u8; DEFAULT_STACK_SIZE],
|
||||
ctx: TaskContext::default(),
|
||||
state: State::Running,
|
||||
};
|
||||
|
||||
// We initialize the rest of our tasks.
|
||||
let mut tasks = vec![base_task];
|
||||
let mut available_tasks: Vec<Task> = (1..MAX_TASKS).map(|i| Task::new(i)).collect();
|
||||
tasks.append(&mut available_tasks);
|
||||
|
||||
Runtime { tasks, current: 0 }
|
||||
}
|
||||
|
||||
/// This is cheating a bit, but we need a pointer to our Runtime stored so we can call yield on it even if
|
||||
/// we don't have a reference to it.
|
||||
pub fn init(&self) {
|
||||
unsafe {
|
||||
let r_ptr: *const Runtime = self;
|
||||
RUNTIME = r_ptr as usize;
|
||||
}
|
||||
}
|
||||
|
||||
/// This is where we start running our runtime. If it is our base task, we call yield until
|
||||
/// it returns false (which means that there are no tasks scheduled) and we are done.
|
||||
pub fn run(&mut self) {
|
||||
while self.t_yield() {}
|
||||
println!("All tasks finished!");
|
||||
}
|
||||
|
||||
/// This is our return function. The only place we use this is in our `guard` function.
|
||||
/// If the current task is not our base task we set its state to Available. It means
|
||||
/// we're finished with it. Then we yield which will schedule a new task to be run.
|
||||
fn t_return(&mut self) {
|
||||
if self.current != 0 {
|
||||
self.tasks[self.current].state = State::Available;
|
||||
self.t_yield();
|
||||
}
|
||||
}
|
||||
|
||||
/// This is the heart of our runtime. Here we go through all tasks and see if anyone is in the `Ready` state.
|
||||
/// If no task is `Ready` we're all done. This is an extremely simple scheduler using only a round-robin algorithm.
|
||||
///
|
||||
/// If we find a task that's ready to be run we change the state of the current task from `Running` to `Ready`.
|
||||
/// Then we call switch which will save the current context (the old context) and load the new context
|
||||
/// into the CPU which then resumes based on the context it was just passed.
|
||||
///
|
||||
/// NOITCE: if we comment below `#[inline(never)]`, we can not get the corrent running result
|
||||
#[inline(never)]
|
||||
fn t_yield(&mut self) -> bool {
|
||||
let mut pos = self.current;
|
||||
while self.tasks[pos].state != State::Ready {
|
||||
pos += 1;
|
||||
if pos == self.tasks.len() {
|
||||
pos = 0;
|
||||
}
|
||||
if pos == self.current {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if self.tasks[self.current].state != State::Available {
|
||||
self.tasks[self.current].state = State::Ready;
|
||||
}
|
||||
|
||||
self.tasks[pos].state = State::Running;
|
||||
let old_pos = self.current;
|
||||
self.current = pos;
|
||||
|
||||
unsafe {
|
||||
switch(&mut self.tasks[old_pos].ctx, &self.tasks[pos].ctx);
|
||||
}
|
||||
|
||||
// NOTE: this might look strange and it is. Normally we would just mark this as `unreachable!()` but our compiler
|
||||
// is too smart for it's own good so it optimized our code away on release builds. Curiously this happens on windows
|
||||
// and not on linux. This is a common problem in tests so Rust has a `black_box` function in the `test` crate that
|
||||
// will "pretend" to use a value we give it to prevent the compiler from eliminating code. I'll just do this instead,
|
||||
// this code will never be run anyways and if it did it would always be `true`.
|
||||
self.tasks.len() > 0
|
||||
}
|
||||
|
||||
/// While `yield` is the logically interesting function I think this the technically most interesting.
|
||||
///
|
||||
/// When we spawn a new task we first check if there are any available tasks (tasks in `Parked` state).
|
||||
/// If we run out of tasks we panic in this scenario but there are several (better) ways to handle that.
|
||||
/// We keep things simple for now.
|
||||
///
|
||||
/// When we find an available task we get the stack length and a pointer to our u8 bytearray.
|
||||
///
|
||||
/// The next part we have to use some unsafe functions. First we write an address to our `guard` function
|
||||
/// that will be called if the function we provide returns. Then we set the address to the function we
|
||||
/// pass inn.
|
||||
///
|
||||
/// Third, we set the value of `sp` which is the stack pointer to the address of our provided function so we start
|
||||
/// executing that first when we are scheuled to run.
|
||||
///
|
||||
/// Lastly we set the state as `Ready` which means we have work to do and is ready to do it.
|
||||
pub fn spawn(&mut self, f: fn()) {
|
||||
let available = self
|
||||
.tasks
|
||||
.iter_mut()
|
||||
.find(|t| t.state == State::Available)
|
||||
.expect("no available task.");
|
||||
|
||||
println!("RUNTIME: spawning task {}\n", available.id);
|
||||
let size = available.stack.len();
|
||||
unsafe {
|
||||
let s_ptr = available.stack.as_mut_ptr().offset(size as isize);
|
||||
|
||||
// make sure our stack itself is 8 byte aligned - it will always
|
||||
// offset to a lower memory address. Since we know we're at the "high"
|
||||
// memory address of our allocated space, we know that offsetting to
|
||||
// a lower one will be a valid address (given that we actually allocated)
|
||||
// enough space to actually get an aligned pointer in the first place).
|
||||
let s_ptr = (s_ptr as usize & !7) as *mut u8;
|
||||
|
||||
available.ctx.x1 = guard as u64; //ctx.x1 is old return address
|
||||
available.ctx.nx1 = f as u64; //ctx.nx2 is new return address
|
||||
available.ctx.x2 = s_ptr.offset(-32) as u64; //cxt.x2 is sp
|
||||
}
|
||||
available.state = State::Ready;
|
||||
}
|
||||
}
|
||||
|
||||
/// This is our guard function that we place on top of the stack. All this function does is set the
|
||||
/// state of our current task and then `yield` which will then schedule a new task to be run.
|
||||
fn guard() {
|
||||
unsafe {
|
||||
let rt_ptr = RUNTIME as *mut Runtime;
|
||||
(*rt_ptr).t_return();
|
||||
};
|
||||
}
|
||||
|
||||
/// We know that Runtime is alive the length of the program and that we only access from one core
|
||||
/// (so no datarace). We yield execution of the current task by dereferencing a pointer to our
|
||||
/// Runtime and then calling `t_yield`
|
||||
pub fn yield_task() {
|
||||
unsafe {
|
||||
let rt_ptr = RUNTIME as *mut Runtime;
|
||||
(*rt_ptr).t_yield();
|
||||
};
|
||||
}
|
||||
|
||||
/// So here is our inline Assembly. As you remember from our first example this is just a bit more elaborate where we first
|
||||
/// read out the values of all the registers we need and then sets all the register values to the register values we
|
||||
/// saved when we suspended exceution on the "new" task.
|
||||
///
|
||||
/// This is essentially all we need to do to save and resume execution.
|
||||
///
|
||||
/// Some details about inline assembly.
|
||||
///
|
||||
/// The assembly commands in the string literal is called the assemblt template. It is preceeded by
|
||||
/// zero or up to four segments indicated by ":":
|
||||
///
|
||||
/// - First ":" we have our output parameters, this parameters that this function will return.
|
||||
/// - Second ":" we have the input parameters which is our contexts. We only read from the "new" context
|
||||
/// but we modify the "old" context saving our registers there (see volatile option below)
|
||||
/// - Third ":" This our clobber list, this is information to the compiler that these registers can't be used freely
|
||||
/// - Fourth ":" This is options we can pass inn, Rust has 3: "alignstack", "volatile" and "intel"
|
||||
///
|
||||
/// For this to work on windows we need to use "alignstack" where the compiler adds the neccesary padding to
|
||||
/// make sure our stack is aligned. Since we modify one of our inputs, our assembly has "side effects"
|
||||
/// therefore we should use the `volatile` option. I **think** this is actually set for us by default
|
||||
/// when there are no output parameters given (my own assumption after going through the source code)
|
||||
/// for the `asm` macro, but we should make it explicit anyway.
|
||||
///
|
||||
/// One last important part (it will not work without this) is the #[naked] attribute. Basically this lets us have full
|
||||
/// control over the stack layout since normal functions has a prologue-and epilogue added by the
|
||||
/// compiler that will cause trouble for us. We avoid this by marking the funtion as "Naked".
|
||||
/// For this to work on `release` builds we also need to use the `#[inline(never)] attribute or else
|
||||
/// the compiler decides to inline this function (curiously this currently only happens on Windows).
|
||||
/// If the function is inlined we get a curious runtime error where it fails when switching back
|
||||
/// to as saved context and in general our assembly will not work as expected.
|
||||
///
|
||||
/// see: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md
|
||||
/// see: https://doc.rust-lang.org/nightly/reference/inline-assembly.html
|
||||
/// see: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html
|
||||
#[naked]
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn switch(old: *mut TaskContext, new: *const TaskContext) {
|
||||
// a0: _old, a1: _new
|
||||
asm!(
|
||||
"
|
||||
sd x1, 0x00(a0)
|
||||
sd x2, 0x08(a0)
|
||||
sd x8, 0x10(a0)
|
||||
sd x9, 0x18(a0)
|
||||
sd x18, 0x20(a0)
|
||||
sd x19, 0x28(a0)
|
||||
sd x20, 0x30(a0)
|
||||
sd x21, 0x38(a0)
|
||||
sd x22, 0x40(a0)
|
||||
sd x23, 0x48(a0)
|
||||
sd x24, 0x50(a0)
|
||||
sd x25, 0x58(a0)
|
||||
sd x26, 0x60(a0)
|
||||
sd x27, 0x68(a0)
|
||||
sd x1, 0x70(a0)
|
||||
|
||||
ld x1, 0x00(a1)
|
||||
ld x2, 0x08(a1)
|
||||
ld x8, 0x10(a1)
|
||||
ld x9, 0x18(a1)
|
||||
ld x18, 0x20(a1)
|
||||
ld x19, 0x28(a1)
|
||||
ld x20, 0x30(a1)
|
||||
ld x21, 0x38(a1)
|
||||
ld x22, 0x40(a1)
|
||||
ld x23, 0x48(a1)
|
||||
ld x24, 0x50(a1)
|
||||
ld x25, 0x58(a1)
|
||||
ld x26, 0x60(a1)
|
||||
ld x27, 0x68(a1)
|
||||
ld t0, 0x70(a1)
|
||||
|
||||
jr t0
|
||||
",
|
||||
options(noreturn)
|
||||
);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() {
|
||||
println!("stackful_coroutine begin...");
|
||||
println!("TASK 0(Runtime) STARTING");
|
||||
let mut runtime = Runtime::new();
|
||||
runtime.init();
|
||||
runtime.spawn(|| {
|
||||
println!("TASK 1 STARTING");
|
||||
let id = 1;
|
||||
for i in 0..4 {
|
||||
println!("task: {} counter: {}", id, i);
|
||||
yield_task();
|
||||
}
|
||||
println!("TASK 1 FINISHED");
|
||||
});
|
||||
runtime.spawn(|| {
|
||||
println!("TASK 2 STARTING");
|
||||
let id = 2;
|
||||
for i in 0..8 {
|
||||
println!("task: {} counter: {}", id, i);
|
||||
yield_task();
|
||||
}
|
||||
println!("TASK 2 FINISHED");
|
||||
});
|
||||
runtime.spawn(|| {
|
||||
println!("TASK 3 STARTING");
|
||||
let id = 3;
|
||||
for i in 0..12 {
|
||||
println!("task: {} counter: {}", id, i);
|
||||
yield_task();
|
||||
}
|
||||
println!("TASK 3 FINISHED");
|
||||
});
|
||||
runtime.spawn(|| {
|
||||
println!("TASK 4 STARTING");
|
||||
let id = 4;
|
||||
for i in 0..16 {
|
||||
println!("task: {} counter: {}", id, i);
|
||||
yield_task();
|
||||
}
|
||||
println!("TASK 4 FINISHED");
|
||||
});
|
||||
runtime.run();
|
||||
println!("stackful_coroutine PASSED");
|
||||
exit(0);
|
||||
}
|
129
user/src/bin/stackless_coroutine.rs
Normal file
129
user/src/bin/stackless_coroutine.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
// https://blog.aloni.org/posts/a-stack-less-rust-coroutine-100-loc/
|
||||
// https://github.com/chyyuu/example-coroutine-and-thread/tree/stackless-coroutine-x86
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
extern crate alloc;
|
||||
use alloc::collections::VecDeque;
|
||||
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
|
||||
enum State {
|
||||
Halted,
|
||||
Running,
|
||||
}
|
||||
|
||||
struct Task {
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
fn waiter<'a>(&'a mut self) -> Waiter<'a> {
|
||||
Waiter { task: self }
|
||||
}
|
||||
}
|
||||
|
||||
struct Waiter<'a> {
|
||||
task: &'a mut Task,
|
||||
}
|
||||
|
||||
impl<'a> Future for Waiter<'a> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
|
||||
match self.task.state {
|
||||
State::Halted => {
|
||||
self.task.state = State::Running;
|
||||
Poll::Ready(())
|
||||
}
|
||||
State::Running => {
|
||||
self.task.state = State::Halted;
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Executor {
|
||||
tasks: VecDeque<Pin<Box<dyn Future<Output = ()>>>>,
|
||||
}
|
||||
|
||||
impl Executor {
|
||||
fn new() -> Self {
|
||||
Executor {
|
||||
tasks: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push<C, F>(&mut self, closure: C)
|
||||
where
|
||||
F: Future<Output = ()> + 'static,
|
||||
C: FnOnce(Task) -> F,
|
||||
{
|
||||
let task = Task {
|
||||
state: State::Running,
|
||||
};
|
||||
self.tasks.push_back(Box::pin(closure(task)));
|
||||
}
|
||||
|
||||
fn run(&mut self) {
|
||||
let waker = create_waker();
|
||||
let mut context = Context::from_waker(&waker);
|
||||
|
||||
while let Some(mut task) = self.tasks.pop_front() {
|
||||
match task.as_mut().poll(&mut context) {
|
||||
Poll::Pending => {
|
||||
self.tasks.push_back(task);
|
||||
}
|
||||
Poll::Ready(()) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_waker() -> Waker {
|
||||
// Safety: The waker points to a vtable with functions that do nothing. Doing
|
||||
// nothing is memory-safe.
|
||||
unsafe { Waker::from_raw(RAW_WAKER) }
|
||||
}
|
||||
|
||||
const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &VTABLE);
|
||||
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
|
||||
|
||||
unsafe fn clone(_: *const ()) -> RawWaker {
|
||||
RAW_WAKER
|
||||
}
|
||||
unsafe fn wake(_: *const ()) {}
|
||||
unsafe fn wake_by_ref(_: *const ()) {}
|
||||
unsafe fn drop(_: *const ()) {}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("stackless coroutine Begin..");
|
||||
let mut exec = Executor::new();
|
||||
println!(" Create futures");
|
||||
for instance in 1..=3 {
|
||||
exec.push(move |mut task| async move {
|
||||
println!(" Task {}: begin state", instance);
|
||||
task.waiter().await;
|
||||
println!(" Task {}: next state", instance);
|
||||
task.waiter().await;
|
||||
println!(" Task {}: end state", instance);
|
||||
});
|
||||
}
|
||||
|
||||
println!(" Running");
|
||||
exec.run();
|
||||
println!(" Done");
|
||||
println!("stackless coroutine PASSED");
|
||||
|
||||
0
|
||||
}
|
46
user/src/bin/udp.rs
Normal file
46
user/src/bin/udp.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use alloc::string::String;
|
||||
|
||||
#[macro_use]
|
||||
extern crate user_lib;
|
||||
#[macro_use]
|
||||
extern crate alloc;
|
||||
|
||||
use user_lib::{connect, write, read};
|
||||
|
||||
#[no_mangle]
|
||||
pub fn main() -> i32 {
|
||||
println!("udp test open!");
|
||||
|
||||
let udp_fd = connect(10 << 24 | 0 << 16 | 2 << 8 | 2, 2001, 26099);
|
||||
|
||||
if udp_fd < 0 {
|
||||
println!("failed to create udp connection.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
let buf = "Hello rCoreOS user program!";
|
||||
|
||||
println!("send <{}>", buf);
|
||||
|
||||
write(udp_fd as usize, buf.as_bytes());
|
||||
|
||||
println!("udp send done, waiting for reply.");
|
||||
|
||||
let mut buf = vec![0u8; 1024];
|
||||
|
||||
let len = read(udp_fd as usize, &mut buf);
|
||||
|
||||
if len < 0 {
|
||||
println!("can't receive udp packet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
let recv_str = String::from_utf8_lossy(&buf[..len as usize]);
|
||||
|
||||
println!("receive reply <{}>", recv_str);
|
||||
|
||||
0
|
||||
}
|
|
@ -27,19 +27,22 @@ static SUCC_TESTS: &[(&str, &str, &str, &str, i32)] = &[
|
|||
("phil_din_mutex\0", "\0", "\0", "\0", 0),
|
||||
("pipe_large_test\0", "\0", "\0", "\0", 0),
|
||||
("pipetest\0", "\0", "\0", "\0", 0),
|
||||
("race_adder_arg\0", "3\0", "\0", "\0", 0),
|
||||
("race_adder_atomic\0", "\0", "\0", "\0", 0),
|
||||
("race_adder_mutex_blocking\0", "\0", "\0", "\0", 0),
|
||||
("race_adder_mutex_spin\0", "\0", "\0", "\0", 0),
|
||||
("adder_peterson_spin\0", "\0", "\0", "\0", 0),
|
||||
("adder_peterson_yield\0", "\0", "\0", "\0", 0),
|
||||
("adder_mutex_blocking\0", "\0", "\0", "\0", 0),
|
||||
("adder_mutex_spin\0", "\0", "\0", "\0", 0),
|
||||
("run_pipe_test\0", "\0", "\0", "\0", 0),
|
||||
("sleep_simple\0", "\0", "\0", "\0", 0),
|
||||
("sleep\0", "\0", "\0", "\0", 0),
|
||||
("sleep_simple\0", "\0", "\0", "\0", 0),
|
||||
("sync_sem\0", "\0", "\0", "\0", 0),
|
||||
("test_condvar\0", "\0", "\0", "\0", 0),
|
||||
("condsync_sem\0", "\0", "\0", "\0", 0),
|
||||
("condsync_condvar\0", "\0", "\0", "\0", 0),
|
||||
("threads_arg\0", "\0", "\0", "\0", 0),
|
||||
("threads\0", "\0", "\0", "\0", 0),
|
||||
("yield\0", "\0", "\0", "\0", 0),
|
||||
("barrier_fail\0", "\0", "\0", "\0", 0),
|
||||
("barrier_condvar\0", "\0", "\0", "\0", 0),
|
||||
];
|
||||
|
||||
static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[
|
||||
|
@ -49,8 +52,9 @@ static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[
|
|||
("priv_inst\0", "\0", "\0", "\0", -4),
|
||||
("store_fault\0", "\0", "\0", "\0", -11),
|
||||
("until_timeout\0", "\0", "\0", "\0", -6),
|
||||
("race_adder\0", "\0", "\0", "\0", -6),
|
||||
("huge_write_mt\0", "\0", "\0", "\0", -6),
|
||||
("adder\0", "\0", "\0", "\0", -6),
|
||||
("adder_simple_spin\0", "\0", "\0", "\0", -6),
|
||||
("adder_simple_yield\0", "\0", "\0", "\0", -6),
|
||||
];
|
||||
|
||||
use user_lib::{exec, fork, waitpid};
|
||||
|
|
30
user/src/file.rs
Normal file
30
user/src/file.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
use super::*;
|
||||
|
||||
bitflags! {
|
||||
pub struct OpenFlags: u32 {
|
||||
const RDONLY = 0;
|
||||
const WRONLY = 1 << 0;
|
||||
const RDWR = 1 << 1;
|
||||
const CREATE = 1 << 9;
|
||||
const TRUNC = 1 << 10;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dup(fd: usize) -> isize {
|
||||
sys_dup(fd)
|
||||
}
|
||||
pub fn open(path: &str, flags: OpenFlags) -> isize {
|
||||
sys_open(path, flags.bits)
|
||||
}
|
||||
pub fn close(fd: usize) -> isize {
|
||||
sys_close(fd)
|
||||
}
|
||||
pub fn pipe(pipe_fd: &mut [usize]) -> isize {
|
||||
sys_pipe(pipe_fd)
|
||||
}
|
||||
pub fn read(fd: usize, buf: &mut [u8]) -> isize {
|
||||
sys_read(fd, buf)
|
||||
}
|
||||
pub fn write(fd: usize, buf: &[u8]) -> isize {
|
||||
sys_write(fd, buf)
|
||||
}
|
118
user/src/io.rs
Normal file
118
user/src/io.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
use super::*;
|
||||
use embedded_graphics::prelude::{RgbColor, Size};
|
||||
use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions};
|
||||
use embedded_graphics::pixelcolor::Rgb888;
|
||||
use virtio_input_decoder::Decoder;
|
||||
pub use virtio_input_decoder::{DecodeType, Key, KeyType, Mouse};
|
||||
|
||||
pub const VIRTGPU_XRES: u32 = 1280;
|
||||
pub const VIRTGPU_YRES: u32 = 800;
|
||||
pub const VIRTGPU_LEN: usize = (VIRTGPU_XRES * VIRTGPU_YRES * 4) as usize;
|
||||
|
||||
pub fn framebuffer() -> isize {
|
||||
sys_framebuffer()
|
||||
}
|
||||
pub fn framebuffer_flush() -> isize {
|
||||
sys_framebuffer_flush()
|
||||
}
|
||||
|
||||
pub struct Display {
|
||||
pub size: Size,
|
||||
pub fb: &'static mut [u8],
|
||||
}
|
||||
|
||||
impl Display {
|
||||
pub fn new(size: Size) -> Self {
|
||||
let fb_ptr = framebuffer() as *mut u8;
|
||||
let fb =
|
||||
unsafe { core::slice::from_raw_parts_mut(fb_ptr, VIRTGPU_LEN as usize) };
|
||||
Self { size, fb}
|
||||
}
|
||||
pub fn framebuffer(&mut self) -> &mut [u8] {
|
||||
self.fb
|
||||
}
|
||||
pub fn paint_on_framebuffer(&mut self, p: impl FnOnce(&mut [u8]) -> ()) {
|
||||
p(self.framebuffer());
|
||||
framebuffer_flush();
|
||||
}
|
||||
}
|
||||
|
||||
impl OriginDimensions for Display {
|
||||
fn size(&self) -> Size {
|
||||
self.size
|
||||
}
|
||||
}
|
||||
|
||||
impl DrawTarget for Display {
|
||||
type Color = Rgb888;
|
||||
|
||||
type Error = core::convert::Infallible;
|
||||
|
||||
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
|
||||
where
|
||||
I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,
|
||||
{
|
||||
pixels.into_iter().for_each(|px| {
|
||||
let idx = (px.0.y * VIRTGPU_XRES as i32 + px.0.x)
|
||||
as usize
|
||||
* 4;
|
||||
if idx + 2 >= self.fb.len() {
|
||||
return;
|
||||
}
|
||||
self.fb[idx] = px.1.b();
|
||||
self.fb[idx + 1] = px.1.g();
|
||||
self.fb[idx + 2] = px.1.r();
|
||||
});
|
||||
framebuffer_flush();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event_get() -> Option<InputEvent> {
|
||||
let raw_value = sys_event_get();
|
||||
if raw_value == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((raw_value as u64).into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_pressed() -> bool {
|
||||
if sys_key_pressed() == 1 {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct InputEvent {
|
||||
pub event_type: u16,
|
||||
pub code: u16,
|
||||
pub value: u32,
|
||||
}
|
||||
|
||||
impl From<u64> for InputEvent {
|
||||
fn from(mut v: u64) -> Self {
|
||||
let value = v as u32;
|
||||
v >>= 32;
|
||||
let code = v as u16;
|
||||
v >>= 16;
|
||||
let event_type = v as u16;
|
||||
Self {
|
||||
event_type,
|
||||
code,
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InputEvent {
|
||||
pub fn decode(&self) -> Option<DecodeType> {
|
||||
Decoder::decode(
|
||||
self.event_type as usize,
|
||||
self.code as usize,
|
||||
self.value as usize,
|
||||
).ok()
|
||||
}
|
||||
}
|
152
user/src/lib.rs
152
user/src/lib.rs
|
@ -8,6 +8,11 @@
|
|||
pub mod console;
|
||||
mod lang_items;
|
||||
mod syscall;
|
||||
mod file;
|
||||
mod task;
|
||||
mod sync;
|
||||
mod io;
|
||||
mod net;
|
||||
|
||||
extern crate alloc;
|
||||
#[macro_use]
|
||||
|
@ -16,6 +21,11 @@ extern crate bitflags;
|
|||
use alloc::vec::Vec;
|
||||
use buddy_system_allocator::LockedHeap;
|
||||
use syscall::*;
|
||||
pub use file::*;
|
||||
pub use task::*;
|
||||
pub use sync::*;
|
||||
pub use io::*;
|
||||
pub use net::*;
|
||||
|
||||
const USER_HEAP_SIZE: usize = 32768;
|
||||
|
||||
|
@ -59,148 +69,6 @@ fn main(_argc: usize, _argv: &[&str]) -> i32 {
|
|||
panic!("Cannot find main!");
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct OpenFlags: u32 {
|
||||
const RDONLY = 0;
|
||||
const WRONLY = 1 << 0;
|
||||
const RDWR = 1 << 1;
|
||||
const CREATE = 1 << 9;
|
||||
const TRUNC = 1 << 10;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dup(fd: usize) -> isize {
|
||||
sys_dup(fd)
|
||||
}
|
||||
pub fn open(path: &str, flags: OpenFlags) -> isize {
|
||||
sys_open(path, flags.bits)
|
||||
}
|
||||
pub fn close(fd: usize) -> isize {
|
||||
sys_close(fd)
|
||||
}
|
||||
pub fn pipe(pipe_fd: &mut [usize]) -> isize {
|
||||
sys_pipe(pipe_fd)
|
||||
}
|
||||
pub fn read(fd: usize, buf: &mut [u8]) -> isize {
|
||||
sys_read(fd, buf)
|
||||
}
|
||||
pub fn write(fd: usize, buf: &[u8]) -> isize {
|
||||
sys_write(fd, buf)
|
||||
}
|
||||
pub fn exit(exit_code: i32) -> ! {
|
||||
sys_exit(exit_code);
|
||||
}
|
||||
pub fn yield_() -> isize {
|
||||
sys_yield()
|
||||
}
|
||||
pub fn get_time() -> isize {
|
||||
sys_get_time()
|
||||
}
|
||||
pub fn getpid() -> isize {
|
||||
sys_getpid()
|
||||
}
|
||||
pub fn fork() -> isize {
|
||||
sys_fork()
|
||||
}
|
||||
pub fn exec(path: &str, args: &[*const u8]) -> isize {
|
||||
sys_exec(path, args)
|
||||
}
|
||||
pub fn wait(exit_code: &mut i32) -> isize {
|
||||
loop {
|
||||
match sys_waitpid(-1, exit_code as *mut _) {
|
||||
-2 => {
|
||||
yield_();
|
||||
}
|
||||
// -1 or a real pid
|
||||
exit_pid => return exit_pid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize {
|
||||
loop {
|
||||
match sys_waitpid(pid as isize, exit_code as *mut _) {
|
||||
-2 => {
|
||||
yield_();
|
||||
}
|
||||
// -1 or a real pid
|
||||
exit_pid => return exit_pid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid_nb(pid: usize, exit_code: &mut i32) -> isize {
|
||||
sys_waitpid(pid as isize, exit_code as *mut _)
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct SignalFlags: i32 {
|
||||
const SIGINT = 1 << 2;
|
||||
const SIGILL = 1 << 4;
|
||||
const SIGABRT = 1 << 6;
|
||||
const SIGFPE = 1 << 8;
|
||||
const SIGSEGV = 1 << 11;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kill(pid: usize, signal: i32) -> isize {
|
||||
sys_kill(pid, signal)
|
||||
}
|
||||
|
||||
pub fn sleep(sleep_ms: usize) {
|
||||
sys_sleep(sleep_ms);
|
||||
}
|
||||
|
||||
pub fn thread_create(entry: usize, arg: usize) -> isize {
|
||||
sys_thread_create(entry, arg)
|
||||
}
|
||||
pub fn gettid() -> isize {
|
||||
sys_gettid()
|
||||
}
|
||||
pub fn waittid(tid: usize) -> isize {
|
||||
loop {
|
||||
match sys_waittid(tid) {
|
||||
-2 => {
|
||||
yield_();
|
||||
}
|
||||
exit_code => return exit_code,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mutex_create() -> isize {
|
||||
sys_mutex_create(false)
|
||||
}
|
||||
pub fn mutex_blocking_create() -> isize {
|
||||
sys_mutex_create(true)
|
||||
}
|
||||
pub fn mutex_lock(mutex_id: usize) {
|
||||
sys_mutex_lock(mutex_id);
|
||||
}
|
||||
pub fn mutex_unlock(mutex_id: usize) {
|
||||
sys_mutex_unlock(mutex_id);
|
||||
}
|
||||
pub fn semaphore_create(res_count: usize) -> isize {
|
||||
sys_semaphore_create(res_count)
|
||||
}
|
||||
pub fn semaphore_up(sem_id: usize) {
|
||||
sys_semaphore_up(sem_id);
|
||||
}
|
||||
pub fn semaphore_down(sem_id: usize) {
|
||||
sys_semaphore_down(sem_id);
|
||||
}
|
||||
pub fn condvar_create() -> isize {
|
||||
sys_condvar_create(0)
|
||||
}
|
||||
pub fn condvar_signal(condvar_id: usize) {
|
||||
sys_condvar_signal(condvar_id);
|
||||
}
|
||||
pub fn condvar_wait(condvar_id: usize, mutex_id: usize) {
|
||||
sys_condvar_wait(condvar_id, mutex_id);
|
||||
}
|
||||
pub fn create_desktop() {
|
||||
sys_create_desktop();
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! vstore {
|
||||
($var_ref: expr, $value: expr) => {
|
||||
|
|
5
user/src/net.rs
Normal file
5
user/src/net.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use super::*;
|
||||
|
||||
pub fn connect(ip: u32, sport: u16, dport: u16) -> isize {
|
||||
sys_connect(ip, sport, dport)
|
||||
}
|
32
user/src/sync.rs
Normal file
32
user/src/sync.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use super::*;
|
||||
|
||||
pub fn mutex_create() -> isize {
|
||||
sys_mutex_create(false)
|
||||
}
|
||||
pub fn mutex_blocking_create() -> isize {
|
||||
sys_mutex_create(true)
|
||||
}
|
||||
pub fn mutex_lock(mutex_id: usize) {
|
||||
sys_mutex_lock(mutex_id);
|
||||
}
|
||||
pub fn mutex_unlock(mutex_id: usize) {
|
||||
sys_mutex_unlock(mutex_id);
|
||||
}
|
||||
pub fn semaphore_create(res_count: usize) -> isize {
|
||||
sys_semaphore_create(res_count)
|
||||
}
|
||||
pub fn semaphore_up(sem_id: usize) {
|
||||
sys_semaphore_up(sem_id);
|
||||
}
|
||||
pub fn semaphore_down(sem_id: usize) {
|
||||
sys_semaphore_down(sem_id);
|
||||
}
|
||||
pub fn condvar_create() -> isize {
|
||||
sys_condvar_create()
|
||||
}
|
||||
pub fn condvar_signal(condvar_id: usize) {
|
||||
sys_condvar_signal(condvar_id);
|
||||
}
|
||||
pub fn condvar_wait(condvar_id: usize, mutex_id: usize) {
|
||||
sys_condvar_wait(condvar_id, mutex_id);
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
const SYSCALL_DUP: usize = 24;
|
||||
const SYSCALL_CONNECT: usize = 29;
|
||||
const SYSCALL_OPEN: usize = 56;
|
||||
const SYSCALL_CLOSE: usize = 57;
|
||||
const SYSCALL_PIPE: usize = 59;
|
||||
|
@ -25,6 +26,10 @@ const SYSCALL_SEMAPHORE_DOWN: usize = 1022;
|
|||
const SYSCALL_CONDVAR_CREATE: usize = 1030;
|
||||
const SYSCALL_CONDVAR_SIGNAL: usize = 1031;
|
||||
const SYSCALL_CONDVAR_WAIT: usize = 1032;
|
||||
const SYSCALL_FRAMEBUFFER: usize = 2000;
|
||||
const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001;
|
||||
const SYSCALL_EVENT_GET: usize = 3000;
|
||||
const SYSCALL_KEY_PRESSED: usize = 3001;
|
||||
|
||||
fn syscall(id: usize, args: [usize; 3]) -> isize {
|
||||
let mut ret: isize;
|
||||
|
@ -44,6 +49,10 @@ pub fn sys_dup(fd: usize) -> isize {
|
|||
syscall(SYSCALL_DUP, [fd, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_connect(dest: u32, sport: u16, dport: u16) -> isize {
|
||||
syscall(SYSCALL_CONNECT, [dest as usize, sport as usize, dport as usize])
|
||||
}
|
||||
|
||||
pub fn sys_open(path: &str, flags: u32) -> isize {
|
||||
syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0])
|
||||
}
|
||||
|
@ -143,8 +152,8 @@ pub fn sys_semaphore_down(sem_id: usize) -> isize {
|
|||
syscall(SYSCALL_SEMAPHORE_DOWN, [sem_id, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_condvar_create(_arg: usize) -> isize {
|
||||
syscall(SYSCALL_CONDVAR_CREATE, [_arg, 0, 0])
|
||||
pub fn sys_condvar_create() -> isize {
|
||||
syscall(SYSCALL_CONDVAR_CREATE, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_condvar_signal(condvar_id: usize) -> isize {
|
||||
|
@ -154,6 +163,20 @@ pub fn sys_condvar_signal(condvar_id: usize) -> isize {
|
|||
pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize {
|
||||
syscall(SYSCALL_CONDVAR_WAIT, [condvar_id, mutex_id, 0])
|
||||
}
|
||||
pub fn sys_create_desktop() -> isize {
|
||||
syscall(2000, [0, 0, 0])
|
||||
|
||||
|
||||
pub fn sys_framebuffer() -> isize {
|
||||
syscall(SYSCALL_FRAMEBUFFER, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_framebuffer_flush() -> isize {
|
||||
syscall(SYSCALL_FRAMEBUFFER_FLUSH, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_event_get() -> isize {
|
||||
syscall(SYSCALL_EVENT_GET, [0, 0, 0])
|
||||
}
|
||||
|
||||
pub fn sys_key_pressed() -> isize {
|
||||
syscall(SYSCALL_KEY_PRESSED, [0, 0, 0])
|
||||
}
|
83
user/src/task.rs
Normal file
83
user/src/task.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use super::*;
|
||||
|
||||
pub fn exit(exit_code: i32) -> ! {
|
||||
sys_exit(exit_code);
|
||||
}
|
||||
pub fn yield_() -> isize {
|
||||
sys_yield()
|
||||
}
|
||||
pub fn get_time() -> isize {
|
||||
sys_get_time()
|
||||
}
|
||||
pub fn getpid() -> isize {
|
||||
sys_getpid()
|
||||
}
|
||||
pub fn fork() -> isize {
|
||||
sys_fork()
|
||||
}
|
||||
pub fn exec(path: &str, args: &[*const u8]) -> isize {
|
||||
sys_exec(path, args)
|
||||
}
|
||||
|
||||
pub fn wait(exit_code: &mut i32) -> isize {
|
||||
loop {
|
||||
match sys_waitpid(-1, exit_code as *mut _) {
|
||||
-2 => {
|
||||
yield_();
|
||||
}
|
||||
// -1 or a real pid
|
||||
exit_pid => return exit_pid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize {
|
||||
loop {
|
||||
match sys_waitpid(pid as isize, exit_code as *mut _) {
|
||||
-2 => {
|
||||
yield_();
|
||||
}
|
||||
// -1 or a real pid
|
||||
exit_pid => return exit_pid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn waitpid_nb(pid: usize, exit_code: &mut i32) -> isize {
|
||||
sys_waitpid(pid as isize, exit_code as *mut _)
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct SignalFlags: i32 {
|
||||
const SIGINT = 1 << 2;
|
||||
const SIGILL = 1 << 4;
|
||||
const SIGABRT = 1 << 6;
|
||||
const SIGFPE = 1 << 8;
|
||||
const SIGSEGV = 1 << 11;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kill(pid: usize, signal: i32) -> isize {
|
||||
sys_kill(pid, signal)
|
||||
}
|
||||
|
||||
pub fn sleep(sleep_ms: usize) {
|
||||
sys_sleep(sleep_ms);
|
||||
}
|
||||
|
||||
pub fn thread_create(entry: usize, arg: usize) -> isize {
|
||||
sys_thread_create(entry, arg)
|
||||
}
|
||||
pub fn gettid() -> isize {
|
||||
sys_gettid()
|
||||
}
|
||||
pub fn waittid(tid: usize) -> isize {
|
||||
loop {
|
||||
match sys_waittid(tid) {
|
||||
-2 => {
|
||||
yield_();
|
||||
}
|
||||
exit_code => return exit_code,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue