diff --git a/user/src/bin/race_adder.rs b/user/src/bin/adder.rs similarity index 74% rename from user/src/bin/race_adder.rs rename to user/src/bin/adder.rs index 235aca88..c39e810d 100644 --- a/user/src/bin/race_adder.rs +++ b/user/src/bin/adder.rs @@ -12,15 +12,19 @@ static mut A: usize = 0; const PER_THREAD: usize = 10000; const THREAD_COUNT: usize = 16; +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 { - 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); + critical_section(&mut t); } exit(t as i32) } diff --git a/user/src/bin/adder_atomic.rs b/user/src/bin/adder_atomic.rs new file mode 100644 index 00000000..fe2cdb17 --- /dev/null +++ b/user/src/bin/adder_atomic.rs @@ -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 +} diff --git a/user/src/bin/adder_loop.rs b/user/src/bin/adder_loop.rs new file mode 100644 index 00000000..3501ff9d --- /dev/null +++ b/user/src/bin/adder_loop.rs @@ -0,0 +1,69 @@ +#![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_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 +} diff --git a/user/src/bin/adder_mutex_blocking.rs b/user/src/bin/adder_mutex_blocking.rs new file mode 100644 index 00000000..963dd753 --- /dev/null +++ b/user/src/bin/adder_mutex_blocking.rs @@ -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 +} diff --git a/user/src/bin/adder_mutex_spin.rs b/user/src/bin/adder_mutex_spin.rs new file mode 100644 index 00000000..b96f933c --- /dev/null +++ b/user/src/bin/adder_mutex_spin.rs @@ -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 +} diff --git a/user/src/bin/adder_peterson_spin.rs b/user/src/bin/adder_peterson_spin.rs new file mode 100644 index 00000000..85e559df --- /dev/null +++ b/user/src/bin/adder_peterson_spin.rs @@ -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}; +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; + TURN = 1 - id; + // 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[1 - id]) && vload!(&TURN) == 1 - id {} +} + +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 +} diff --git a/user/src/bin/adder_peterson_yield.rs b/user/src/bin/adder_peterson_yield.rs new file mode 100644 index 00000000..a7f9a25e --- /dev/null +++ b/user/src/bin/adder_peterson_yield.rs @@ -0,0 +1,88 @@ +//! 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; + TURN = 1 - id; + // Tell the compiler not to reorder memory operations + // across this fence. + compiler_fence(Ordering::SeqCst); + while FLAG[1 - id] && TURN == 1 - id { + 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 +} diff --git a/user/src/bin/peterson.rs b/user/src/bin/peterson.rs index 556ebc0d..be3e299a 100644 --- a/user/src/bin/peterson.rs +++ b/user/src/bin/peterson.rs @@ -10,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]; @@ -29,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 { @@ -74,4 +77,4 @@ pub fn main() -> i32 { } println!("main thread exited."); 0 -} +} \ No newline at end of file diff --git a/user/src/bin/race_adder_arg.rs b/user/src/bin/race_adder_arg.rs deleted file mode 100644 index 43315f13..00000000 --- a/user/src/bin/race_adder_arg.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -extern crate alloc; - -use crate::alloc::string::ToString; -use alloc::vec::Vec; -use user_lib::{exit, get_time, thread_create, waittid}; - -static mut A: usize = 0; -const PER_THREAD: usize = 10000; -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(argc: usize, argv: &[&str]) -> i32 { - let mut thread_count = THREAD_COUNT; - if argc == 2 { - thread_count = argv[1].to_string().parse::().unwrap(); - } else if argc != 1 { - println!("ERROR in argv"); - exit(-1); - } - - 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 }, PER_THREAD * thread_count); - 0 -} diff --git a/user/src/bin/race_adder_atomic.rs b/user/src/bin/race_adder_atomic.rs deleted file mode 100644 index 60a5c004..00000000 --- a/user/src/bin/race_adder_atomic.rs +++ /dev/null @@ -1,50 +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 = 10000; -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); - } - for tid in v.into_iter() { - waittid(tid); - } - println!("time cost is {}ms", get_time() - start); - assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); - 0 -} diff --git a/user/src/bin/race_adder_loop.rs b/user/src/bin/race_adder_loop.rs deleted file mode 100644 index 0f04e033..00000000 --- a/user/src/bin/race_adder_loop.rs +++ /dev/null @@ -1,50 +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 = 10000; -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); - } - for tid in v.into_iter() { - waittid(tid); - } - println!("time cost is {}ms", get_time() - start); - assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); - 0 -} diff --git a/user/src/bin/race_adder_mutex_blocking.rs b/user/src/bin/race_adder_mutex_blocking.rs deleted file mode 100644 index 720fe8bc..00000000 --- a/user/src/bin/race_adder_mutex_blocking.rs +++ /dev/null @@ -1,45 +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 = 10000; -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); - } - for tid in v.into_iter() { - waittid(tid); - } - println!("time cost is {}ms", get_time() - start); - assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); - 0 -} diff --git a/user/src/bin/race_adder_mutex_spin.rs b/user/src/bin/race_adder_mutex_spin.rs deleted file mode 100644 index 98fb441d..00000000 --- a/user/src/bin/race_adder_mutex_spin.rs +++ /dev/null @@ -1,45 +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 = 10000; -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); - } - for tid in v.into_iter() { - waittid(tid); - } - println!("time cost is {}ms", get_time() - start); - assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); - 0 -}