From ef3f87d31bcb5ded969c81f3cea2f32e888505dd Mon Sep 17 00:00:00 2001 From: DeathWish5 Date: Wed, 22 Dec 2021 23:35:34 +0800 Subject: [PATCH 1/2] user: add peterson algorithm and Eisenberg & McGuire algorithm --- user/src/bin/eisenberg.rs | 114 ++++++++++++++++++++++++++++++++++++++ user/src/bin/peterson.rs | 59 ++++++++++++++++++++ user/src/lib.rs | 21 +++++++ 3 files changed, 194 insertions(+) create mode 100644 user/src/bin/eisenberg.rs create mode 100644 user/src/bin/peterson.rs diff --git a/user/src/bin/eisenberg.rs b/user/src/bin/eisenberg.rs new file mode 100644 index 0000000..1ebb2d2 --- /dev/null +++ b/user/src/bin/eisenberg.rs @@ -0,0 +1,114 @@ +#![no_std] +#![no_main] +#![feature(core_intrinsics)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; +extern crate core; + +use user_lib::{thread_create, waittid, exit, sleep}; +use alloc::vec::Vec; + +const N: usize = 2; +const THREAD_NUM: usize = 10; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum FlagState { + Out, Want, In, +} + +static mut TURN: usize = 0; +static mut FLAG: [FlagState; THREAD_NUM] = [FlagState::Out; THREAD_NUM]; + +fn eisenberg_enter_critical(id: usize) { + /* announce that we want to enter */ + loop { + println!("Thread[{}] try enter", id); + store!(&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 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 { + prior_thread = Some(i % THREAD_NUM); + break; + } + } + if prior_thread.is_none() { + break; + } + println!("Thread[{}]: prior thread {} exist, sleep and retry", + id, prior_thread.unwrap()); + sleep(1); + } + /* now tentatively claim the resource */ + store!(&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 { + conflict = true; + } + } + if !conflict { + break; + } + println!("Thread[{}]: CONFLECT!", id); + /* no need to sleep */ + } + /* clain the trun */ + store!(&TURN, id); + println!("Thread[{}] enter", id); +} + +fn eisenberg_exit_critical(id: usize) { + /* find next one who wants to enter and give the turn to it*/ + let mut next = id; + let ring_id = id + THREAD_NUM; + for i in (id+1)..ring_id { + let idx = i % THREAD_NUM; + if load!(&FLAG[idx]) == FlagState::Want { + next = idx; + break; + } + } + store!(&TURN, next); + /* All done */ + store!(&FLAG[id], FlagState::Out); + println!("Thread[{}] exit, give turn to {}", id, next); +} + +pub fn thread_fn(id: usize) -> ! { + println!("Thread[{}] init.", id); + for _ in 0..N { + eisenberg_enter_critical(id); + for _ in 0..3 { + sleep(2); + } + eisenberg_exit_critical(id); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + // TODO: really shuffle + assert_eq!(THREAD_NUM, 10); + let shuffle:[usize; 10] = [0, 7, 4, 6, 2, 9, 8, 1, 3, 5]; + for i in 0..THREAD_NUM { + v.push(thread_create(thread_fn as usize, shuffle[i])); + } + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} \ No newline at end of file diff --git a/user/src/bin/peterson.rs b/user/src/bin/peterson.rs new file mode 100644 index 0000000..11447ce --- /dev/null +++ b/user/src/bin/peterson.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] +#![feature(core_intrinsics)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; +extern crate core; + +use user_lib::{thread_create, waittid, exit, sleep}; +use alloc::vec::Vec; +const N: usize = 3; + +static mut TURN: usize = 0; +static mut FLAG: [bool; 2] = [false; 2]; + +fn peterson_enter_critical(id: usize, peer_id: usize) { + println!("Thread {} try enter", id); + store!(&FLAG[id], true); + store!(&TURN, peer_id); + memory_fence!(); + while load!(&FLAG[peer_id]) && load!(&TURN) == peer_id { + println!("Thread {} enter fail", id); + sleep(1); + println!("Thread {} retry enter", id); + } + println!("Thread {} enter", id); +} + +fn peterson_exit_critical(id: usize) { + store!(&FLAG[id], false); + println!("Thread {} exit", id); +} + +pub fn thread_fn(id: usize) -> ! { + println!("Thread {} init.", id); + let peer_id: usize = id ^ 1; + for _ in 0..N { + peterson_enter_critical(id, peer_id); + for _ in 0..3 { + sleep(2); + } + peterson_exit_critical(id); + } + exit(0) +} + +#[no_mangle] +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)); + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} \ No newline at end of file diff --git a/user/src/lib.rs b/user/src/lib.rs index afde2ec..c1a47a1 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -3,6 +3,7 @@ #![feature(linkage)] #![feature(panic_info_message)] #![feature(alloc_error_handler)] +#![feature(core_intrinsics)] #[macro_use] pub mod console; @@ -129,3 +130,23 @@ pub fn semaphore_down(sem_id: usize) { sys_semaphore_down(sem_id); } +#[macro_export] +macro_rules! store { + ($var_ref: expr, $value: expr) => { + unsafe { core::intrinsics::volatile_store($var_ref as *const _ as _, $value) } + }; +} + +#[macro_export] +macro_rules! load { + ($var_ref: expr) => { + unsafe { core::intrinsics::volatile_load($var_ref as *const _ as _) } + }; +} + +#[macro_export] +macro_rules! memory_fence { + () => { + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst) + }; +} \ No newline at end of file From 4262726f0b1474fbfa6793da49fcfed1937f53fa Mon Sep 17 00:00:00 2001 From: DeathWish5 Date: Thu, 23 Dec 2021 15:25:06 +0800 Subject: [PATCH 2/2] 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 1ebb2d2..f01ea1a 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 11447ce..21865e2 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 c1a47a1..816cca3 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 _) } };