mirror of
https://github.com/rcore-os/rCore-Tutorial-v3.git
synced 2024-11-22 09:26:26 +04:00
user: add critical test for software-synchronous tests
This commit is contained in:
parent
97fdd4f2a2
commit
045c47e4ef
@ -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.");
|
||||||
|
@ -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.");
|
||||||
|
@ -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 _) }
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user