user: add critical test for software-synchronous tests

This commit is contained in:
DeathWish5 2021-12-23 15:25:06 +08:00
parent 97fdd4f2a2
commit 045c47e4ef
3 changed files with 60 additions and 22 deletions

View File

@ -9,6 +9,7 @@ extern crate core;
use user_lib::{thread_create, waittid, exit, sleep}; use user_lib::{thread_create, waittid, exit, sleep};
use alloc::vec::Vec; use alloc::vec::Vec;
use core::sync::atomic::{AtomicUsize, Ordering};
const N: usize = 2; const N: usize = 2;
const THREAD_NUM: usize = 10; const THREAD_NUM: usize = 10;
@ -21,19 +22,33 @@ enum FlagState {
static mut TURN: usize = 0; static mut TURN: usize = 0;
static mut FLAG: [FlagState; THREAD_NUM] = [FlagState::Out; THREAD_NUM]; static mut FLAG: [FlagState; THREAD_NUM] = [FlagState::Out; THREAD_NUM];
static GUARD: AtomicUsize = AtomicUsize::new(0);
fn critical_test_enter() {
assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0);
}
fn critical_test_claim() {
assert_eq!(GUARD.load(Ordering::SeqCst), 1);
}
fn critical_test_exit() {
assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1);
}
fn eisenberg_enter_critical(id: usize) { fn eisenberg_enter_critical(id: usize) {
/* announce that we want to enter */ /* announce that we want to enter */
loop { loop {
println!("Thread[{}] try enter", id); println!("Thread[{}] try enter", id);
store!(&FLAG[id], FlagState::Want); vstore!(&FLAG[id], FlagState::Want);
loop { loop {
/* check if any with higher priority is `Want` or `In` */ /* check if any with higher priority is `Want` or `In` */
let mut prior_thread:Option<usize> = None; let mut prior_thread:Option<usize> = None;
let turn = load!(&TURN); let turn = vload!(&TURN);
let ring_id = if id < turn { id + THREAD_NUM } else { id }; let ring_id = if id < turn { id + THREAD_NUM } else { id };
// FLAG.iter() may lead to some errors, use for-loop instead // FLAG.iter() may lead to some errors, use for-loop instead
for i in turn..ring_id { for i in turn..ring_id {
if load!(&FLAG[i % THREAD_NUM]) != FlagState::Out { if vload!(&FLAG[i % THREAD_NUM]) != FlagState::Out {
prior_thread = Some(i % THREAD_NUM); prior_thread = Some(i % THREAD_NUM);
break; break;
} }
@ -46,13 +61,13 @@ fn eisenberg_enter_critical(id: usize) {
sleep(1); sleep(1);
} }
/* now tentatively claim the resource */ /* now tentatively claim the resource */
store!(&FLAG[id], FlagState::In); vstore!(&FLAG[id], FlagState::In);
/* enforce the order of `claim` and `conflict check`*/ /* enforce the order of `claim` and `conflict check`*/
memory_fence!(); memory_fence!();
/* check if anthor thread is also `In`, which imply a conflict*/ /* check if anthor thread is also `In`, which imply a conflict*/
let mut conflict = false; let mut conflict = false;
for i in 0..THREAD_NUM { for i in 0..THREAD_NUM {
if i != id && load!(&FLAG[i]) == FlagState::In { if i != id && vload!(&FLAG[i]) == FlagState::In {
conflict = true; conflict = true;
} }
} }
@ -63,7 +78,7 @@ fn eisenberg_enter_critical(id: usize) {
/* no need to sleep */ /* no need to sleep */
} }
/* clain the trun */ /* clain the trun */
store!(&TURN, id); vstore!(&TURN, id);
println!("Thread[{}] enter", id); println!("Thread[{}] enter", id);
} }
@ -73,14 +88,14 @@ fn eisenberg_exit_critical(id: usize) {
let ring_id = id + THREAD_NUM; let ring_id = id + THREAD_NUM;
for i in (id+1)..ring_id { for i in (id+1)..ring_id {
let idx = i % THREAD_NUM; let idx = i % THREAD_NUM;
if load!(&FLAG[idx]) == FlagState::Want { if vload!(&FLAG[idx]) == FlagState::Want {
next = idx; next = idx;
break; break;
} }
} }
store!(&TURN, next); vstore!(&TURN, next);
/* All done */ /* All done */
store!(&FLAG[id], FlagState::Out); vstore!(&FLAG[id], FlagState::Out);
println!("Thread[{}] exit, give turn to {}", id, next); println!("Thread[{}] exit, give turn to {}", id, next);
} }
@ -88,9 +103,12 @@ pub fn thread_fn(id: usize) -> ! {
println!("Thread[{}] init.", id); println!("Thread[{}] init.", id);
for _ in 0..N { for _ in 0..N {
eisenberg_enter_critical(id); eisenberg_enter_critical(id);
critical_test_enter();
for _ in 0..3 { for _ in 0..3 {
critical_test_claim();
sleep(2); sleep(2);
} }
critical_test_exit();
eisenberg_exit_critical(id); eisenberg_exit_critical(id);
} }
exit(0) exit(0)
@ -107,6 +125,7 @@ pub fn main() -> i32 {
} }
for tid in v.iter() { for tid in v.iter() {
let exit_code = waittid(*tid as usize); let exit_code = waittid(*tid as usize);
assert_eq!(exit_code, 0, "thread conflict happened!");
println!("thread#{} exited with code {}", tid, exit_code); println!("thread#{} exited with code {}", tid, exit_code);
} }
println!("main thread exited."); println!("main thread exited.");

View File

@ -1,6 +1,7 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
#![feature(core_intrinsics)] #![feature(core_intrinsics)]
#![feature(asm)]
#[macro_use] #[macro_use]
extern crate user_lib; extern crate user_lib;
@ -8,38 +9,55 @@ extern crate alloc;
extern crate core; extern crate core;
use user_lib::{thread_create, waittid, exit, sleep}; use user_lib::{thread_create, waittid, exit, sleep};
use core::sync::atomic::{AtomicUsize, Ordering};
use alloc::vec::Vec; use alloc::vec::Vec;
const N: usize = 3; const N: usize = 3;
static mut TURN: usize = 0; static mut TURN: usize = 0;
static mut FLAG: [bool; 2] = [false; 2]; static mut FLAG: [bool; 2] = [false; 2];
static GUARD: AtomicUsize = AtomicUsize::new(0);
fn critical_test_enter() {
assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0);
}
fn critical_test_claim() {
assert_eq!(GUARD.load(Ordering::SeqCst), 1);
}
fn critical_test_exit() {
assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1);
}
fn peterson_enter_critical(id: usize, peer_id: usize) { fn peterson_enter_critical(id: usize, peer_id: usize) {
println!("Thread {} try enter", id); println!("Thread[{}] try enter", id);
store!(&FLAG[id], true); vstore!(&FLAG[id], true);
store!(&TURN, peer_id); vstore!(&TURN, peer_id);
memory_fence!(); memory_fence!();
while load!(&FLAG[peer_id]) && load!(&TURN) == peer_id { while vload!(&FLAG[peer_id]) && vload!(&TURN) == peer_id {
println!("Thread {} enter fail", id); println!("Thread[{}] enter fail", id);
sleep(1); 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) { fn peterson_exit_critical(id: usize) {
store!(&FLAG[id], false); vstore!(&FLAG[id], false);
println!("Thread {} exit", id); println!("Thread[{}] exit", id);
} }
pub fn thread_fn(id: usize) -> ! { pub fn thread_fn(id: usize) -> ! {
println!("Thread {} init.", id); println!("Thread[{}] init.", id);
let peer_id: usize = id ^ 1; let peer_id: usize = id ^ 1;
for _ in 0..N { for _ in 0..N {
peterson_enter_critical(id, peer_id); peterson_enter_critical(id, peer_id);
critical_test_enter();
for _ in 0..3 { for _ in 0..3 {
critical_test_claim();
sleep(2); sleep(2);
} }
critical_test_exit();
peterson_exit_critical(id); peterson_exit_critical(id);
} }
exit(0) exit(0)
@ -49,9 +67,10 @@ pub fn thread_fn(id: usize) -> ! {
pub fn main() -> i32 { pub fn main() -> i32 {
let mut v = Vec::new(); let mut v = Vec::new();
v.push(thread_create(thread_fn as usize, 0)); v.push(thread_create(thread_fn as usize, 0));
v.push(thread_create(thread_fn as usize, 1)); // v.push(thread_create(thread_fn as usize, 1));
for tid in v.iter() { for tid in v.iter() {
let exit_code = waittid(*tid as usize); let exit_code = waittid(*tid as usize);
assert_eq!(exit_code, 0, "thread conflict happened!");
println!("thread#{} exited with code {}", tid, exit_code); println!("thread#{} exited with code {}", tid, exit_code);
} }
println!("main thread exited."); println!("main thread exited.");

View File

@ -131,14 +131,14 @@ pub fn semaphore_down(sem_id: usize) {
} }
#[macro_export] #[macro_export]
macro_rules! store { macro_rules! vstore {
($var_ref: expr, $value: expr) => { ($var_ref: expr, $value: expr) => {
unsafe { core::intrinsics::volatile_store($var_ref as *const _ as _, $value) } unsafe { core::intrinsics::volatile_store($var_ref as *const _ as _, $value) }
}; };
} }
#[macro_export] #[macro_export]
macro_rules! load { macro_rules! vload {
($var_ref: expr) => { ($var_ref: expr) => {
unsafe { core::intrinsics::volatile_load($var_ref as *const _ as _) } unsafe { core::intrinsics::volatile_load($var_ref as *const _ as _) }
}; };