From 045c47e4efdec3a5bc4af803370e2627a039cc9a Mon Sep 17 00:00:00 2001 From: DeathWish5 Date: Thu, 23 Dec 2021 15:25:06 +0800 Subject: [PATCH] user: add critical test for software-synchronous tests --- user/src/bin/eisenberg.rs | 37 ++++++++++++++++++++++++++--------- user/src/bin/peterson.rs | 41 ++++++++++++++++++++++++++++----------- user/src/lib.rs | 4 ++-- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/user/src/bin/eisenberg.rs b/user/src/bin/eisenberg.rs index 1ebb2d28..f01ea1a2 100644 --- a/user/src/bin/eisenberg.rs +++ b/user/src/bin/eisenberg.rs @@ -9,6 +9,7 @@ extern crate core; use user_lib::{thread_create, waittid, exit, sleep}; use alloc::vec::Vec; +use core::sync::atomic::{AtomicUsize, Ordering}; const N: usize = 2; const THREAD_NUM: usize = 10; @@ -21,19 +22,33 @@ enum FlagState { static mut TURN: usize = 0; 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) { /* announce that we want to enter */ loop { println!("Thread[{}] try enter", id); - store!(&FLAG[id], FlagState::Want); + vstore!(&FLAG[id], FlagState::Want); loop { /* check if any with higher priority is `Want` or `In` */ let mut prior_thread:Option = None; - let turn = load!(&TURN); + let turn = vload!(&TURN); let ring_id = if id < turn { id + THREAD_NUM } else { id }; // FLAG.iter() may lead to some errors, use for-loop instead 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); break; } @@ -46,13 +61,13 @@ fn eisenberg_enter_critical(id: usize) { sleep(1); } /* now tentatively claim the resource */ - store!(&FLAG[id], FlagState::In); + vstore!(&FLAG[id], FlagState::In); /* enforce the order of `claim` and `conflict check`*/ memory_fence!(); /* check if anthor thread is also `In`, which imply a conflict*/ let mut conflict = false; for i in 0..THREAD_NUM { - if i != id && load!(&FLAG[i]) == FlagState::In { + if i != id && vload!(&FLAG[i]) == FlagState::In { conflict = true; } } @@ -63,7 +78,7 @@ fn eisenberg_enter_critical(id: usize) { /* no need to sleep */ } /* clain the trun */ - store!(&TURN, id); + vstore!(&TURN, id); println!("Thread[{}] enter", id); } @@ -73,14 +88,14 @@ fn eisenberg_exit_critical(id: usize) { let ring_id = id + THREAD_NUM; for i in (id+1)..ring_id { let idx = i % THREAD_NUM; - if load!(&FLAG[idx]) == FlagState::Want { + if vload!(&FLAG[idx]) == FlagState::Want { next = idx; break; } } - store!(&TURN, next); + vstore!(&TURN, next); /* All done */ - store!(&FLAG[id], FlagState::Out); + vstore!(&FLAG[id], FlagState::Out); println!("Thread[{}] exit, give turn to {}", id, next); } @@ -88,9 +103,12 @@ pub fn thread_fn(id: usize) -> ! { println!("Thread[{}] init.", id); for _ in 0..N { eisenberg_enter_critical(id); + critical_test_enter(); for _ in 0..3 { + critical_test_claim(); sleep(2); } + critical_test_exit(); eisenberg_exit_critical(id); } exit(0) @@ -107,6 +125,7 @@ pub fn main() -> i32 { } for tid in v.iter() { let exit_code = waittid(*tid as usize); + assert_eq!(exit_code, 0, "thread conflict happened!"); println!("thread#{} exited with code {}", tid, exit_code); } println!("main thread exited."); diff --git a/user/src/bin/peterson.rs b/user/src/bin/peterson.rs index 11447ce9..21865e24 100644 --- a/user/src/bin/peterson.rs +++ b/user/src/bin/peterson.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] #![feature(core_intrinsics)] +#![feature(asm)] #[macro_use] extern crate user_lib; @@ -8,38 +9,55 @@ extern crate alloc; extern crate core; use user_lib::{thread_create, waittid, exit, sleep}; +use core::sync::atomic::{AtomicUsize, Ordering}; use alloc::vec::Vec; const N: usize = 3; static mut TURN: usize = 0; 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) { - println!("Thread {} try enter", id); - store!(&FLAG[id], true); - store!(&TURN, peer_id); + println!("Thread[{}] try enter", id); + vstore!(&FLAG[id], true); + vstore!(&TURN, peer_id); memory_fence!(); - while load!(&FLAG[peer_id]) && load!(&TURN) == peer_id { - println!("Thread {} enter fail", id); + while vload!(&FLAG[peer_id]) && vload!(&TURN) == peer_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) { - store!(&FLAG[id], false); - println!("Thread {} exit", id); + vstore!(&FLAG[id], false); + 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 { peterson_enter_critical(id, peer_id); + critical_test_enter(); for _ in 0..3 { + critical_test_claim(); sleep(2); } + critical_test_exit(); peterson_exit_critical(id); } exit(0) @@ -49,9 +67,10 @@ pub fn thread_fn(id: usize) -> ! { pub fn main() -> i32 { let mut v = Vec::new(); 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() { let exit_code = waittid(*tid as usize); + assert_eq!(exit_code, 0, "thread conflict happened!"); println!("thread#{} exited with code {}", tid, exit_code); } println!("main thread exited."); diff --git a/user/src/lib.rs b/user/src/lib.rs index c1a47a15..816cca38 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -131,14 +131,14 @@ pub fn semaphore_down(sem_id: usize) { } #[macro_export] -macro_rules! store { +macro_rules! vstore { ($var_ref: expr, $value: expr) => { unsafe { core::intrinsics::volatile_store($var_ref as *const _ as _, $value) } }; } #[macro_export] -macro_rules! load { +macro_rules! vload { ($var_ref: expr) => { unsafe { core::intrinsics::volatile_load($var_ref as *const _ as _) } };