diff --git a/.cargo/config.toml b/.cargo/config.toml index a50fdf1a7952230fbf22384cc06ba60456172079..f00fc8041886a2bf36ed72a2f46a87575cd56cbb 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,5 +1,5 @@ # Mircrosoft Visual Studio 目标配置(ARM平台不需要) -[target.x86_64-pc-windows-msvc.arch] +[target.x86_64-pc-windows-msvc] rustflags = [ # +crt_static Indicates a static connection to the C runtime library # -crt_static Indicates dynamic connection to the C runtime library @@ -32,14 +32,7 @@ rustflags = [ "WIN10", ] -[nightly-x86_64-unknown-linux-gnu] -rustflags = [ - # Designated as win10 system. Some functions of the system below win10 cannot be used. - "--cfg", - "WIN10", -] - -[x86_64-unknown-linux-gnu] +[target.x86_64-unknown-linux-gnu] rustflags = [ # Designated as win10 system. Some functions of the system below win10 cannot be used. "--cfg", @@ -56,8 +49,12 @@ rustflags = [ 'target_os="liteos_m"', # Specify LiteOS location "--cfg", - 'LOS_PATH="../../stm32/LiteOS"', + 'LOS_PATH="../../../c/LiteOS"', + "--cfg", + "single_thread", ] [build] -target="thumbv7m-none-eabi" \ No newline at end of file +# target = "x86_64-pc-windows-msvc" +target = "x86_64-unknown-linux-gnu" +# target = "thumbv7m-none-eabi" diff --git a/.gitignore b/.gitignore index 4fffb2f89cbd8f2169ce9914bd16bd43785bb368..40c0b10c2aeff1565f19b85570ba0450b2d87af4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /Cargo.lock +src/sysinfo.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 513cbeb9d40d194855d83ea90af0123f31f55eba..9232241c50be5ce77c8a0bf13b8c85aea5d31864 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,8 @@ { - "rust-analyzer.checkOnSave.allTargets": false + "lldb.displayFormat": "auto", + "lldb.showDisassembly": "auto", + "lldb.dereferencePointers": true, + "lldb.consoleMode": "commands", + "editor.formatOnPaste": true, + "editor.formatOnSave": true } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000000000000000000000000000000000..013c2afb9686fa4f74fa11b0a69e4689ac98b39e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,17 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "cargo", + "command": "build", + "args": [ + "--examples" + ], + "problemMatcher": [ + "$rustc" + ], + "group": "build", + "label": "build stdlib stdlib" + } + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index d8692d0ddce07498bd2858edffc327fa4856120e..091fb5a36693a3a9c4a255fb0df3b3434afb2a39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,19 +5,24 @@ version = "0.1.0" [lib] crate-type = ["rlib"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["alloc"] alloc = ["arch"] std = ["parking"] + [dependencies] -arch = { git = "https://gitee.com/iot-ua/arch.git", optional = true,branch ="develop"} +utils = { git = "https://gitee.com/iot-ua/utils.git", branch = "develop" } +arch = { git = "https://gitee.com/iot-ua/arch.git", branch = "develop", optional = true } parking = { version = "2.0.0", optional = true } cfg-if = "1.0.0" cache-padded = "1.2.0" +[build-dependencies] +regex = "1.5.6" +num_cpus = "1.13.1" + [target.'cfg(unix)'.dev-dependencies] libc = { version = "0.2.126", default-features = false } @@ -36,11 +41,25 @@ windows-sys = { version = "0.36.1", features = [ "Win32_System_SystemServices", ] } -[[example]] -name="led" -crate-type=["staticlib"] [profile.dev] -panic = "abort" +opt-level = 2 +debug = true +rpath = false +lto = false +debug-assertions = false +codegen-units = 1 +panic = 'abort' +incremental = false +overflow-checks = false + [profile.release] -panic = "abort" \ No newline at end of file +opt-level = 3 +debug = false +rpath = false +lto = true +debug-assertions = false +codegen-units = 1 +panic = 'abort' +incremental = false +overflow-checks = false diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..fa3a28318cca8d633f4309f8e886bc23848a20f8 --- /dev/null +++ b/build.rs @@ -0,0 +1,35 @@ +use std::{env, ffi::OsStr, fs::File, io::Write, path::PathBuf, str::FromStr}; +fn main() { + //当前构建文件所在位置 + let manifest_path = env::var("CARGO_MANIFEST_DIR").unwrap(); + let manifest_path = PathBuf::from_str(&manifest_path).unwrap(); + //根据项目输入位置,推导项目根文件夹实质位置。 + let out_dir = env::var("OUT_DIR").unwrap(); + let out_path = PathBuf::from_str(&out_dir).unwrap(); + let mut top_path = PathBuf::new(); + for i in out_path.iter() { + if i == OsStr::new("target") { + break; + } + top_path.push(i); + } + //是否配置位置LITEOS,详细参见编译配置文件 ".cargo/config.toml 中的编译选项: "--cfg", "liteos" + if env::var("CARGO_CFG_LITEOS").is_err() { + let mut fcfg = File::options() + .write(true) + .create(true) + .truncate(true) + .open(manifest_path.join("src/sysinfo.rs")) + .unwrap(); + let cpus = num_cpus::get(); + + write!(fcfg,"//The following content is automatically generated by the compiler.\n//please do not modify it.\n").unwrap(); + + write!(fcfg, "pub const CPU_CORE_NUMBERS: usize = {}usize;", cpus).unwrap(); + } + println!("cargo:rerun-if-changed=build.rs"); + println!( + "cargo:rerun-if-changed={}", + top_path.join(".cargo/config.toml").to_str().unwrap() + ); +} diff --git a/examples/liteos/mod.rs b/examples/liteos/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..29eca78453888dc694c4241dd5aa39a8d1bc91b4 --- /dev/null +++ b/examples/liteos/mod.rs @@ -0,0 +1,4 @@ +mod parker; +pub use parker::*; +mod mutex; +pub use mutex::*; diff --git a/examples/liteos/mutex.rs b/examples/liteos/mutex.rs new file mode 100644 index 0000000000000000000000000000000000000000..70124c3e5f128ab964d6436ced0ca0dd3bc50096 --- /dev/null +++ b/examples/liteos/mutex.rs @@ -0,0 +1,18 @@ +use alloc::sync::Arc; +use stdlib::sync::Mutex; +use stdlib::thread; +pub fn mutex_test() { + let mutex = Arc::new(Mutex::new(32)); + let thmutex = mutex.clone(); + let mut mm = mutex.lock(); + *mm = 35; + let th = thread::spawn(move || { + let mut mm = thmutex.lock(); + *mm = 46; + }); + thread::sleep(10000); + drop(mm); + th.join(); + let mm = mutex.lock(); + assert_eq!(*mm, 46); +} diff --git a/examples/liteos/parker.rs b/examples/liteos/parker.rs new file mode 100644 index 0000000000000000000000000000000000000000..a60f2f7afe2d8fb13f9c3368ae86aed438048d31 --- /dev/null +++ b/examples/liteos/parker.rs @@ -0,0 +1,47 @@ +use stdlib::{println, sync::parker::*, thread}; + +pub fn park_timeout_unpark_before() { + let p = Parker::new(); + for _ in 0..10 { + p.unparker().unpark(); + p.park_timeout(u32::MAX); + } +} + +pub fn park_timeout_unpark_not_called() { + let p = Parker::new(); + for _ in 0..10 { + p.park_timeout(10); + } +} + +pub fn park_timeout_unpark_called_other_thread() { + for _ in 0..5 { + let p = Parker::new(); + let u = p.unparker(); + thread::spawn(move || { + thread::sleep(50); + u.unpark(); + }); + thread::spawn(move || { + p.park_timeout(u32::MAX); + }); + } +} + +pub fn parker_driver_led() { + let (p, u) = pair(); + thread::spawn(move || { + for _ in 0..10 { + p.park(); + unsafe { + crate::led_toggle(); + println!("Ok!"); + }; + } + }); + for _ in 0..10 { + thread::sleep(500); + u.unpark(); + } +} diff --git a/examples/test_condvar.rs b/examples/test_condvar.rs index 33a9a33ec20bfd411fc78b742ea2d81f041bb159..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/examples/test_condvar.rs +++ b/examples/test_condvar.rs @@ -1,31 +0,0 @@ -#![cfg_attr(liteos, no_std)] - -extern crate alloc; -use alloc::string::ToString; - -#[no_mangle] -extern "C" fn app_init() { - main(); -} -fn main() { - use stdlib::thread::{self, *}; - - sleep(100); - let a = Builder::new() - .name("asdf".to_string()) - .stack_size(1024) - .spawn(|| { - thread::yield_now(); - thread::sleep(100); - }) - .unwrap(); - - a.join(); - let b = thread::spawn(|| 55); - let _c = b.is_finished(); - let d = b.thread(); - let _e = d.name(); - - let f = b.join(); - assert_eq!(f, 55); -} diff --git a/examples/test_liteos.rs b/examples/test_liteos.rs new file mode 100644 index 0000000000000000000000000000000000000000..cd15257b554bf557cd4e67c3a2a37e5e3c738b22 --- /dev/null +++ b/examples/test_liteos.rs @@ -0,0 +1,44 @@ +#![no_std] +#![feature(default_alloc_error_handler)] +#![feature(panic_info_message)] + +#[allow(unused_imports)] +#[macro_use] +extern crate alloc; +use alloc::string::ToString; +use stdlib::println; + +#[allow(unused)] +extern "C" { + fn led_toggle(); + fn DemoEntry(); +} + +// #[cfg(not(test))] +#[panic_handler] +pub fn painc_handler(info: &core::panic::PanicInfo<'_>) -> ! { + let s = info.to_string(); + println!("{}", s); + loop {} +} + +mod liteos; +use liteos::*; +#[no_mangle] +unsafe extern "C" fn app_init() { + DemoEntry(); + println!("-----------------------------------------"); + println!("{}", "Test park_timeout_unpark_before start..."); + park_timeout_unpark_before(); + println!("{}", "Test park_timeout_unpark_not_called start..."); + park_timeout_unpark_not_called(); + println!( + "{}", + "Test park_timeout_unpark_called_other_thread start..." + ); + park_timeout_unpark_called_other_thread(); + println!("{}", "Test parker_driver_led start..."); + parker_driver_led(); + println!("{}", "Test Mutex start..."); + mutex_test(); +} diff --git a/examples/test_mutex.rs b/examples/test_mutex.rs index 608ac2803099a3368d67be90b7c5c400ff210b9e..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/examples/test_mutex.rs +++ b/examples/test_mutex.rs @@ -1,31 +0,0 @@ -#![cfg_attr(liteos, no_std)] - -extern crate alloc; - -#[no_mangle] -extern "C" fn app_init() { - main(); -} - -#[cfg(not(liteos))] -fn main() { - use stdlib::sync::Mutex; - let mut mt = Mutex::new(0); - let _abc = mt.get_mut(); - *_abc = 22; - let mut t = mt.lock(); - - *t = 45; - let tt = mt.try_lock(); - assert!(tt.is_none()); - drop(tt); - Mutex::unlock(t); - let tt = mt.try_lock(); - assert!(tt.is_some()); - drop(tt); - let v = mt.into_inner(); - assert_eq!(v, 45); -} - -#[cfg(liteos)] -fn main() {} diff --git a/src/lib.rs b/src/lib.rs index 9949093bec5cf5645c46c73c3027c31db78f4611..65b34bcb8d26703c6c3ac171ed443dcd2fbdd9cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,6 +33,8 @@ #![feature(once_cell)] #![feature(dropck_eyepatch)] #![feature(stmt_expr_attributes)] +#![feature(atomic_mut_ptr)] +#![feature(let_chains)] #[cfg(all(feature = "std", feature = "alloc"))] compile_error!("std and alloc features cannot be enabled at same time."); @@ -47,10 +49,17 @@ cfg_if! { mod std; pub use crate::std::*; pub mod anyhow; + mod sysinfo; + pub mod os{ + pub use super::sysinfo::CPU_CORE_NUMBERS; + } }else if #[cfg(all(feature="alloc",not(feature="std")))]{ #[allow(unused_imports)] mod no_std; pub use no_std::*; pub mod anyhow; + pub mod os{ + pub const CPU_CORE_NUMBERS:usize=arch::os::CPU_CORE_NUMBERS; + } } } diff --git a/src/no_std/collections/objpool.rs b/src/no_std/collections/objpool.rs index 37f9d75814ccea5dfbc285eb22eff708a9709f09..ba21022aae96cd13ea83c4f9f90ae85075641c2f 100644 --- a/src/no_std/collections/objpool.rs +++ b/src/no_std/collections/objpool.rs @@ -1,5 +1,6 @@ -use crate::sync::Parker; -use crate::thread; +#[cfg(all(test, not(target_os = "none")))] +mod tests; +use crate::sync::parker::Parker; use alloc::alloc::{alloc, dealloc}; use core::{ alloc::Layout, @@ -349,66 +350,3 @@ impl Drop for ObjPool { } } } - -#[cfg(test)] -mod test { - - use super::Obj; - use alloc::format; - use alloc::sync::Arc; - use alloc::vec::Vec; - use core::mem::MaybeUninit; - struct TestDrop(usize); - impl Drop for TestDrop { - fn drop(&mut self) { - // println!("Droped\n\0"); - } - } - impl Default for TestDrop { - fn default() -> Self { - Self(0) - } - } - use crate::thread; - - use super::ObjPool; - - static POOL: ObjPool = ObjPool::new(); - - #[test] - fn test_new() { - for _ in 0..50000 { - let pool = Arc::new(ObjPool::::new()); - let mut threads = Vec::new(); - let c = unsafe { POOL.get_uninit() }; - let m = unsafe { c.as_mut() }; - m.0 = 44; - drop(c); - - for i in 0..8 { - let pool = pool.clone(); - let th = thread::Builder::new() - .name(format!("thread-{}", i)) - .stack_size(1024 * 1024) - .spawn(move || { - let mut q: [Option>; 100] = - unsafe { core::mem::zeroed() }; - for _ in 0..10 { - for i in 0..20 { - q[i] = Some(pool.get(TestDrop(10))); - let i = q[i].as_ref().take().unwrap(); - unsafe { i.as_mut().0 = 55 }; - } - } - }) - .unwrap(); - threads.push(th); - } - - for th in threads.drain(..) { - th.join(); - } - } - assert_eq!(1, 1); - } -} diff --git a/src/no_std/collections/objpool/tests.rs b/src/no_std/collections/objpool/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..e909de3c8f070fdbb8d20db74c40f71cbeb95b92 --- /dev/null +++ b/src/no_std/collections/objpool/tests.rs @@ -0,0 +1,56 @@ +use super::Obj; +use alloc::format; +use alloc::sync::Arc; +use alloc::vec::Vec; +struct TestDrop(usize); +impl Drop for TestDrop { + fn drop(&mut self) { + // println!("Droped\n\0"); + } +} +impl Default for TestDrop { + fn default() -> Self { + Self(0) + } +} +use crate::thread; + +use super::ObjPool; + +static POOL: ObjPool = ObjPool::new(); + +#[test] +fn test_new() { + for _ in 0..50000 { + let pool = Arc::new(ObjPool::::new()); + let mut threads = Vec::new(); + let c = unsafe { POOL.get_uninit() }; + let m = unsafe { c.as_mut() }; + m.0 = 44; + drop(c); + + for i in 0..8 { + let pool = pool.clone(); + let th = thread::Builder::new() + .name(format!("thread-{}", i)) + .stack_size(1024 * 1024) + .spawn(move || { + let mut q: [Option>; 100] = unsafe { core::mem::zeroed() }; + for _ in 0..10 { + for i in 0..20 { + q[i] = Some(pool.get(TestDrop(10))); + let i = q[i].as_ref().take().unwrap(); + unsafe { i.as_mut().0 = 55 }; + } + } + }) + .unwrap(); + threads.push(th); + } + + for th in threads.drain(..) { + th.join(); + } + } + assert_eq!(1, 1); +} diff --git a/src/no_std/collections/unbounded.rs b/src/no_std/collections/unbounded.rs index 973e2b28bd0e946ee703ca6bd24609ba648497e8..bf82f337299abcdc9c327606c85bd06a04e07690 100644 --- a/src/no_std/collections/unbounded.rs +++ b/src/no_std/collections/unbounded.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, not(target_os = "none")))] mod tests; use crate::thread; use alloc::boxed::Box; diff --git a/src/no_std/mod.rs b/src/no_std/mod.rs index 1d514c8b660cf4330b9cf808e7ad329863f7d9c2..df9462431120059e986276e76ebdcea3fb1f29c2 100644 --- a/src/no_std/mod.rs +++ b/src/no_std/mod.rs @@ -1,18 +1,20 @@ -#[cfg(unix)] pub mod collections; pub mod error; pub mod io; -#[cfg(unix)] pub mod lazy; -#[cfg(unix)] pub mod sync; pub mod thread; -#[macro_export] -macro_rules! println { +pub use arch::os::printf; +pub macro println{ () => { - //TODO 实现平台相关的打印输出。 - }; - ($($arg:tt)*) => { - //TODO 实现平台相关的打印输出。 - }; + unsafe{ $crate::printf("\n\0") } + }, + ($fmt:tt)=>{ + let s=concat!($fmt,"\n\0"); + unsafe{ $crate::printf(s.as_ptr().cast()) } + }, + ($fmt:tt,$($arg:tt)*) => { + let s= alloc::format!(core::concat!($fmt, "\n\0"),$($arg)*); + unsafe{ $crate::printf(s.as_ptr().cast()) } + } } diff --git a/src/no_std/sync/mod.rs b/src/no_std/sync/mod.rs index d647b0cf206fed4a15f410746eac6226a0a02635..472756d3e1096672b84be95021fa0105d106285f 100644 --- a/src/no_std/sync/mod.rs +++ b/src/no_std/sync/mod.rs @@ -1,11 +1,19 @@ +#[cfg(unix)] mod condvar; + +#[cfg(unix)] +pub use condvar::Condvar; +#[allow(dead_code)] mod mutex; +pub use mutex::{Mutex, MutexGuard}; + mod once; +pub mod parker; +#[cfg(unix)] mod rwlock; mod spinlock; -pub use arch::locks::Parker; -pub use condvar::Condvar; -pub use mutex::{Mutex, MutexGuard}; + pub use once::{Once, OnceState}; +#[cfg(unix)] pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; pub use spinlock::*; diff --git a/src/no_std/sync/mutex.rs b/src/no_std/sync/mutex.rs index 3d739d7d23452b7abd4dcb44657545c75ed6921e..c07e6704c30bcc28229e138e900874c6dafaa1fe 100644 --- a/src/no_std/sync/mutex.rs +++ b/src/no_std/sync/mutex.rs @@ -1,4 +1,4 @@ -#[cfg(test)] +#[cfg(all(test, not(target_os = "none")))] mod tests; use arch::locks::MovableMutex as imp; use core::cell::UnsafeCell; @@ -6,10 +6,9 @@ use core::ops::Deref; use core::ops::DerefMut; pub(super) struct MovableMutex(imp); impl MovableMutex { - fn new() -> Self { - let mut m = imp::from(arch::locks::Mutex::new()); - unsafe { m.init() }; - Self(m) + #[inline] + const fn new() -> Self { + Self(imp::new()) } pub(super) fn raw(&self) -> &imp { &self.0 @@ -27,12 +26,6 @@ impl MovableMutex { self.0.unlock() } } -impl Drop for MovableMutex { - #[inline] - fn drop(&mut self) { - unsafe { self.0.destroy() }; - } -} pub struct Mutex { inner: MovableMutex, @@ -41,7 +34,7 @@ pub struct Mutex { unsafe impl Send for Mutex {} unsafe impl Sync for Mutex {} impl Mutex { - pub fn new(t: T) -> Mutex { + pub const fn new(t: T) -> Mutex { Mutex { inner: MovableMutex::new(), data: UnsafeCell::new(t), diff --git a/src/no_std/sync/mutex/tests.rs b/src/no_std/sync/mutex/tests.rs index 8b137891791fe96927ad78e64b0aad7bded08bdc..3aa80411ba0d14142813bc80ee49f504f4422c42 100644 --- a/src/no_std/sync/mutex/tests.rs +++ b/src/no_std/sync/mutex/tests.rs @@ -1 +1,40 @@ +extern crate std; +use crate::sync::mutex::Mutex; +use crate::thread; +use std::sync::mpsc::channel; +use std::sync::Arc; +#[test] +fn lots_and_lots() { + const J: u32 = 1000; + const K: u32 = 3; + let m = Arc::new(Mutex::new(0)); + + fn inc(m: &Mutex) { + for _ in 0..J { + *m.lock() += 1; + } + } + + let (tx, rx) = channel(); + for _ in 0..K { + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + } + + drop(tx); + for _ in 0..2 * K { + rx.recv().unwrap(); + } + assert_eq!(*m.lock(), J * K * 2); +} diff --git a/src/no_std/sync/once.rs b/src/no_std/sync/once.rs index 3fdf398e6f8762c32ffca6969ed16cb9f6ce5c3c..ccfe02864616e3c2475010e513d831825b6fb13a 100644 --- a/src/no_std/sync/once.rs +++ b/src/no_std/sync/once.rs @@ -1,7 +1,10 @@ -#[cfg(test)] +#[cfg(all(test, not(target_os = "none")))] mod tests; -use crate::sync::Parker; +use arch::locks::parker::*; + +use arch::os::CPU_CORE_NUMBERS; + use crate::thread::{self, Thread}; use core::cell::Cell; use core::fmt; @@ -163,9 +166,16 @@ impl Once { fn wait(state_and_queue: &AtomicPtr, mut current_state: *mut Masked) { // Note: the following code was carefully written to avoid creating a // mutable reference to `node` that gets aliased. + let mut cnt = 1000i32; loop { // Don't queue this thread if the status is no longer running, // otherwise we will not be woken up. + if CPU_CORE_NUMBERS > 1 && current_state.addr() & STATE_MASK == RUNNING && cnt > 0 { + current_state = state_and_queue.load(Ordering::Acquire); + cnt -= 1; + core::hint::spin_loop(); + continue; + } if current_state.addr() & STATE_MASK != RUNNING { return; } @@ -201,7 +211,7 @@ fn wait(state_and_queue: &AtomicPtr, mut current_state: *mut Masked) { // can park ourselves, the result could be this thread never gets // unparked. Luckily `park` comes with the guarantee that if it got // an `unpark` just before on an unparked thread it does not park. - unsafe { node.parker.park() }; + node.parker.park(); } break; } diff --git a/src/no_std/sync/once/tests.rs b/src/no_std/sync/once/tests.rs index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bed344cd24ae8ee4c05a4f152e6517daa05e563a 100644 --- a/src/no_std/sync/once/tests.rs +++ b/src/no_std/sync/once/tests.rs @@ -0,0 +1,89 @@ +extern crate std; +use super::Once; +use crate::thread; +use std::panic; +use std::sync::mpsc::channel; +#[test] +fn smoke_once() { + static O: Once = Once::new(); + let mut a = 0; + O.call_once(|| a += 1); + assert_eq!(a, 1); + O.call_once(|| a += 1); + assert_eq!(a, 1); +} +#[test] +fn stampede_once() { + static O: Once = Once::new(); + static mut RUN: bool = false; + + let (tx, rx) = channel(); + for _ in 0..100 { + let tx = tx.clone(); + thread::spawn(move || { + for _ in 0..4 { + thread::yield_now() + } + unsafe { + O.call_once(|| { + assert!(!RUN); + RUN = true; + }); + assert!(RUN); + } + tx.send(()).unwrap(); + }); + } + + unsafe { + O.call_once(|| { + assert!(!RUN); + RUN = true; + }); + assert!(RUN); + } + + for _ in 0..10 { + rx.recv().unwrap(); + } +} + +#[test] +fn wait_for_force_to_finish() { + static O: Once = Once::new(); + + // poison the once + let t = panic::catch_unwind(|| { + O.call_once(|| panic!()); + }); + assert!(t.is_err()); + + // make sure someone's waiting inside the once via a force + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + let t1 = thread::spawn(move || { + O.call_once_force(|p| { + assert!(p.is_poisoned()); + tx1.send(()).unwrap(); + rx2.recv().unwrap(); + }); + true + }); + + rx1.recv().unwrap(); + + // put another waiter on the once + let t2 = thread::spawn(|| { + let mut called = false; + O.call_once(|| { + called = true; + }); + assert!(!called); + true + }); + + tx2.send(()).unwrap(); + + assert!(t1.join()); + assert!(t2.join()); +} diff --git a/src/no_std/sync/parker.rs b/src/no_std/sync/parker.rs new file mode 100644 index 0000000000000000000000000000000000000000..fd6927c9c302855afc8911c01936b14e0e5ae731 --- /dev/null +++ b/src/no_std/sync/parker.rs @@ -0,0 +1 @@ +pub use arch::locks::parker::*; diff --git a/src/no_std/sync/spinlock.rs b/src/no_std/sync/spinlock.rs index 79709504171c0d29e9aad8cb937b1cb4bdf879d5..6f169746e28bfc6c872077079cfe72f144d90737 100644 --- a/src/no_std/sync/spinlock.rs +++ b/src/no_std/sync/spinlock.rs @@ -1,3 +1,5 @@ +use crate::os::CPU_CORE_NUMBERS; +use crate::thread::yield_now; use core::{ cell::UnsafeCell, hint::spin_loop, @@ -37,7 +39,11 @@ impl Spinlock { self.lock .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) { - spin_loop(); + if CPU_CORE_NUMBERS > 1 { + spin_loop(); + } else { + yield_now(); + } } } pub fn lock(&self) -> SpinlockGuard { diff --git a/src/no_std/thread.rs b/src/no_std/thread.rs index a06fcd9e995a24da91f9c32982a5ab27e6203b71..9bc8ba7a9333f51442e844200c9ce76a82219ac1 100644 --- a/src/no_std/thread.rs +++ b/src/no_std/thread.rs @@ -1,4 +1,4 @@ -#[cfg(all(test,not(target_os="none")))] +#[cfg(all(test, not(target_os = "none")))] mod tests; use crate::io::Result; use alloc::{boxed::Box, string::String, sync::Arc}; diff --git a/src/std/sync/condvar.rs b/src/std/sync/condvar.rs index d91d424ec9c8c83de474bfc6b8e082048e01afe5..b22a1e5016e43c73393160726e6d15b649cf056a 100644 --- a/src/std/sync/condvar.rs +++ b/src/std/sync/condvar.rs @@ -11,7 +11,7 @@ pub struct Condvar(imp); impl Condvar { #[must_use] #[inline] - pub fn new() -> Condvar { + pub const fn new() -> Condvar { Condvar(imp::new()) } diff --git a/src/std/sync/mod.rs b/src/std/sync/mod.rs index 2a7fce9efb1eafd633d49db3226f37ae1168fafa..00d0b9ca5b17aff6976c62bcc8dcafbcedb4d086 100644 --- a/src/std/sync/mod.rs +++ b/src/std/sync/mod.rs @@ -7,6 +7,5 @@ pub use rwlock::*; #[path = "../../no_std/sync/spinlock.rs"] mod spinlock; pub use spinlock::*; -mod parker; -pub use parker::*; +pub mod parker; pub use std::sync::{Once, OnceState}; diff --git a/src/std/sync/mutex.rs b/src/std/sync/mutex.rs index ddc6af0c50bc840dcf0705e788da69f8adc3a746..346e6c630073bb187565be116d11d95a7fa4c86c 100644 --- a/src/std/sync/mutex.rs +++ b/src/std/sync/mutex.rs @@ -6,7 +6,7 @@ pub use std::sync::MutexGuard; pub struct Mutex(imp); impl Mutex { - pub fn new(t: T) -> Mutex { + pub const fn new(t: T) -> Mutex { Self(imp::new(t)) } } diff --git a/src/std/sync/parker.rs b/src/std/sync/parker.rs index e253d32feffa1f66a33342ffbc7493315f867a0a..c5610b38ad5e158bcf9ae96174a096f31dbcdaba 100644 --- a/src/std/sync/parker.rs +++ b/src/std/sync/parker.rs @@ -1,7 +1,33 @@ -pub use parking::Parker; -#[test] -fn test() { - let park = Parker::new(); - park.park(); - park.unpark(); +use core::time::Duration; + +use crate::std::ExtDuration; + +pub struct Parker(parking::Parker); +pub use parking::Unparker; +impl Parker { + #[inline] + pub fn new() -> Self { + Self(parking::Parker::new()) + } + #[inline] + pub fn park(&self) { + self.0.park(); + } + #[inline] + pub fn park_timeout(&self, ms: u32) -> bool { + self.0.park_timeout(Duration::saturating_from_millisec(ms)) + } + #[inline] + pub fn unpark(&self) { + self.0.unpark(); + } + #[inline] + pub fn unparker(&self) -> Unparker { + self.0.unparker() + } +} +pub fn pair() -> (Parker, Unparker) { + let p = Parker::new(); + let u = p.unparker(); + (p, u) }