From 7659ad968208b893f67a2f758776479e9d3d9d5c Mon Sep 17 00:00:00 2001 From: Yuekai Jia Date: Sun, 23 Jul 2023 12:06:49 +0800 Subject: [PATCH] ulib: make `axstd` API more similar to rust std - now apps can be built with both std and ArceOS without modification --- .github/workflows/build.yml | 38 ++++ Cargo.lock | 5 +- apps/display/Cargo.toml | 2 +- apps/display/src/display.rs | 2 +- apps/display/src/main.rs | 11 +- apps/exception/Cargo.toml | 2 +- apps/exception/expect_debug_aarch64.out | 2 - apps/exception/expect_debug_riscv64.out | 2 - apps/exception/expect_debug_x86_64.out | 3 +- apps/exception/src/main.rs | 13 +- apps/fs/shell/Cargo.toml | 5 +- apps/fs/shell/src/cmd.rs | 108 +++++++---- apps/fs/shell/src/main.rs | 44 +++-- apps/fs/shell/src/ramfs.rs | 2 +- apps/helloworld/Cargo.toml | 2 +- apps/helloworld/src/main.rs | 11 +- apps/memtest/Cargo.toml | 3 +- apps/memtest/src/main.rs | 33 ++-- apps/memtest/test_cmd | 2 +- apps/net/bwbench/Cargo.toml | 4 +- apps/net/echoserver/Cargo.toml | 2 +- apps/net/echoserver/src/main.rs | 23 +-- apps/net/httpclient/Cargo.toml | 4 +- apps/net/httpclient/src/main.rs | 13 +- apps/net/httpserver/Cargo.toml | 2 +- apps/net/httpserver/src/main.rs | 28 ++- apps/net/udpserver/Cargo.toml | 2 +- apps/net/udpserver/src/main.rs | 13 +- apps/task/parallel/Cargo.toml | 3 +- apps/task/parallel/expect_info_smp1_fifo.out | 37 ++-- apps/task/parallel/expect_info_smp4_cfs.out | 67 ++++--- apps/task/parallel/expect_info_smp4_rr.out | 67 ++++--- apps/task/parallel/src/main.rs | 46 +++-- apps/task/priority/Cargo.toml | 2 +- apps/task/priority/expect_info_smp1_cfs.out | 21 +-- apps/task/priority/expect_info_smp1_fifo.out | 21 +-- apps/task/priority/expect_info_smp1_rr.out | 21 +-- apps/task/priority/expect_info_smp4_cfs.out | 21 +-- apps/task/priority/src/main.rs | 30 +-- apps/task/sleep/Cargo.toml | 2 +- apps/task/sleep/expect_info_smp4_fifo.out | 1 - apps/task/sleep/expect_info_smp4_rr.out | 1 - apps/task/sleep/src/main.rs | 19 +- apps/task/yield/Cargo.toml | 2 +- apps/task/yield/expect_debug_smp1_fifo.out | 54 ++++++ apps/task/yield/expect_info_smp1_fifo.out | 32 ---- apps/task/yield/expect_info_smp4_fifo.out | 21 +-- apps/task/yield/expect_info_smp4_rr.out | 21 +-- apps/task/yield/src/main.rs | 14 +- apps/task/yield/test_cmd | 2 +- crates/axfs_vfs/src/structs.rs | 35 +++- modules/axfs/src/fops.rs | 19 ++ modules/axlog/Cargo.toml | 3 + modules/axlog/src/lib.rs | 48 ++++- ulib/axlibc/Cargo.toml | 1 - ulib/axlibc/src/fd_ops.rs | 3 +- ulib/axlibc/src/lib.rs | 17 +- ulib/axlibc/src/pthread/mod.rs | 6 +- ulib/{axstd => axlibc}/src/rand.rs | 6 +- ulib/axlibc/src/stdio.rs | 15 +- ulib/axlibc/src/time.rs | 2 +- ulib/axstd/Cargo.toml | 1 + ulib/axstd/src/display/mod.rs | 15 -- ulib/axstd/src/env.rs | 17 +- ulib/axstd/src/io/mod.rs | 24 ++- ulib/axstd/src/io/stdio.rs | 158 +++++++++------- ulib/axstd/src/lib.rs | 42 ++--- ulib/axstd/src/macros.rs | 23 +++ ulib/axstd/src/os.rs | 11 ++ ulib/axstd/src/process.rs | 10 + ulib/axstd/src/sync/mod.rs | 12 +- ulib/axstd/src/thread/mod.rs | 173 ++--------------- ulib/axstd/src/thread/multi.rs | 187 +++++++++++++++++++ ulib/axstd/src/thread/single.rs | 39 ---- ulib/axstd/src/time.rs | 73 +++++++- 75 files changed, 1104 insertions(+), 722 deletions(-) create mode 100644 apps/task/yield/expect_debug_smp1_fifo.out delete mode 100644 apps/task/yield/expect_info_smp1_fifo.out rename ulib/{axstd => axlibc}/src/rand.rs (78%) delete mode 100644 ulib/axstd/src/display/mod.rs create mode 100644 ulib/axstd/src/macros.rs create mode 100644 ulib/axstd/src/os.rs create mode 100644 ulib/axstd/src/process.rs create mode 100644 ulib/axstd/src/thread/multi.rs delete mode 100644 ulib/axstd/src/thread/single.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cad350ec75..1d60fb3c52 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -91,3 +91,41 @@ jobs: run: make ARCH=${{ matrix.arch }} A=apps/c/udpserver - name: Build c/iperf run: make ARCH=${{ matrix.arch }} A=apps/c/iperf + + build-apps-for-std: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + arch: [x86_64] + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ env.rust-toolchain }} + - name: Build helloworld + run: cargo build -p arceos-helloworld + - name: Build memtest + run: cargo build -p arceos-memtest + - name: Build exception + run: cargo build -p arceos-exception + - name: Build task/yield + run: cargo build -p arceos-yield + - name: Build task/parallel + run: cargo build -p arceos-parallel + - name: Build task/sleep + run: cargo build -p arceos-sleep + - name: Build task/priority + run: cargo build -p arceos-priority + - name: Build fs/shell + run: cargo build -p arceos-shell + - name: Build net/echoserver + run: cargo build -p arceos-echoserver + - name: Build net/httpclient + run: cargo build -p arceos-httpclient + - name: Build net/httpserver + run: cargo build -p arceos-httpserver + - name: Build net/udpserver + run: cargo build -p arceos-udpserver diff --git a/Cargo.lock b/Cargo.lock index 121116c478..90d8eadd32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,6 +100,7 @@ name = "arceos-memtest" version = "0.1.0" dependencies = [ "axstd", + "rand", ] [[package]] @@ -107,6 +108,7 @@ name = "arceos-parallel" version = "0.1.0" dependencies = [ "axstd", + "rand", ] [[package]] @@ -120,7 +122,6 @@ dependencies = [ name = "arceos-shell" version = "0.1.0" dependencies = [ - "axfs", "axfs_ramfs", "axfs_vfs", "axstd", @@ -348,7 +349,6 @@ dependencies = [ "flatten_objects", "lazy_static", "spin 0.9.8", - "spinlock", "static_assertions", ] @@ -356,6 +356,7 @@ dependencies = [ name = "axlog" version = "0.1.0" dependencies = [ + "axlog", "cfg-if", "chrono", "crate_interface", diff --git a/apps/display/Cargo.toml b/apps/display/Cargo.toml index 96a08e8749..4cfc2ba437 100644 --- a/apps/display/Cargo.toml +++ b/apps/display/Cargo.toml @@ -7,5 +7,5 @@ authors = ["Shiping Yuan "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axstd = { path = "../../ulib/axstd", features = ["display"] } +axstd = { path = "../../ulib/axstd", features = ["display"], optional = true } embedded-graphics = "0.8" diff --git a/apps/display/src/display.rs b/apps/display/src/display.rs index 9c80ea828b..7e8017400e 100644 --- a/apps/display/src/display.rs +++ b/apps/display/src/display.rs @@ -2,7 +2,7 @@ use embedded_graphics::pixelcolor::Rgb888; use embedded_graphics::prelude::{RgbColor, Size}; use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; -pub use axstd::display::{framebuffer_flush, framebuffer_info, DisplayInfo}; +pub use axstd::os::arceos::axdisplay::{framebuffer_flush, framebuffer_info, DisplayInfo}; pub struct Display { size: Size, diff --git a/apps/display/src/main.rs b/apps/display/src/main.rs index d37b6c4801..ecfe778377 100644 --- a/apps/display/src/main.rs +++ b/apps/display/src/main.rs @@ -1,9 +1,10 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] -extern crate axstd; -mod display; +#[cfg(feature = "axstd")] +extern crate axstd as std; +mod display; use display::*; use embedded_graphics::{ @@ -77,7 +78,7 @@ fn test_gpu() -> i32 { 0 } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() -> ! { test_gpu(); loop { diff --git a/apps/exception/Cargo.toml b/apps/exception/Cargo.toml index cbcadd408a..1be50f0d1d 100644 --- a/apps/exception/Cargo.toml +++ b/apps/exception/Cargo.toml @@ -7,4 +7,4 @@ authors = ["Yuekai Jia "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axstd = { path = "../../ulib/axstd", features = ["paging"] } +axstd = { path = "../../ulib/axstd", optional = true } diff --git a/apps/exception/expect_debug_aarch64.out b/apps/exception/expect_debug_aarch64.out index 2291ea0da9..63396e49a8 100644 --- a/apps/exception/expect_debug_aarch64.out +++ b/apps/exception/expect_debug_aarch64.out @@ -11,8 +11,6 @@ Found physcial memory regions: boot stack (READ | WRITE | RESERVED) .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) -Initialize global memory allocator... -Initialize kernel page table... Initialize platform devices... Primary CPU 0 init OK. Running exception tests... diff --git a/apps/exception/expect_debug_riscv64.out b/apps/exception/expect_debug_riscv64.out index 3e61b79bcc..aafe7113c6 100644 --- a/apps/exception/expect_debug_riscv64.out +++ b/apps/exception/expect_debug_riscv64.out @@ -11,8 +11,6 @@ Found physcial memory regions: boot stack (READ | WRITE | RESERVED) .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) -Initialize global memory allocator... -Initialize kernel page table... Initialize platform devices... Primary CPU 0 init OK. Running exception tests... diff --git a/apps/exception/expect_debug_x86_64.out b/apps/exception/expect_debug_x86_64.out index 0560551fba..adeb8c196f 100644 --- a/apps/exception/expect_debug_x86_64.out +++ b/apps/exception/expect_debug_x86_64.out @@ -11,8 +11,7 @@ Found physcial memory regions: boot stack (READ | WRITE | RESERVED) .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) -Initialize global memory allocator... -Initialize kernel page table... +Initialize platform devices... Primary CPU 0 init OK. Running exception tests... #BP @ 0x[0-9a-f]\{16\} diff --git a/apps/exception/src/main.rs b/apps/exception/src/main.rs index 8fdf9d3fd4..e196cabd0a 100644 --- a/apps/exception/src/main.rs +++ b/apps/exception/src/main.rs @@ -1,8 +1,11 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] -use axstd::println; -use core::arch::asm; +#[cfg(feature = "axstd")] +extern crate axstd as std; + +use std::arch::asm; +use std::println; fn raise_break_exception() { unsafe { @@ -15,7 +18,7 @@ fn raise_break_exception() { } } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { println!("Running exception tests..."); raise_break_exception(); diff --git a/apps/fs/shell/Cargo.toml b/apps/fs/shell/Cargo.toml index d3c4bd1962..bd99c1d11b 100644 --- a/apps/fs/shell/Cargo.toml +++ b/apps/fs/shell/Cargo.toml @@ -7,12 +7,11 @@ authors = ["Yuekai Jia "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -use_ramfs = ["axfs/myfs", "dep:axfs_vfs", "dep:axfs_ramfs", "dep:crate_interface"] +use-ramfs = ["axstd/myfs", "dep:axfs_vfs", "dep:axfs_ramfs", "dep:crate_interface"] default = [] [dependencies] -axfs = { path = "../../../modules/axfs", optional = true } axfs_vfs = { path = "../../../crates/axfs_vfs", optional = true } axfs_ramfs = { path = "../../../crates/axfs_ramfs", optional = true } crate_interface = { path = "../../../crates/crate_interface", optional = true } -axstd = { path = "../../../ulib/axstd", features = ["fs"] } +axstd = { path = "../../../ulib/axstd", features = ["alloc", "fs"], optional = true } diff --git a/apps/fs/shell/src/cmd.rs b/apps/fs/shell/src/cmd.rs index b463e45a21..e89920cf3c 100644 --- a/apps/fs/shell/src/cmd.rs +++ b/apps/fs/shell/src/cmd.rs @@ -1,21 +1,16 @@ -use axstd::fs::{self, File}; -use axstd::io::{self, prelude::*}; -use axstd::{string::String, vec::Vec}; +use std::fs::{self, File, FileType}; +use std::io::{self, prelude::*}; +use std::{string::String, vec::Vec}; + +#[cfg(all(not(feature = "axstd"), unix))] +use std::os::unix::fs::{FileTypeExt, PermissionsExt}; macro_rules! print_err { - ($cmd: literal, $msg: literal) => { + ($cmd: literal, $msg: expr) => { println!("{}: {}", $cmd, $msg); }; - ($cmd: literal, $err: ident) => { - use io::Error::*; - println!("{}: {}", $cmd, $err.as_str()); - }; - ($cmd: literal, $arg: expr, $err: ident) => { - use io::Error::*; - println!("{}: {}: {}", $cmd, $arg, $err.as_str()); - }; - ($cmd: literal, $arg: expr, $msg: expr) => { - println!("{}: {}: {}", $cmd, $arg, $msg); + ($cmd: literal, $arg: expr, $err: expr) => { + println!("{}: {}: {}", $cmd, $arg, $err); }; } @@ -34,10 +29,47 @@ const CMD_TABLE: &[(&str, CmdHandler)] = &[ ("uname", do_uname), ]; +fn file_type_to_char(ty: FileType) -> char { + if ty.is_char_device() { + 'c' + } else if ty.is_block_device() { + 'b' + } else if ty.is_socket() { + 's' + } else if ty.is_fifo() { + 'p' + } else if ty.is_symlink() { + 'l' + } else if ty.is_dir() { + 'd' + } else if ty.is_file() { + '-' + } else { + '?' + } +} + +#[rustfmt::skip] +const fn file_perm_to_rwx(mode: u32) -> [u8; 9] { + let mut perm = [b'-'; 9]; + macro_rules! set { + ($bit:literal, $rwx:literal) => { + if mode & (1 << $bit) != 0 { + perm[8 - $bit] = $rwx + } + }; + } + + set!(2, b'r'); set!(1, b'w'); set!(0, b'x'); + set!(5, b'r'); set!(4, b'w'); set!(3, b'x'); + set!(8, b'r'); set!(7, b'w'); set!(6, b'x'); + perm +} + fn do_ls(args: &str) { - let current_dir = axstd::env::current_dir().unwrap(); + let current_dir = std::env::current_dir().unwrap(); let args = if args.is_empty() { - current_dir.as_str() + path_to_str!(current_dir) } else { args }; @@ -47,8 +79,8 @@ fn do_ls(args: &str) { let metadata = fs::metadata(path)?; let size = metadata.len(); let file_type = metadata.file_type(); - let file_type_char = file_type.as_char(); - let rwx = metadata.permissions().rwx_buf(); + let file_type_char = file_type_to_char(file_type); + let rwx = file_perm_to_rwx(metadata.permissions().mode()); let rwx = unsafe { core::str::from_utf8_unchecked(&rwx) }; println!("{}{} {:>8} {}", file_type_char, rwx, size, entry); Ok(()) @@ -70,9 +102,10 @@ fn do_ls(args: &str) { entries.sort(); for entry in entries { - let path = String::from(name) + "/" + &entry; - if let Err(e) = show_entry_info(&path, &entry) { - print_err!("ls", path, e.as_str()); + let entry = path_to_str!(entry); + let path = String::from(name) + "/" + entry; + if let Err(e) = show_entry_info(&path, entry) { + print_err!("ls", path, e); } } Ok(()) @@ -83,7 +116,7 @@ fn do_ls(args: &str) { println!(); } if let Err(e) = list_one(name, name_count > 1) { - print_err!("ls", name, e.as_str()); + print_err!("ls", name, e); } } } @@ -100,7 +133,7 @@ fn do_cat(args: &str) { loop { let n = file.read(&mut buf)?; if n > 0 { - io::stdout().write(&buf[..n])?; + io::stdout().write_all(&buf[..n])?; } else { return Ok(()); } @@ -109,7 +142,7 @@ fn do_cat(args: &str) { for fname in args.split_whitespace() { if let Err(e) = cat_one(fname) { - print_err!("cat", fname, e.as_str()); + print_err!("cat", fname, e); } } } @@ -138,7 +171,7 @@ fn do_echo(args: &str) { "\n", ]; if let Err(e) = echo_file(fname, &text_list) { - print_err!("echo", fname, e.as_str()); + print_err!("echo", fname, e); } } else { println!("{}", args) @@ -157,11 +190,7 @@ fn do_mkdir(args: &str) { for path in args.split_whitespace() { if let Err(e) = mkdir_one(path) { - print_err!( - "mkdir", - format_args!("cannot create directory '{path}'"), - e.as_str() - ); + print_err!("mkdir", format_args!("cannot create directory '{path}'"), e); } } } @@ -191,7 +220,7 @@ fn do_rm(args: &str) { continue; } if let Err(e) = rm_one(path, rm_dir) { - print_err!("rm", format_args!("cannot remove '{path}'"), e.as_str()); + print_err!("rm", format_args!("cannot remove '{path}'"), e); } } } @@ -201,8 +230,8 @@ fn do_cd(mut args: &str) { args = "/"; } if !args.contains(char::is_whitespace) { - if let Err(e) = axstd::env::set_current_dir(args) { - print_err!("cd", args, e.as_str()); + if let Err(e) = std::env::set_current_dir(args) { + print_err!("cd", args, e); } } else { print_err!("cd", "too many arguments"); @@ -210,14 +239,14 @@ fn do_cd(mut args: &str) { } fn do_pwd(_args: &str) { - let pwd = axstd::env::current_dir().unwrap(); - println!("{}", pwd); + let pwd = std::env::current_dir().unwrap(); + println!("{}", path_to_str!(pwd)); } fn do_uname(_args: &str) { - let arch = option_env!("AX_ARCH").unwrap_or(""); - let platform = option_env!("AX_PLATFORM").unwrap_or(""); - let smp = match option_env!("AX_SMP") { + let arch = option_env!("ARCH").unwrap_or(""); + let platform = option_env!("PLATFORM").unwrap_or(""); + let smp = match option_env!("SMP") { None | Some("1") => "", _ => " SMP", }; @@ -239,7 +268,8 @@ fn do_help(_args: &str) { } fn do_exit(_args: &str) { - axstd::thread::exit(0); + println!("Bye~"); + std::process::exit(0); } pub fn run_cmd(line: &[u8]) { diff --git a/apps/fs/shell/src/main.rs b/apps/fs/shell/src/main.rs index 544aa57722..1e2875aad9 100644 --- a/apps/fs/shell/src/main.rs +++ b/apps/fs/shell/src/main.rs @@ -1,15 +1,29 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] + +#[macro_use] +#[cfg(feature = "axstd")] +extern crate axstd as std; + +macro_rules! path_to_str { + ($path:expr) => {{ + #[cfg(not(feature = "axstd"))] + { + $path.to_str().unwrap() // Path/OsString -> &str + } + #[cfg(feature = "axstd")] + { + $path.as_str() // String -> &str + } + }}; +} mod cmd; -#[cfg(feature = "use_ramfs")] +#[cfg(feature = "use-ramfs")] mod ramfs; -use axstd::io::prelude::*; - -#[macro_use] -extern crate axstd; +use std::io::prelude::*; const LF: u8 = b'\n'; const CR: u8 = b'\r'; @@ -20,13 +34,17 @@ const SPACE: u8 = b' '; const MAX_CMD_LEN: usize = 256; fn print_prompt() { - print!("arceos:{}$ ", axstd::env::current_dir().unwrap()); + print!( + "arceos:{}$ ", + path_to_str!(std::env::current_dir().unwrap()) + ); + std::io::stdout().flush().unwrap(); } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { - let mut stdin = axstd::io::stdin(); - let mut stdout = axstd::io::stdout(); + let mut stdin = std::io::stdin(); + let mut stdout = std::io::stdout(); let mut buf = [0; MAX_CMD_LEN]; let mut cursor = 0; @@ -51,14 +69,14 @@ fn main() { } BS | DL => { if cursor > 0 { - stdout.write(&[BS, SPACE, BS]).unwrap(); + stdout.write_all(&[BS, SPACE, BS]).unwrap(); cursor -= 1; } } 0..=31 => {} c => { if cursor < MAX_CMD_LEN - 1 { - stdout.write(&[c]).unwrap(); + stdout.write_all(&[c]).unwrap(); cursor += 1; } } diff --git a/apps/fs/shell/src/ramfs.rs b/apps/fs/shell/src/ramfs.rs index a660874cc3..07922bb209 100644 --- a/apps/fs/shell/src/ramfs.rs +++ b/apps/fs/shell/src/ramfs.rs @@ -1,9 +1,9 @@ extern crate alloc; use alloc::sync::Arc; -use axfs::fops::{Disk, MyFileSystemIf}; use axfs_ramfs::RamFileSystem; use axfs_vfs::VfsOps; +use std::os::arceos::axfs::fops::{Disk, MyFileSystemIf}; struct MyFileSystemIfImpl; diff --git a/apps/helloworld/Cargo.toml b/apps/helloworld/Cargo.toml index 90018987ce..9b759ec55a 100644 --- a/apps/helloworld/Cargo.toml +++ b/apps/helloworld/Cargo.toml @@ -7,4 +7,4 @@ authors = ["Yuekai Jia "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axstd = { path = "../../ulib/axstd" } +axstd = { path = "../../ulib/axstd", optional = true } diff --git a/apps/helloworld/src/main.rs b/apps/helloworld/src/main.rs index b742b84179..97161c8660 100644 --- a/apps/helloworld/src/main.rs +++ b/apps/helloworld/src/main.rs @@ -1,7 +1,10 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] -#[no_mangle] +#[cfg(feature = "axstd")] +use axstd::println; + +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { - axstd::println!("Hello, world!"); + println!("Hello, world!"); } diff --git a/apps/memtest/Cargo.toml b/apps/memtest/Cargo.toml index b600a67eb9..592b6c4f15 100644 --- a/apps/memtest/Cargo.toml +++ b/apps/memtest/Cargo.toml @@ -7,4 +7,5 @@ authors = ["Yuekai Jia "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axstd = { path = "../../ulib/axstd", features = ["alloc", "paging"] } +rand = { version = "0.8", default-features = false, features = ["small_rng"] } +axstd = { path = "../../ulib/axstd", features = ["alloc"], optional = true } diff --git a/apps/memtest/src/main.rs b/apps/memtest/src/main.rs index a357358141..1d3a081c25 100644 --- a/apps/memtest/src/main.rs +++ b/apps/memtest/src/main.rs @@ -1,19 +1,19 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] #[macro_use] -extern crate axstd; -extern crate alloc; +#[cfg(feature = "axstd")] +extern crate axstd as std; -use alloc::collections::BTreeMap; -use alloc::vec::Vec; -use axstd::rand; +use rand::{rngs::SmallRng, RngCore, SeedableRng}; +use std::collections::BTreeMap; +use std::vec::Vec; -fn test_vec() { +fn test_vec(rng: &mut impl RngCore) { const N: usize = 1_000_000; let mut v = Vec::with_capacity(N); for _ in 0..N { - v.push(rand::rand_u32()); + v.push(rng.next_u32()); } v.sort(); for i in 0..N - 1 { @@ -22,12 +22,12 @@ fn test_vec() { println!("test_vec() OK!"); } -fn test_btree_map() { +fn test_btree_map(rng: &mut impl RngCore) { const N: usize = 10_000; let mut m = BTreeMap::new(); for _ in 0..N { - let value = rand::rand_u32(); - let key = alloc::format!("key_{value}"); + let value = rng.next_u32(); + let key = format!("key_{value}"); m.insert(key, value); } for (k, v) in m.iter() { @@ -38,10 +38,13 @@ fn test_btree_map() { println!("test_btree_map() OK!"); } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { println!("Running memory tests..."); - test_vec(); - test_btree_map(); + + let mut rng = SmallRng::seed_from_u64(0xdead_beef); + test_vec(&mut rng); + test_btree_map(&mut rng); + println!("Memory tests run OK!"); } diff --git a/apps/memtest/test_cmd b/apps/memtest/test_cmd index e2d646be0f..dfa19caf9f 100644 --- a/apps/memtest/test_cmd +++ b/apps/memtest/test_cmd @@ -1 +1 @@ -test_one "LOG=trace" "expect_trace.out" +test_one "LOG=trace APP_FEATURES=axstd/paging" "expect_trace.out" diff --git a/apps/net/bwbench/Cargo.toml b/apps/net/bwbench/Cargo.toml index 3c4a1a2003..4cc2454873 100644 --- a/apps/net/bwbench/Cargo.toml +++ b/apps/net/bwbench/Cargo.toml @@ -7,5 +7,5 @@ authors = ["ChengXiang Qi "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axstd = {path = "../../../ulib/axstd", features = ["net"]} -axnet = {path = "../../../modules/axnet"} +axstd = { path = "../../../ulib/axstd", features = ["net"] } +axnet = { path = "../../../modules/axnet" } diff --git a/apps/net/echoserver/Cargo.toml b/apps/net/echoserver/Cargo.toml index f606e05922..7698bf0c11 100644 --- a/apps/net/echoserver/Cargo.toml +++ b/apps/net/echoserver/Cargo.toml @@ -7,4 +7,4 @@ authors = ["Yuekai Jia "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axstd = { path = "../../../ulib/axstd", features = ["multitask", "net"] } +axstd = { path = "../../../ulib/axstd", features = ["alloc", "multitask", "net"], optional = true } diff --git a/apps/net/echoserver/src/main.rs b/apps/net/echoserver/src/main.rs index 9ab4b2cc87..bff296b3d4 100644 --- a/apps/net/echoserver/src/main.rs +++ b/apps/net/echoserver/src/main.rs @@ -1,13 +1,14 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] #[macro_use] -extern crate axstd; +#[cfg(feature = "axstd")] +extern crate axstd as std; -use axstd::io::{self, prelude::*}; -use axstd::net::{TcpListener, TcpStream}; -use axstd::thread; -use axstd::vec::Vec; +use std::io::{self, prelude::*}; +use std::net::{TcpListener, TcpStream}; +use std::thread; +use std::vec::Vec; const LOCAL_IP: &str = "0.0.0.0"; const LOCAL_PORT: u16 = 5555; @@ -42,10 +43,10 @@ fn accept_loop() -> io::Result<()> { loop { match listener.accept() { Ok((stream, addr)) => { - info!("new client {}: {}", i, addr); + println!("new client {}: {}", i, addr); thread::spawn(move || match echo_server(stream) { - Err(e) => error!("client connection error: {:?}", e), - Ok(()) => info!("client {} closed successfully", i), + Err(e) => println!("client connection error: {:?}", e), + Ok(()) => println!("client {} closed successfully", i), }); } Err(e) => return Err(e), @@ -54,7 +55,7 @@ fn accept_loop() -> io::Result<()> { } } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { println!("Hello, echo server!"); accept_loop().expect("test echo server failed"); diff --git a/apps/net/httpclient/Cargo.toml b/apps/net/httpclient/Cargo.toml index 159f05d747..8efffa0328 100644 --- a/apps/net/httpclient/Cargo.toml +++ b/apps/net/httpclient/Cargo.toml @@ -7,8 +7,8 @@ authors = ["Yuekai Jia ", "Dashuai Wu io::Result<()> { Ok(()) } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { println!("Hello, simple http client!"); client().expect("test http client failed"); diff --git a/apps/net/httpserver/Cargo.toml b/apps/net/httpserver/Cargo.toml index fac8d3cfe6..ba59e98205 100644 --- a/apps/net/httpserver/Cargo.toml +++ b/apps/net/httpserver/Cargo.toml @@ -7,4 +7,4 @@ authors = ["Yuekai Jia "] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -axstd = { path = "../../../ulib/axstd", features = ["multitask", "net"] } +axstd = { path = "../../../ulib/axstd", features = ["alloc", "multitask", "net"], optional = true } diff --git a/apps/net/httpserver/src/main.rs b/apps/net/httpserver/src/main.rs index c70c5501f1..a5943a909b 100644 --- a/apps/net/httpserver/src/main.rs +++ b/apps/net/httpserver/src/main.rs @@ -6,15 +6,16 @@ //! ab -n 5000 -c 20 http://X.X.X.X:5555/ //! ``` -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] #[macro_use] -extern crate axstd; +#[cfg(feature = "axstd")] +extern crate axstd as std; -use axstd::io::{self, prelude::*}; -use axstd::net::{TcpListener, TcpStream}; -use axstd::thread; +use std::io::{self, prelude::*}; +use std::net::{TcpListener, TcpStream}; +use std::thread; const LOCAL_IP: &str = "0.0.0.0"; const LOCAL_PORT: u16 = 5555; @@ -47,6 +48,17 @@ const CONTENT: &str = r#" "#; +macro_rules! info { + ($($arg:tt)*) => { + match option_env!("LOG") { + Some("info") | Some("debug") | Some("trace") => { + print!("[INFO] {}\n", format_args!($($arg)*)); + } + _ => {} + } + }; +} + fn http_server(mut stream: TcpStream) -> io::Result<()> { let mut buf = [0u8; 4096]; let _len = stream.read(&mut buf)?; @@ -67,7 +79,7 @@ fn accept_loop() -> io::Result<()> { Ok((stream, addr)) => { info!("new client {}: {}", i, addr); thread::spawn(move || match http_server(stream) { - Err(e) => error!("client connection error: {:?}", e), + Err(e) => info!("client connection error: {:?}", e), Ok(()) => info!("client {} closed successfully", i), }); } @@ -77,7 +89,7 @@ fn accept_loop() -> io::Result<()> { } } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { println!("Hello, ArceOS HTTP server!"); accept_loop().expect("test HTTP server failed"); diff --git a/apps/net/udpserver/Cargo.toml b/apps/net/udpserver/Cargo.toml index 25ef569aaa..0c6b86a4aa 100644 --- a/apps/net/udpserver/Cargo.toml +++ b/apps/net/udpserver/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" authors = ["Dashuai Wu "] [dependencies] -axstd = { path = "../../../ulib/axstd", features = ["net"] } +axstd = { path = "../../../ulib/axstd", features = ["net"], optional = true } diff --git a/apps/net/udpserver/src/main.rs b/apps/net/udpserver/src/main.rs index d9848a4f4c..54c2f96373 100644 --- a/apps/net/udpserver/src/main.rs +++ b/apps/net/udpserver/src/main.rs @@ -1,11 +1,12 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] #[macro_use] -extern crate axstd; +#[cfg(feature = "axstd")] +extern crate axstd as std; -use axstd::io; -use axstd::net::{ToSocketAddrs, UdpSocket}; +use std::io; +use std::net::{ToSocketAddrs, UdpSocket}; const LOCAL_IP: &str = "0.0.0.0"; const LOCAL_PORT: u16 = 5555; @@ -30,7 +31,7 @@ fn receive_loop() -> io::Result<()> { } } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { println!("Hello, simple udp client!"); receive_loop().expect("test udp client failed"); diff --git a/apps/task/parallel/Cargo.toml b/apps/task/parallel/Cargo.toml index 9c7d1ccc6f..defca45026 100644 --- a/apps/task/parallel/Cargo.toml +++ b/apps/task/parallel/Cargo.toml @@ -11,4 +11,5 @@ sched_rr = ["axstd/sched_rr"] sched_cfs = ["axstd/sched_cfs"] [dependencies] -axstd = { path = "../../../ulib/axstd", features = ["alloc", "paging", "multitask", "irq"] } +rand = { version = "0.8", default-features = false, features = ["small_rng"] } +axstd = { path = "../../../ulib/axstd", features = ["alloc", "multitask", "irq"], optional = true } diff --git a/apps/task/parallel/expect_info_smp1_fifo.out b/apps/task/parallel/expect_info_smp1_fifo.out index 738527a856..27c30d25f6 100644 --- a/apps/task/parallel/expect_info_smp1_fifo.out +++ b/apps/task/parallel/expect_info_smp1_fifo.out @@ -12,29 +12,28 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize platform devices... Initialize scheduling... use FIFO scheduler. Initialize interrupt handlers... Primary CPU 0 init OK. -part 0: TaskId(4) \[0, 125000) -part 1: TaskId(5) \[125000, 250000) -part 2: TaskId(6) \[250000, 375000) -part 3: TaskId(7) \[375000, 500000) -part 4: TaskId(8) \[500000, 625000) -part 5: TaskId(9) \[625000, 750000) -part 6: TaskId(10) \[750000, 875000) -part 7: TaskId(11) \[875000, 1000000) -part 8: TaskId(12) \[1000000, 1125000) -part 9: TaskId(13) \[1125000, 1250000) -part 10: TaskId(14) \[1250000, 1375000) -part 11: TaskId(15) \[1375000, 1500000) -part 12: TaskId(16) \[1500000, 1625000) -part 13: TaskId(17) \[1625000, 1750000) -part 14: TaskId(18) \[1750000, 1875000) -part 15: TaskId(19) \[1875000, 2000000) -part 15: TaskId(19) finished -sum = 61783189038 +part 0: ThreadId(4) \[0, 125000) +part 1: ThreadId(5) \[125000, 250000) +part 2: ThreadId(6) \[250000, 375000) +part 3: ThreadId(7) \[375000, 500000) +part 4: ThreadId(8) \[500000, 625000) +part 5: ThreadId(9) \[625000, 750000) +part 6: ThreadId(10) \[750000, 875000) +part 7: ThreadId(11) \[875000, 1000000) +part 8: ThreadId(12) \[1000000, 1125000) +part 9: ThreadId(13) \[1125000, 1250000) +part 10: ThreadId(14) \[1250000, 1375000) +part 11: ThreadId(15) \[1375000, 1500000) +part 12: ThreadId(16) \[1500000, 1625000) +part 13: ThreadId(17) \[1625000, 1750000) +part 14: ThreadId(18) \[1750000, 1875000) +part 15: ThreadId(19) \[1875000, 2000000) +part 15: ThreadId(19) finished +sum = 87362923216 Parallel summation tests run OK! Shutting down... diff --git a/apps/task/parallel/expect_info_smp4_cfs.out b/apps/task/parallel/expect_info_smp4_cfs.out index b5e12364a6..76d61f0256 100644 --- a/apps/task/parallel/expect_info_smp4_cfs.out +++ b/apps/task/parallel/expect_info_smp4_cfs.out @@ -12,7 +12,6 @@ boot stack (READ | WRITE | RESERVED) .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize platform devices... Initialize scheduling... use Completely Fair scheduler. @@ -24,38 +23,38 @@ CPU 3 started CPU 1 init OK CPU 2 init OK CPU 3 init OK -part 0: TaskId([0-9]\+) \[0, 125000) -part 1: TaskId([0-9]\+) \[125000, 250000) -part 2: TaskId([0-9]\+) \[250000, 375000) -part 3: TaskId([0-9]\+) \[375000, 500000) -part 4: TaskId([0-9]\+) \[500000, 625000) -part 5: TaskId([0-9]\+) \[625000, 750000) -part 6: TaskId([0-9]\+) \[750000, 875000) -part 7: TaskId([0-9]\+) \[875000, 1000000) -part 8: TaskId([0-9]\+) \[1000000, 1125000) -part 9: TaskId([0-9]\+) \[1125000, 1250000) -part 10: TaskId([0-9]\+) \[1250000, 1375000) -part 11: TaskId([0-9]\+) \[1375000, 1500000) -part 12: TaskId([0-9]\+) \[1500000, 1625000) -part 13: TaskId([0-9]\+) \[1625000, 1750000) -part 14: TaskId([0-9]\+) \[1750000, 1875000) -part 15: TaskId([0-9]\+) \[1875000, 2000000) -part 15: TaskId([0-9]\+) finished -part 0: TaskId([0-9]\+) finished -part 1: TaskId([0-9]\+) finished -part 2: TaskId([0-9]\+) finished -part 3: TaskId([0-9]\+) finished -part 4: TaskId([0-9]\+) finished -part 5: TaskId([0-9]\+) finished -part 6: TaskId([0-9]\+) finished -part 7: TaskId([0-9]\+) finished -part 8: TaskId([0-9]\+) finished -part 9: TaskId([0-9]\+) finished -part 10: TaskId([0-9]\+) finished -part 11: TaskId([0-9]\+) finished -part 12: TaskId([0-9]\+) finished -part 13: TaskId([0-9]\+) finished -part 14: TaskId([0-9]\+) finished -sum = 61783189038 +part 0: ThreadId([0-9]\+) \[0, 125000) +part 1: ThreadId([0-9]\+) \[125000, 250000) +part 2: ThreadId([0-9]\+) \[250000, 375000) +part 3: ThreadId([0-9]\+) \[375000, 500000) +part 4: ThreadId([0-9]\+) \[500000, 625000) +part 5: ThreadId([0-9]\+) \[625000, 750000) +part 6: ThreadId([0-9]\+) \[750000, 875000) +part 7: ThreadId([0-9]\+) \[875000, 1000000) +part 8: ThreadId([0-9]\+) \[1000000, 1125000) +part 9: ThreadId([0-9]\+) \[1125000, 1250000) +part 10: ThreadId([0-9]\+) \[1250000, 1375000) +part 11: ThreadId([0-9]\+) \[1375000, 1500000) +part 12: ThreadId([0-9]\+) \[1500000, 1625000) +part 13: ThreadId([0-9]\+) \[1625000, 1750000) +part 14: ThreadId([0-9]\+) \[1750000, 1875000) +part 15: ThreadId([0-9]\+) \[1875000, 2000000) +part 15: ThreadId([0-9]\+) finished +part 0: ThreadId([0-9]\+) finished +part 1: ThreadId([0-9]\+) finished +part 2: ThreadId([0-9]\+) finished +part 3: ThreadId([0-9]\+) finished +part 4: ThreadId([0-9]\+) finished +part 5: ThreadId([0-9]\+) finished +part 6: ThreadId([0-9]\+) finished +part 7: ThreadId([0-9]\+) finished +part 8: ThreadId([0-9]\+) finished +part 9: ThreadId([0-9]\+) finished +part 10: ThreadId([0-9]\+) finished +part 11: ThreadId([0-9]\+) finished +part 12: ThreadId([0-9]\+) finished +part 13: ThreadId([0-9]\+) finished +part 14: ThreadId([0-9]\+) finished +sum = 87362923216 Parallel summation tests run OK! Shutting down... diff --git a/apps/task/parallel/expect_info_smp4_rr.out b/apps/task/parallel/expect_info_smp4_rr.out index d97c6fb591..6ec5acedc1 100644 --- a/apps/task/parallel/expect_info_smp4_rr.out +++ b/apps/task/parallel/expect_info_smp4_rr.out @@ -12,7 +12,6 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize platform devices... Initialize scheduling... use Round-robin scheduler. @@ -24,38 +23,38 @@ CPU 3 started CPU 1 init OK CPU 2 init OK CPU 3 init OK -part 0: TaskId([0-9]\+) \[0, 125000) -part 1: TaskId([0-9]\+) \[125000, 250000) -part 2: TaskId([0-9]\+) \[250000, 375000) -part 3: TaskId([0-9]\+) \[375000, 500000) -part 4: TaskId([0-9]\+) \[500000, 625000) -part 5: TaskId([0-9]\+) \[625000, 750000) -part 6: TaskId([0-9]\+) \[750000, 875000) -part 7: TaskId([0-9]\+) \[875000, 1000000) -part 8: TaskId([0-9]\+) \[1000000, 1125000) -part 9: TaskId([0-9]\+) \[1125000, 1250000) -part 10: TaskId([0-9]\+) \[1250000, 1375000) -part 11: TaskId([0-9]\+) \[1375000, 1500000) -part 12: TaskId([0-9]\+) \[1500000, 1625000) -part 13: TaskId([0-9]\+) \[1625000, 1750000) -part 14: TaskId([0-9]\+) \[1750000, 1875000) -part 15: TaskId([0-9]\+) \[1875000, 2000000) -part 15: TaskId([0-9]\+) finished -part 0: TaskId([0-9]\+) finished -part 1: TaskId([0-9]\+) finished -part 2: TaskId([0-9]\+) finished -part 3: TaskId([0-9]\+) finished -part 4: TaskId([0-9]\+) finished -part 5: TaskId([0-9]\+) finished -part 6: TaskId([0-9]\+) finished -part 7: TaskId([0-9]\+) finished -part 8: TaskId([0-9]\+) finished -part 9: TaskId([0-9]\+) finished -part 10: TaskId([0-9]\+) finished -part 11: TaskId([0-9]\+) finished -part 12: TaskId([0-9]\+) finished -part 13: TaskId([0-9]\+) finished -part 14: TaskId([0-9]\+) finished -sum = 61783189038 +part 0: ThreadId([0-9]\+) \[0, 125000) +part 1: ThreadId([0-9]\+) \[125000, 250000) +part 2: ThreadId([0-9]\+) \[250000, 375000) +part 3: ThreadId([0-9]\+) \[375000, 500000) +part 4: ThreadId([0-9]\+) \[500000, 625000) +part 5: ThreadId([0-9]\+) \[625000, 750000) +part 6: ThreadId([0-9]\+) \[750000, 875000) +part 7: ThreadId([0-9]\+) \[875000, 1000000) +part 8: ThreadId([0-9]\+) \[1000000, 1125000) +part 9: ThreadId([0-9]\+) \[1125000, 1250000) +part 10: ThreadId([0-9]\+) \[1250000, 1375000) +part 11: ThreadId([0-9]\+) \[1375000, 1500000) +part 12: ThreadId([0-9]\+) \[1500000, 1625000) +part 13: ThreadId([0-9]\+) \[1625000, 1750000) +part 14: ThreadId([0-9]\+) \[1750000, 1875000) +part 15: ThreadId([0-9]\+) \[1875000, 2000000) +part 15: ThreadId([0-9]\+) finished +part 0: ThreadId([0-9]\+) finished +part 1: ThreadId([0-9]\+) finished +part 2: ThreadId([0-9]\+) finished +part 3: ThreadId([0-9]\+) finished +part 4: ThreadId([0-9]\+) finished +part 5: ThreadId([0-9]\+) finished +part 6: ThreadId([0-9]\+) finished +part 7: ThreadId([0-9]\+) finished +part 8: ThreadId([0-9]\+) finished +part 9: ThreadId([0-9]\+) finished +part 10: ThreadId([0-9]\+) finished +part 11: ThreadId([0-9]\+) finished +part 12: ThreadId([0-9]\+) finished +part 13: ThreadId([0-9]\+) finished +part 14: ThreadId([0-9]\+) finished +sum = 87362923216 Parallel summation tests run OK! Shutting down... diff --git a/apps/task/parallel/src/main.rs b/apps/task/parallel/src/main.rs index a1e82c457b..a8077f575d 100644 --- a/apps/task/parallel/src/main.rs +++ b/apps/task/parallel/src/main.rs @@ -1,26 +1,36 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] #[macro_use] -extern crate axstd; -extern crate alloc; +#[cfg(feature = "axstd")] +extern crate axstd as std; -use alloc::sync::Arc; -use alloc::vec::Vec; -use axstd::sync::WaitQueue; -use axstd::{rand, thread}; -use core::sync::atomic::{AtomicUsize, Ordering}; -use core::time::Duration; +use rand::{rngs::SmallRng, RngCore, SeedableRng}; +use std::thread; +use std::{sync::Arc, vec::Vec}; + +#[cfg(feature = "axstd")] +use std::os::arceos::axtask::WaitQueue; const NUM_DATA: usize = 2_000_000; const NUM_TASKS: usize = 16; +#[cfg(feature = "axstd")] fn barrier() { + use std::sync::atomic::{AtomicUsize, Ordering}; static BARRIER_WQ: WaitQueue = WaitQueue::new(); static BARRIER_COUNT: AtomicUsize = AtomicUsize::new(0); + BARRIER_COUNT.fetch_add(1, Ordering::Relaxed); BARRIER_WQ.wait_until(|| BARRIER_COUNT.load(Ordering::Relaxed) == NUM_TASKS); - BARRIER_WQ.notify_all(true); + BARRIER_WQ.notify_all(true); // wakeup all +} + +#[cfg(not(feature = "axstd"))] +fn barrier() { + use std::sync::{Barrier, OnceLock}; + static BARRIER: OnceLock = OnceLock::new(); + BARRIER.get_or_init(|| Barrier::new(NUM_TASKS)).wait(); } fn sqrt(n: &u64) -> u64 { @@ -33,18 +43,22 @@ fn sqrt(n: &u64) -> u64 { } } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { + let mut rng = SmallRng::seed_from_u64(0xdead_beef); let vec = Arc::new( (0..NUM_DATA) - .map(|_| rand::rand_u32() as u64) + .map(|_| rng.next_u32() as u64) .collect::>(), ); let expect: u64 = vec.iter().map(sqrt).sum(); - // equals to sleep(500ms) - let timeout = WaitQueue::new().wait_timeout(Duration::from_millis(500)); - assert!(timeout); + #[cfg(feature = "axstd")] + { + // equals to sleep(500ms) + let timeout = WaitQueue::new().wait_timeout(std::time::Duration::from_millis(500)); + assert!(timeout); + } let mut tasks = Vec::with_capacity(NUM_TASKS); for i in 0..NUM_TASKS { diff --git a/apps/task/priority/Cargo.toml b/apps/task/priority/Cargo.toml index 2b6eda902e..19c32545fb 100644 --- a/apps/task/priority/Cargo.toml +++ b/apps/task/priority/Cargo.toml @@ -11,4 +11,4 @@ sched_rr = ["axstd/sched_rr"] sched_cfs = ["axstd/sched_cfs"] [dependencies] -axstd = { path = "../../../ulib/axstd", features = ["alloc", "paging", "multitask"] } +axstd = { path = "../../../ulib/axstd", features = ["alloc", "multitask"], optional = true } diff --git a/apps/task/priority/expect_info_smp1_cfs.out b/apps/task/priority/expect_info_smp1_cfs.out index 1a51880aa2..c6fa49e266 100644 --- a/apps/task/priority/expect_info_smp1_cfs.out +++ b/apps/task/priority/expect_info_smp1_cfs.out @@ -12,21 +12,20 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize scheduling... use Completely Fair scheduler. Initialize interrupt handlers... Primary CPU 0 init OK. -part 0: TaskId(4) \[0, 40) -part 1: TaskId(5) \[0, 40) -part 2: TaskId(6) \[0, 40) -part 3: TaskId(7) \[0, 40) -part 4: TaskId(8) \[0, 4) -part [0-9]\+: TaskId([0-9]\+) finished -part [0-9]\+: TaskId([0-9]\+) finished -part [0-9]\+: TaskId([0-9]\+) finished -part [0-9]\+: TaskId([0-9]\+) finished -part [0-9]\+: TaskId([0-9]\+) finished +part 0: ThreadId(4) \[0, 40) +part 1: ThreadId(5) \[0, 40) +part 2: ThreadId(6) \[0, 40) +part 3: ThreadId(7) \[0, 40) +part 4: ThreadId(8) \[0, 4) +part 0: ThreadId(4) finished +part 1: ThreadId(5) finished +part 2: ThreadId(6) finished +part 3: ThreadId(7) finished +part 4: ThreadId(8) finished sum = 3318102132 leave time: task 0 = diff --git a/apps/task/priority/expect_info_smp1_fifo.out b/apps/task/priority/expect_info_smp1_fifo.out index 74c8cd62bd..094700cb9c 100644 --- a/apps/task/priority/expect_info_smp1_fifo.out +++ b/apps/task/priority/expect_info_smp1_fifo.out @@ -12,20 +12,19 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize scheduling... use FIFO scheduler. Primary CPU 0 init OK. -part 0: TaskId(4) \[0, 40) -part 0: TaskId(4) finished -part 1: TaskId(5) \[0, 40) -part 1: TaskId(5) finished -part 2: TaskId(6) \[0, 40) -part 2: TaskId(6) finished -part 3: TaskId(7) \[0, 40) -part 3: TaskId(7) finished -part 4: TaskId(8) \[0, 4) -part 4: TaskId(8) finished +part 0: ThreadId(4) \[0, 40) +part 0: ThreadId(4) finished +part 1: ThreadId(5) \[0, 40) +part 1: ThreadId(5) finished +part 2: ThreadId(6) \[0, 40) +part 2: ThreadId(6) finished +part 3: ThreadId(7) \[0, 40) +part 3: ThreadId(7) finished +part 4: ThreadId(8) \[0, 4) +part 4: ThreadId(8) finished sum = 3318102132 leave time: task 0 = diff --git a/apps/task/priority/expect_info_smp1_rr.out b/apps/task/priority/expect_info_smp1_rr.out index b3a9270cf8..166215637b 100644 --- a/apps/task/priority/expect_info_smp1_rr.out +++ b/apps/task/priority/expect_info_smp1_rr.out @@ -12,21 +12,20 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize scheduling... use Round-robin scheduler. Initialize interrupt handlers... Primary CPU 0 init OK. -part 0: TaskId(4) \[0, 40) -part 1: TaskId(5) \[0, 40) -part 2: TaskId(6) \[0, 40) -part 3: TaskId(7) \[0, 40) -part 4: TaskId(8) \[0, 4) -part 0: TaskId(4) finished -part 1: TaskId(5) finished -part 2: TaskId(6) finished -part 3: TaskId(7) finished -part 4: TaskId(8) finished +part 0: ThreadId(4) \[0, 40) +part 1: ThreadId(5) \[0, 40) +part 2: ThreadId(6) \[0, 40) +part 3: ThreadId(7) \[0, 40) +part 4: ThreadId(8) \[0, 4) +part 0: ThreadId(4) finished +part 1: ThreadId(5) finished +part 2: ThreadId(6) finished +part 3: ThreadId(7) finished +part 4: ThreadId(8) finished sum = 3318102132 leave time: task 0 = diff --git a/apps/task/priority/expect_info_smp4_cfs.out b/apps/task/priority/expect_info_smp4_cfs.out index 38c7f1e710..631a583483 100644 --- a/apps/task/priority/expect_info_smp4_cfs.out +++ b/apps/task/priority/expect_info_smp4_cfs.out @@ -18,21 +18,20 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize scheduling... use Completely Fair scheduler. Initialize interrupt handlers... Primary CPU [0-9]\+ init OK. -part 0: TaskId(7) \[0, 40) -part 1: TaskId(8) \[0, 40) -part 2: TaskId(9) \[0, 40) -part 3: TaskId(10) \[0, 40) -part 4: TaskId(11) \[0, 4) -part [0-9]\+: TaskId([0-9]\+) finished -part [0-9]\+: TaskId([0-9]\+) finished -part [0-9]\+: TaskId([0-9]\+) finished -part [0-9]\+: TaskId([0-9]\+) finished -part [0-9]\+: TaskId([0-9]\+) finished +part 0: ThreadId(7) \[0, 40) +part 1: ThreadId(8) \[0, 40) +part 2: ThreadId(9) \[0, 40) +part 3: ThreadId(10) \[0, 40) +part 4: ThreadId(11) \[0, 4) +part 0: ThreadId(7) finished +part 1: ThreadId(8) finished +part 2: ThreadId(9) finished +part 3: ThreadId(10) finished +part 4: ThreadId(11) finished sum = 3318102132 leave time: task 0 = diff --git a/apps/task/priority/src/main.rs b/apps/task/priority/src/main.rs index 3c95a2d22e..f40924d243 100644 --- a/apps/task/priority/src/main.rs +++ b/apps/task/priority/src/main.rs @@ -1,13 +1,16 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] #[macro_use] -extern crate axstd; -extern crate alloc; +#[cfg(feature = "axstd")] +extern crate axstd as std; -use alloc::sync::Arc; -use alloc::vec::Vec; -use axstd::thread; +use std::sync::Arc; +use std::{thread, time}; +use std::{vec, vec::Vec}; + +#[cfg(any(feature = "axstd", target_os = "arceos"))] +use std::os::arceos::axtask; struct TaskParam { data_len: usize, @@ -56,9 +59,11 @@ fn load(n: &u64) -> u64 { sum } -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { - thread::set_priority(-20); + #[cfg(feature = "axstd")] + axtask::set_priority(-20); + let data = (0..PAYLOAD_KIND) .map(|i| Arc::new(vec![TASK_PARAMS[i].value; TASK_PARAMS[i].data_len])) .collect::>(); @@ -68,15 +73,17 @@ fn main() { } let mut tasks = Vec::with_capacity(PAYLOAD_KIND); - let start_time = axstd::time::Instant::now(); + let start_time = time::Instant::now(); for i in 0..PAYLOAD_KIND { let vec = data[i].clone(); let data_len = TASK_PARAMS[i].data_len; let nice = TASK_PARAMS[i].nice; tasks.push(thread::spawn(move || { + #[cfg(feature = "axstd")] + axtask::set_priority(nice); + let left = 0; let right = data_len; - thread::set_priority(nice); println!( "part {}: {:?} [{}, {})", i, @@ -103,6 +110,7 @@ fn main() { println!("task {} = {}ms", i, time); } + #[cfg(feature = "axstd")] if cfg!(feature = "sched_cfs") && option_env!("AX_SMP") == Some("1") { assert!( leave_times[0] > leave_times[1] diff --git a/apps/task/sleep/Cargo.toml b/apps/task/sleep/Cargo.toml index eb7be63677..c286ff2d7d 100644 --- a/apps/task/sleep/Cargo.toml +++ b/apps/task/sleep/Cargo.toml @@ -10,4 +10,4 @@ authors = ["Yuekai Jia "] sched_rr = ["axstd/sched_rr"] [dependencies] -axstd = { path = "../../../ulib/axstd", features = ["paging", "multitask", "irq"] } +axstd = { path = "../../../ulib/axstd", features = ["multitask", "irq"], optional = true } diff --git a/apps/task/sleep/expect_info_smp4_fifo.out b/apps/task/sleep/expect_info_smp4_fifo.out index 5a514bf0dc..66a9ee0edb 100644 --- a/apps/task/sleep/expect_info_smp4_fifo.out +++ b/apps/task/sleep/expect_info_smp4_fifo.out @@ -12,7 +12,6 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize platform devices... Initialize scheduling... use FIFO scheduler. diff --git a/apps/task/sleep/expect_info_smp4_rr.out b/apps/task/sleep/expect_info_smp4_rr.out index 3a3535d66d..0f3b0c44a6 100644 --- a/apps/task/sleep/expect_info_smp4_rr.out +++ b/apps/task/sleep/expect_info_smp4_rr.out @@ -12,7 +12,6 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize platform devices... Initialize scheduling... use Round-robin scheduler. diff --git a/apps/task/sleep/src/main.rs b/apps/task/sleep/src/main.rs index 3eb6406a62..6cc4f0c2f4 100644 --- a/apps/task/sleep/src/main.rs +++ b/apps/task/sleep/src/main.rs @@ -1,18 +1,19 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] #[macro_use] -extern crate axstd; +#[cfg(feature = "axstd")] +extern crate axstd as std; -use axstd::thread; -use axstd::time::{Duration, Instant}; -use core::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread; +use std::time::{Duration, Instant}; const NUM_TASKS: usize = 5; static FINISHED_TASKS: AtomicUsize = AtomicUsize::new(0); -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { println!("Hello, main task!"); let now = Instant::now(); @@ -23,7 +24,7 @@ fn main() { // backgroud ticks, 0.5s x 30 = 15s thread::spawn(|| { for i in 0..30 { - info!(" tick {}", i); + println!(" tick {}", i); thread::sleep(Duration::from_millis(500)); } }); @@ -37,7 +38,7 @@ fn main() { let now = Instant::now(); thread::sleep(Duration::from_secs(sec as _)); let elapsed = now.elapsed(); - info!("task {} actual sleep {:?} seconds ({}).", i, elapsed, j); + println!("task {} actual sleep {:?} seconds ({}).", i, elapsed, j); } FINISHED_TASKS.fetch_add(1, Ordering::Relaxed); }); diff --git a/apps/task/yield/Cargo.toml b/apps/task/yield/Cargo.toml index f620082af8..228bd7bb57 100644 --- a/apps/task/yield/Cargo.toml +++ b/apps/task/yield/Cargo.toml @@ -11,4 +11,4 @@ sched_rr = ["axstd/sched_rr"] sched_cfs = ["axstd/sched_cfs"] [dependencies] -axstd = { path = "../../../ulib/axstd", features = ["paging", "multitask"] } +axstd = { path = "../../../ulib/axstd", features = ["multitask"], optional = true } diff --git a/apps/task/yield/expect_debug_smp1_fifo.out b/apps/task/yield/expect_debug_smp1_fifo.out new file mode 100644 index 0000000000..afc62269f5 --- /dev/null +++ b/apps/task/yield/expect_debug_smp1_fifo.out @@ -0,0 +1,54 @@ +smp = 1 +build_mode = release +log_level = debug + +Primary CPU 0 started, +Found physcial memory regions: + .text (READ | EXECUTE | RESERVED) + .rodata (READ | RESERVED) + .data (READ | WRITE | RESERVED) + .percpu (READ | WRITE | RESERVED) + boot stack (READ | WRITE | RESERVED) + .bss (READ | WRITE | RESERVED) + free memory (READ | WRITE | FREE) +Initialize global memory allocator... +Initialize platform devices... +Initialize scheduling... + use FIFO scheduler. +Primary CPU 0 init OK. +Hello, main task! +Hello, task 0! id = ThreadId(4) +Hello, task 1! id = ThreadId(5) +Hello, task 2! id = ThreadId(6) +Hello, task 3! id = ThreadId(7) +Hello, task 4! id = ThreadId(8) +Hello, task 5! id = ThreadId(9) +Hello, task 6! id = ThreadId(10) +Hello, task 7! id = ThreadId(11) +Hello, task 8! id = ThreadId(12) +Hello, task 9! id = ThreadId(13) +task block: Task(3, "gc") +task exit: Task(4, ""), exit_code=0 +task unblock: Task(3, "gc") +task exit: Task(5, ""), exit_code=0 +task exit: Task(6, ""), exit_code=0 +task exit: Task(7, ""), exit_code=0 +task exit: Task(8, ""), exit_code=0 +task exit: Task(9, ""), exit_code=0 +task exit: Task(10, ""), exit_code=0 +task exit: Task(11, ""), exit_code=0 +task exit: Task(12, ""), exit_code=0 +task exit: Task(13, ""), exit_code=0 +Task yielding tests run OK! +task exit: Task(2, "main"), exit_code=0 +task drop: Task(4, "") +task drop: Task(5, "") +task drop: Task(6, "") +task drop: Task(7, "") +task drop: Task(8, "") +task drop: Task(9, "") +task drop: Task(10, "") +task drop: Task(11, "") +task drop: Task(12, "") +task drop: Task(13, "") +Shutting down... diff --git a/apps/task/yield/expect_info_smp1_fifo.out b/apps/task/yield/expect_info_smp1_fifo.out deleted file mode 100644 index 8930c2a663..0000000000 --- a/apps/task/yield/expect_info_smp1_fifo.out +++ /dev/null @@ -1,32 +0,0 @@ -smp = 1 -build_mode = release -log_level = info - -Primary CPU 0 started, -Found physcial memory regions: - .text (READ | EXECUTE | RESERVED) - .rodata (READ | RESERVED) - .data (READ | WRITE | RESERVED) - .percpu (READ | WRITE | RESERVED) - boot stack (READ | WRITE | RESERVED) - .bss (READ | WRITE | RESERVED) - free memory (READ | WRITE | FREE) -Initialize global memory allocator... -Initialize kernel page table... -Initialize platform devices... -Initialize scheduling... - use FIFO scheduler. -Primary CPU 0 init OK. -Hello, main task! -Hello, task 0! id = TaskId(4) -Hello, task 1! id = TaskId(5) -Hello, task 2! id = TaskId(6) -Hello, task 3! id = TaskId(7) -Hello, task 4! id = TaskId(8) -Hello, task 5! id = TaskId(9) -Hello, task 6! id = TaskId(10) -Hello, task 7! id = TaskId(11) -Hello, task 8! id = TaskId(12) -Hello, task 9! id = TaskId(13) -Task yielding tests run OK! -Shutting down... diff --git a/apps/task/yield/expect_info_smp4_fifo.out b/apps/task/yield/expect_info_smp4_fifo.out index aa11a55891..923a4ba68d 100644 --- a/apps/task/yield/expect_info_smp4_fifo.out +++ b/apps/task/yield/expect_info_smp4_fifo.out @@ -12,7 +12,6 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize platform devices... Initialize scheduling... use FIFO scheduler. @@ -25,15 +24,15 @@ CPU 1 init OK CPU 2 init OK CPU 3 init OK Hello, main task! -Hello, task 0! id = TaskId(7) -Hello, task 1! id = TaskId(8) -Hello, task 2! id = TaskId(9) -Hello, task 3! id = TaskId(10) -Hello, task 4! id = TaskId(11) -Hello, task 5! id = TaskId(12) -Hello, task 6! id = TaskId(13) -Hello, task 7! id = TaskId(14) -Hello, task 8! id = TaskId(15) -Hello, task 9! id = TaskId(16) +Hello, task 0! id = ThreadId(7) +Hello, task 1! id = ThreadId(8) +Hello, task 2! id = ThreadId(9) +Hello, task 3! id = ThreadId(10) +Hello, task 4! id = ThreadId(11) +Hello, task 5! id = ThreadId(12) +Hello, task 6! id = ThreadId(13) +Hello, task 7! id = ThreadId(14) +Hello, task 8! id = ThreadId(15) +Hello, task 9! id = ThreadId(16) Task yielding tests run OK! Shutting down... diff --git a/apps/task/yield/expect_info_smp4_rr.out b/apps/task/yield/expect_info_smp4_rr.out index ba1179ebd2..278df37f94 100644 --- a/apps/task/yield/expect_info_smp4_rr.out +++ b/apps/task/yield/expect_info_smp4_rr.out @@ -12,7 +12,6 @@ Found physcial memory regions: .bss (READ | WRITE | RESERVED) free memory (READ | WRITE | FREE) Initialize global memory allocator... -Initialize kernel page table... Initialize platform devices... Initialize scheduling... use Round-robin scheduler. @@ -25,15 +24,15 @@ CPU 1 init OK CPU 2 init OK CPU 3 init OK Hello, main task! -Hello, task 0! id = TaskId(7) -Hello, task 1! id = TaskId(8) -Hello, task 2! id = TaskId(9) -Hello, task 3! id = TaskId(10) -Hello, task 4! id = TaskId(11) -Hello, task 5! id = TaskId(12) -Hello, task 6! id = TaskId(13) -Hello, task 7! id = TaskId(14) -Hello, task 8! id = TaskId(15) -Hello, task 9! id = TaskId(16) +Hello, task 0! id = ThreadId(7) +Hello, task 1! id = ThreadId(8) +Hello, task 2! id = ThreadId(9) +Hello, task 3! id = ThreadId(10) +Hello, task 4! id = ThreadId(11) +Hello, task 5! id = ThreadId(12) +Hello, task 6! id = ThreadId(13) +Hello, task 7! id = ThreadId(14) +Hello, task 8! id = ThreadId(15) +Hello, task 9! id = ThreadId(16) Task yielding tests run OK! Shutting down... diff --git a/apps/task/yield/src/main.rs b/apps/task/yield/src/main.rs index b6fd59044b..f8cd27bf26 100644 --- a/apps/task/yield/src/main.rs +++ b/apps/task/yield/src/main.rs @@ -1,15 +1,17 @@ -#![no_std] -#![no_main] +#![cfg_attr(feature = "axstd", no_std)] +#![cfg_attr(feature = "axstd", no_main)] #[macro_use] -extern crate axstd; -use axstd::thread; -use core::sync::atomic::{AtomicUsize, Ordering}; +#[cfg(feature = "axstd")] +extern crate axstd as std; + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::thread; const NUM_TASKS: usize = 10; static FINISHED_TASKS: AtomicUsize = AtomicUsize::new(0); -#[no_mangle] +#[cfg_attr(feature = "axstd", no_mangle)] fn main() { for i in 0..NUM_TASKS { thread::spawn(move || { diff --git a/apps/task/yield/test_cmd b/apps/task/yield/test_cmd index f9a34c127d..bf30e3ba40 100644 --- a/apps/task/yield/test_cmd +++ b/apps/task/yield/test_cmd @@ -1,3 +1,3 @@ -test_one "LOG=info" "expect_info_smp1_fifo.out" +test_one "LOG=debug" "expect_debug_smp1_fifo.out" test_one "SMP=4 LOG=info" "expect_info_smp4_fifo.out" test_one "SMP=4 LOG=info APP_FEATURES=sched_rr" "expect_info_smp4_rr.out" diff --git a/crates/axfs_vfs/src/structs.rs b/crates/axfs_vfs/src/structs.rs index 58c4783dbf..9d0dbfabac 100644 --- a/crates/axfs_vfs/src/structs.rs +++ b/crates/axfs_vfs/src/structs.rs @@ -87,6 +87,12 @@ impl VfsNodePerm { Self::from_bits_truncate(0o755) } + /// Returns the underlying raw `st_mode` bits that contain the standard + /// Unix permissions for this file. + pub const fn mode(&self) -> u32 { + self.bits() as u32 + } + /// Returns a 9-bytes string representation of the permission. /// /// For example, `0o755` is represented as `rwxr-xr-x`. @@ -139,16 +145,41 @@ impl VfsNodePerm { } impl VfsNodeType { - /// Whether the node is a file. + /// Tests whether this node type represents a regular file. pub const fn is_file(self) -> bool { matches!(self, Self::File) } - /// Whether the node is a directory. + /// Tests whether this node type represents a directory. pub const fn is_dir(self) -> bool { matches!(self, Self::Dir) } + /// Tests whether this node type represents a symbolic link. + pub const fn is_symlink(self) -> bool { + matches!(self, Self::SymLink) + } + + /// Returns `true` if this node type is a block device. + pub const fn is_block_device(self) -> bool { + matches!(self, Self::BlockDevice) + } + + /// Returns `true` if this node type is a char device. + pub const fn is_char_device(self) -> bool { + matches!(self, Self::CharDevice) + } + + /// Returns `true` if this node type is a fifo. + pub const fn is_fifo(self) -> bool { + matches!(self, Self::Fifo) + } + + /// Returns `true` if this node type is a socket. + pub const fn is_socket(self) -> bool { + matches!(self, Self::Socket) + } + /// Returns a character representation of the node type. /// /// For example, `d` for directory, `-` for regular file, etc. diff --git a/modules/axfs/src/fops.rs b/modules/axfs/src/fops.rs index ab9878ff3d..a34c1f2edf 100644 --- a/modules/axfs/src/fops.rs +++ b/modules/axfs/src/fops.rs @@ -182,6 +182,15 @@ impl File { Ok(read_len) } + /// Reads the file at the given position. Returns the number of bytes read. + /// + /// It does not update the file cursor. + pub fn read_at(&self, offset: u64, buf: &mut [u8]) -> AxResult { + let node = self.node.access(Cap::READ)?; + let read_len = node.read_at(offset, buf)?; + Ok(read_len) + } + /// Writes the file at the current position. Returns the number of bytes /// written. /// @@ -197,6 +206,16 @@ impl File { Ok(write_len) } + /// Writes the file at the given position. Returns the number of bytes + /// written. + /// + /// It does not update the file cursor. + pub fn write_at(&self, offset: u64, buf: &[u8]) -> AxResult { + let node = self.node.access(Cap::WRITE)?; + let write_len = node.write_at(offset, buf)?; + Ok(write_len) + } + /// Flushes the file, writes all buffered data to the underlying device. pub fn flush(&self) -> AxResult { self.node.access(Cap::WRITE)?.fsync()?; diff --git a/modules/axlog/Cargo.toml b/modules/axlog/Cargo.toml index 5e80b88042..a679060a47 100644 --- a/modules/axlog/Cargo.toml +++ b/modules/axlog/Cargo.toml @@ -25,3 +25,6 @@ log = "0.4" spinlock = { path = "../../crates/spinlock" } crate_interface = { path = "../../crates/crate_interface" } chrono = { version = "0.4", optional = true } + +[dev-dependencies] +axlog = { path = ".", features = ["std"] } diff --git a/modules/axlog/src/lib.rs b/modules/axlog/src/lib.rs index 6ecd82bd45..d3ad70b468 100644 --- a/modules/axlog/src/lib.rs +++ b/modules/axlog/src/lib.rs @@ -7,6 +7,13 @@ //! If it is used in `no_std` environment, the users need to implement the //! [`LogIf`] to provide external functions such as console output. //! +//! To use in the `std` environment, please enable the `std` feature: +//! +//! ```toml +//! [dependencies] +//! axlog = { version = "0.1", features = ["std"] } +//! ``` +//! //! # Cargo features: //! //! - `std`: Use in the `std` environment. If it is enabled, you can use console @@ -18,6 +25,26 @@ //! optimized out to a no-op. //! - `log-level-warn`, `log-level-info`, `log-level-debug`, `log-level-trace`: //! Similar to `log-level-error`. +//! +//! # Examples +//! +//! ``` +//! use axlog::{debug, error, info, trace, warn}; +//! +//! // Initialize the logger. +//! axlog::init(); +//! // Set the maximum log level to `info`. +//! axlog::set_max_level("info"); +//! +//! // The following logs will be printed. +//! error!("error"); +//! warn!("warn"); +//! info!("info"); +//! +//! // The following logs will not be printed. +//! debug!("debug"); +//! trace!("trace"); +//! ``` #![cfg_attr(not(feature = "std"), no_std)] @@ -39,17 +66,17 @@ pub use log::{debug, error, info, trace, warn}; /// the end of the message. #[macro_export] macro_rules! ax_print { - ($fmt: literal $(, $($arg: tt)+)?) => { - $crate::__print_impl(format_args!($fmt $(, $($arg)+)?)); + ($($arg:tt)*) => { + $crate::__print_impl(format_args!($($arg)*)); } } /// Prints to the console, with a newline. #[macro_export] macro_rules! ax_println { - () => { ax_print!("\n") }; - ($fmt: literal $(, $($arg: tt)+)?) => { - $crate::__print_impl(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); + () => { $crate::ax_print!("\n") }; + ($($arg:tt)*) => { + $crate::__print_impl(format_args!("{}\n", format_args!($($arg)*))); } } @@ -197,13 +224,18 @@ impl Log for Logger { fn flush(&self) {} } -#[doc(hidden)] -pub fn __print_impl(args: fmt::Arguments) { +/// Prints the formatted string to the console. +pub fn print_fmt(args: fmt::Arguments) -> fmt::Result { use spinlock::SpinNoIrq; // TODO: more efficient static LOCK: SpinNoIrq<()> = SpinNoIrq::new(()); let _guard = LOCK.lock(); - Logger.write_fmt(args).unwrap(); + Logger.write_fmt(args) +} + +#[doc(hidden)] +pub fn __print_impl(args: fmt::Arguments) { + print_fmt(args).unwrap(); } /// Initializes the logger. diff --git a/ulib/axlibc/Cargo.toml b/ulib/axlibc/Cargo.toml index 2e2fd15442..bad87e7dab 100644 --- a/ulib/axlibc/Cargo.toml +++ b/ulib/axlibc/Cargo.toml @@ -64,7 +64,6 @@ axtask = { path = "../../modules/axtask", optional = true } # Other crates axio = { path = "../../crates/axio" } axerrno = { path = "../../crates/axerrno" } -spinlock = { path = "../../crates/spinlock" } static_assertions = "1.1.0" spin = { version = "0.9" } lazy_static = { version = "1.4", features = ["spin_no_std"] } diff --git a/ulib/axlibc/src/fd_ops.rs b/ulib/axlibc/src/fd_ops.rs index 9f5f9d1596..38112a92ba 100644 --- a/ulib/axlibc/src/fd_ops.rs +++ b/ulib/axlibc/src/fd_ops.rs @@ -2,7 +2,8 @@ use alloc::sync::Arc; use core::ffi::{c_int, c_void}; use axerrno::{LinuxError, LinuxResult}; -use axstd::io::{stdin, stdout, PollState}; +use axio::PollState; +use axstd::io::{stdin, stdout}; use flatten_objects::FlattenObjects; use spin::RwLock; diff --git a/ulib/axlibc/src/lib.rs b/ulib/axlibc/src/lib.rs index b663772d83..952dc002a8 100644 --- a/ulib/axlibc/src/lib.rs +++ b/ulib/axlibc/src/lib.rs @@ -29,7 +29,7 @@ extern crate alloc; /// cbindgen:ignore #[rustfmt::skip] #[path = "./ctypes_gen.rs"] -#[allow(dead_code, non_camel_case_types, non_upper_case_globals, clippy::upper_case_acronyms)] +#[allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals, clippy::upper_case_acronyms)] mod ctypes; #[macro_use] @@ -55,23 +55,12 @@ mod strtod; mod uio; mod errno; +mod rand; mod setjmp; mod stdio; mod sys; mod time; -/// Sets the seed for the random number generator. -#[no_mangle] -pub unsafe extern "C" fn ax_srand(seed: u32) { - axstd::rand::srand(seed); -} - -/// Returns a 32-bit unsigned pseudo random interger. -#[no_mangle] -pub unsafe extern "C" fn ax_rand_u32() -> u32 { - axstd::rand::rand_u32() -} - /// Abort the current process. #[no_mangle] pub unsafe extern "C" fn ax_panic() -> ! { @@ -84,6 +73,8 @@ pub unsafe extern "C" fn ax_exit(exit_code: core::ffi::c_int) -> ! { axstd::thread::exit(exit_code) } +pub use self::rand::{ax_rand_u32, ax_srand}; + #[cfg(feature = "alloc")] pub use self::malloc::{ax_free, ax_malloc}; diff --git a/ulib/axlibc/src/pthread/mod.rs b/ulib/axlibc/src/pthread/mod.rs index 07f9583d7f..e989151d16 100644 --- a/ulib/axlibc/src/pthread/mod.rs +++ b/ulib/axlibc/src/pthread/mod.rs @@ -13,7 +13,7 @@ pub mod mutex; lazy_static::lazy_static! { static ref TID_TO_PTHREAD: RwLock>> = { let mut map = BTreeMap::new(); - let main_task = axstd::thread::current(); + let main_task = axtask::current(); let main_tid = main_task.id().as_u64(); let main_thread = Pthread { inner: main_task.as_task_ref().clone(), @@ -71,7 +71,7 @@ impl Pthread { } fn current_ptr() -> *mut Pthread { - let tid = axstd::thread::current().id().as_u64(); + let tid = axtask::current().id().as_u64(); match TID_TO_PTHREAD.read().get(&tid) { None => core::ptr::null_mut(), Some(ptr) => ptr.0 as *mut Pthread, @@ -107,7 +107,7 @@ impl Pthread { #[no_mangle] pub unsafe extern "C" fn ax_getpid() -> c_int { ax_call_body!(ax_getpid, { - let pid = axstd::thread::current().id().as_u64() as c_int; + let pid = axtask::current().id().as_u64() as c_int; Ok(pid) }) } diff --git a/ulib/axstd/src/rand.rs b/ulib/axlibc/src/rand.rs similarity index 78% rename from ulib/axstd/src/rand.rs rename to ulib/axlibc/src/rand.rs index c416f68371..5ac5daface 100644 --- a/ulib/axstd/src/rand.rs +++ b/ulib/axlibc/src/rand.rs @@ -5,12 +5,14 @@ use core::sync::atomic::{AtomicU64, Ordering::SeqCst}; static SEED: AtomicU64 = AtomicU64::new(0xa2ce_a2ce); /// Sets the seed for the random number generator. -pub fn srand(seed: u32) { +#[no_mangle] +pub unsafe extern "C" fn ax_srand(seed: u32) { SEED.store(seed.wrapping_sub(1) as u64, SeqCst); } /// Returns a 32-bit unsigned pseudo random interger. -pub fn rand_u32() -> u32 { +#[no_mangle] +pub unsafe extern "C" fn ax_rand_u32() -> u32 { let new_seed = SEED.load(SeqCst).wrapping_mul(6364136223846793005) + 1; SEED.store(new_seed, SeqCst); (new_seed >> 33) as u32 diff --git a/ulib/axlibc/src/stdio.rs b/ulib/axlibc/src/stdio.rs index aaed31ec8e..f3baec917d 100644 --- a/ulib/axlibc/src/stdio.rs +++ b/ulib/axlibc/src/stdio.rs @@ -2,12 +2,9 @@ use core::ffi::{c_char, c_int}; use axerrno::LinuxError; use axstd::io::{self, Write}; -use spinlock::SpinNoIrq; #[cfg(feature = "fd")] -use {alloc::sync::Arc, axerrno::LinuxResult, axio::PollState}; - -static LOCK: SpinNoIrq<()> = SpinNoIrq::new(()); // Lock used by `ax_println_str` for C apps +use {alloc::sync::Arc, axerrno::LinuxResult, axio::PollState, axstd::io::Read}; /// Print a string to the global standard output stream. #[no_mangle] @@ -32,9 +29,9 @@ pub unsafe extern "C" fn ax_println_str(buf: *const c_char, count: usize) -> c_i } let bytes = unsafe { core::slice::from_raw_parts(buf as *const u8, count as _) }; - let _guard = LOCK.lock(); - let len = io::stdout().write(bytes)?; - let len = io::stdout().write(b"\n")? + len; + let mut stdout = io::stdout().lock(); + let len = stdout.write(bytes)?; + let len = stdout.write(b"\n")? + len; Ok(len as c_int) }) } @@ -42,7 +39,7 @@ pub unsafe extern "C" fn ax_println_str(buf: *const c_char, count: usize) -> c_i #[cfg(feature = "fd")] impl super::fd_ops::FileLike for axstd::io::Stdin { fn read(&self, buf: &mut [u8]) -> LinuxResult { - Ok(self.read_locked(buf)?) + Ok(self.lock().read(buf)?) } fn write(&self, _buf: &[u8]) -> LinuxResult { @@ -82,7 +79,7 @@ impl super::fd_ops::FileLike for axstd::io::Stdout { } fn write(&self, buf: &[u8]) -> LinuxResult { - Ok(self.write_locked(buf)?) + Ok(self.lock().write(buf)?) } fn stat(&self) -> LinuxResult { diff --git a/ulib/axlibc/src/time.rs b/ulib/axlibc/src/time.rs index 067f009085..845f277820 100644 --- a/ulib/axlibc/src/time.rs +++ b/ulib/axlibc/src/time.rs @@ -42,7 +42,7 @@ pub unsafe extern "C" fn ax_clock_gettime(ts: *mut ctypes::timespec) -> c_int { if ts.is_null() { return Err(LinuxError::EFAULT); } - let now = Instant::now().as_duration().into(); + let now = axhal::time::current_time().into(); unsafe { *ts = now }; debug!("ax_clock_gettime: {}.{:09}s", now.tv_sec, now.tv_nsec); Ok(0) diff --git a/ulib/axstd/Cargo.toml b/ulib/axstd/Cargo.toml index dd578a539b..08945a31ad 100644 --- a/ulib/axstd/Cargo.toml +++ b/ulib/axstd/Cargo.toml @@ -40,6 +40,7 @@ sched_cfs = ["axtask/sched_cfs", "irq"] # File system fs = ["alloc", "axruntime/fs", "dep:axdriver", "dep:axfs"] +myfs = ["axfs?/myfs"] use-ramdisk = ["axdriver?/ramdisk", "axfs?/use-ramdisk"] # Networking diff --git a/ulib/axstd/src/display/mod.rs b/ulib/axstd/src/display/mod.rs deleted file mode 100644 index ccb5c7b40e..0000000000 --- a/ulib/axstd/src/display/mod.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Graphics manipulation operations. - -use axdisplay; - -pub use axdisplay::DisplayInfo; - -/// Returns the framebuffer information. -pub fn framebuffer_info() -> DisplayInfo { - axdisplay::framebuffer_info() -} - -/// Flushes the framebuffer. -pub fn framebuffer_flush() { - axdisplay::framebuffer_flush() -} diff --git a/ulib/axstd/src/env.rs b/ulib/axstd/src/env.rs index f742542f55..9da6fff36e 100644 --- a/ulib/axstd/src/env.rs +++ b/ulib/axstd/src/env.rs @@ -1,4 +1,19 @@ //! Inspection and manipulation of the process’s environment. #[cfg(feature = "fs")] -pub use axfs::api::{current_dir, set_current_dir}; +extern crate alloc; + +#[cfg(feature = "fs")] +use {crate::io, alloc::string::String}; + +/// Returns the current working directory as a [`String`]. +#[cfg(feature = "fs")] +pub fn current_dir() -> io::Result { + axfs::api::current_dir() +} + +/// Changes the current working directory to the specified path. +#[cfg(feature = "fs")] +pub fn set_current_dir(path: &str) -> io::Result<()> { + axfs::api::set_current_dir(path) +} diff --git a/ulib/axstd/src/io/mod.rs b/ulib/axstd/src/io/mod.rs index 75c70fb18d..7a0cf1fc15 100644 --- a/ulib/axstd/src/io/mod.rs +++ b/ulib/axstd/src/io/mod.rs @@ -3,6 +3,26 @@ mod stdio; pub use axio::prelude; -pub use axio::{BufRead, BufReader, Error, PollState, Read, Result, Seek, SeekFrom, Write}; +pub use axio::{BufRead, BufReader, Error, Read, Seek, SeekFrom, Write}; -pub use self::stdio::{stdin, stdout, Stdin, Stdout, __print_impl}; +#[doc(hidden)] +pub use self::stdio::__print_impl; +pub use self::stdio::{stdin, stdout, Stdin, StdinLock, Stdout, StdoutLock}; + +/// A specialized [`Result`] type for I/O operations. +/// +/// This type is broadly used across [`axstd::io`] for any operation which may +/// produce an error. +/// +/// This typedef is generally used to avoid writing out [`io::Error`] directly and +/// is otherwise a direct mapping to [`Result`]. +/// +/// While usual Rust style is to import types directly, aliases of [`Result`] +/// often are not, to make it easier to distinguish between them. [`Result`] is +/// generally assumed to be [`std::result::Result`][`Result`], and so users of this alias +/// will generally use `io::Result` instead of shadowing the [prelude]'s import +/// of [`std::result::Result`][`Result`]. +/// +/// [`axstd::io`]: crate::io +/// [`io::Error`]: Error +pub type Result = axio::Result; diff --git a/ulib/axstd/src/io/stdio.rs b/ulib/axstd/src/io/stdio.rs index b50fb4bf6f..39b0c2acac 100644 --- a/ulib/axstd/src/io/stdio.rs +++ b/ulib/axstd/src/io/stdio.rs @@ -1,34 +1,18 @@ -#[cfg(feature = "alloc")] -use alloc::string::String; +use crate::io::{self, prelude::*, BufReader}; +use crate::sync::{Mutex, MutexGuard}; -use crate::io::{prelude::*, BufReader, Result}; -use crate::sync::Mutex; +#[cfg(feature = "alloc")] +use alloc::{string::String, vec::Vec}; struct StdinRaw; struct StdoutRaw; -/// A handle to the standard input stream of a process. -pub struct Stdin { - inner: &'static Mutex>, -} - -/// A handle to the global standard output stream of the current process. -pub struct Stdout { - inner: &'static Mutex, -} - -impl StdinRaw { - fn getchar() -> Option { - axhal::console::getchar().map(|c| if c == b'\r' { b'\n' } else { c }) - } -} - impl Read for StdinRaw { // Non-blocking read, returns number of bytes read. - fn read(&mut self, buf: &mut [u8]) -> Result { + fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut read_len = 0; while read_len < buf.len() { - if let Some(c) = Self::getchar() { + if let Some(c) = axhal::console::getchar() { buf[read_len] = c; read_len += 1; } else { @@ -40,36 +24,55 @@ impl Read for StdinRaw { } impl Write for StdoutRaw { - fn write(&mut self, buf: &[u8]) -> Result { + fn write(&mut self, buf: &[u8]) -> io::Result { axhal::console::write_bytes(buf); Ok(buf.len()) } - fn flush(&mut self) -> Result { + fn flush(&mut self) -> io::Result<()> { Ok(()) } } +/// A handle to the standard input stream of a process. +pub struct Stdin { + inner: &'static Mutex>, +} + +/// A locked reference to the [`Stdin`] handle. +pub struct StdinLock<'a> { + inner: MutexGuard<'a, BufReader>, +} + impl Stdin { + /// Locks this handle to the standard input stream, returning a readable + /// guard. + /// + /// The lock is released when the returned lock goes out of scope. The + /// returned guard also implements the [`Read`] and [`BufRead`] traits for + /// accessing the underlying data. + pub fn lock(&self) -> StdinLock<'static> { + // Locks this handle with 'static lifetime. This depends on the + // implementation detail that the underlying `Mutex` is static. + StdinLock { + inner: self.inner.lock(), + } + } + /// Locks this handle and reads a line of input, appending it to the specified buffer. #[cfg(feature = "alloc")] - pub fn read_line(&self, buf: &mut String) -> Result { + pub fn read_line(&self, buf: &mut String) -> io::Result { self.inner.lock().read_line(buf) } - - /// TODO: remove this - pub fn read_locked(&self, buf: &mut [u8]) -> Result { - self.inner.lock().read(buf) - } } impl Read for Stdin { // Block until at least one byte is read. - fn read(&mut self, buf: &mut [u8]) -> Result { + fn read(&mut self, buf: &mut [u8]) -> io::Result { let read_len = self.inner.lock().read(buf)?; if buf.is_empty() || read_len > 0 { return Ok(read_len); } - // try again until we get something + // try again until we got something loop { let read_len = self.inner.lock().read(buf)?; if read_len > 0 { @@ -80,22 +83,73 @@ impl Read for Stdin { } } +impl Read for StdinLock<'_> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.inner.read(buf) + } +} + +impl BufRead for StdinLock<'_> { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + self.inner.fill_buf() + } + + fn consume(&mut self, n: usize) { + self.inner.consume(n) + } + + #[cfg(feature = "alloc")] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + self.inner.read_until(byte, buf) + } + + #[cfg(feature = "alloc")] + fn read_line(&mut self, buf: &mut String) -> io::Result { + self.inner.read_line(buf) + } +} + +/// A handle to the global standard output stream of the current process. +pub struct Stdout { + inner: &'static Mutex, +} + +/// A locked reference to the [`Stdout`] handle. +pub struct StdoutLock<'a> { + inner: MutexGuard<'a, StdoutRaw>, +} + impl Stdout { - /// TODO: remove this - pub fn write_locked(&self, buf: &[u8]) -> Result { - self.inner.lock().write(buf) + /// Locks this handle to the standard output stream, returning a writable + /// guard. + /// + /// The lock is released when the returned lock goes out of scope. The + /// returned guard also implements the `Write` trait for writing data. + pub fn lock(&self) -> StdoutLock<'static> { + StdoutLock { + inner: self.inner.lock(), + } } } impl Write for Stdout { - fn write(&mut self, buf: &[u8]) -> Result { + fn write(&mut self, buf: &[u8]) -> io::Result { self.inner.lock().write(buf) } - fn flush(&mut self) -> Result { + fn flush(&mut self) -> io::Result<()> { self.inner.lock().flush() } } +impl Write for StdoutLock<'_> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.inner.write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} + /// Constructs a new handle to the standard input of the current process. pub fn stdin() -> Stdin { static INSTANCE: Mutex> = Mutex::new(BufReader::new(StdinRaw)); @@ -108,35 +162,13 @@ pub fn stdout() -> Stdout { Stdout { inner: &INSTANCE } } -/// Prints to the standard output. -/// -/// Equivalent to the [`println!`] macro except that a newline is not printed at -/// the end of the message. -/// -/// [`println!`]: crate::println -#[macro_export] -macro_rules! print { - ($fmt: literal $(, $($arg: tt)+)?) => { - $crate::io::__print_impl(format_args!($fmt $(, $($arg)+)?)); - } -} - -/// Prints to the standard output, with a newline. -#[macro_export] -macro_rules! println { - () => { $crate::print!("\n") }; - ($fmt: literal $(, $($arg: tt)+)?) => { - $crate::io::__print_impl(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); - } -} - #[doc(hidden)] pub fn __print_impl(args: core::fmt::Arguments) { if cfg!(feature = "smp") { - axlog::__print_impl(args); // synchronize using the lock in axlog + // synchronize using the lock in axlog, to avoid interleaving + // with kernel logs + axlog::print_fmt(args).unwrap(); } else { - static INLINE_LOCK: Mutex<()> = Mutex::new(()); // not break in one line - let _guard = INLINE_LOCK.lock(); - stdout().write_fmt(args).unwrap(); + stdout().lock().write_fmt(args).unwrap(); } } diff --git a/ulib/axstd/src/lib.rs b/ulib/axstd/src/lib.rs index e77e48260c..5770847626 100644 --- a/ulib/axstd/src/lib.rs +++ b/ulib/axstd/src/lib.rs @@ -1,19 +1,19 @@ -//! [ArceOS] user program library, with an interface similar to rust -//! [std], but calling the functions directly -//! in ArceOS modules, instead of using libc and system calls. +//! # The ArceOS Standard Library //! -//! # Cargo Features +//! The [ArceOS] Standard Library is a mini-std library, with an interface similar +//! to rust [std], but calling the functions directly in ArceOS modules, instead +//! of using libc and system calls. +//! +//! ## Cargo Features //! //! - CPU //! - `smp`: Enable SMP (symmetric multiprocessing) support. //! - `fp_simd`: Enable floating point and SIMD support. +//! - Interrupts: +//! - `irq`: Enable interrupt handling support. //! - Memory //! - `alloc`: Enable dynamic memory allocation. //! - `paging`: Enable page table manipulation. -//! - Interrupts: -//! - `irq`: Enable interrupt handling support. This feature is required for -//! some multitask operations, such as [`sync::WaitQueue::wait_timeout`] and -//! non-spinning [`thread::sleep`]. //! - Task management //! - `multitask`: Enable multi-threading support. //! - `sched_fifo`: Use the FIFO cooperative scheduler. @@ -21,6 +21,7 @@ //! - `sched_cfs`: Use the Completely Fair Scheduler (CFS) preemptive scheduler. //! - Device and upperlayer stack //! - `fs`: Enable file system support. +//! - `myfs`: Allow users to define their custom filesystems to override the default. //! - `use-ramdisk`: Use the RAM disk to emulate the block device. //! - `net`: Enable networking support. //! - `dns`: Enable DNS lookup support. @@ -39,12 +40,6 @@ #![feature(doc_auto_cfg)] #![feature(ip_in_core)] -#[allow(unused_imports)] -#[macro_use] -extern crate axlog; - -pub use axlog::{debug, error, info, trace, warn}; - #[cfg(not(test))] extern crate axruntime; @@ -53,22 +48,23 @@ extern crate alloc; #[cfg(feature = "alloc")] #[doc(no_inline)] -pub use alloc::{boxed, format, string, vec}; +pub use alloc::{boxed, collections, format, string, vec}; + +#[doc(no_inline)] +pub use core::{arch, cell, cmp, hint, marker, mem, ops, ptr, slice, str}; + +#[macro_use] +mod macros; pub mod env; pub mod io; -pub mod rand; +pub mod os; +pub mod process; pub mod sync; -pub mod time; - -#[cfg_attr(not(feature = "multitask"), path = "thread/single.rs")] pub mod thread; +pub mod time; #[cfg(feature = "fs")] pub mod fs; - #[cfg(feature = "net")] pub mod net; - -#[cfg(feature = "display")] -pub mod display; diff --git a/ulib/axstd/src/macros.rs b/ulib/axstd/src/macros.rs new file mode 100644 index 0000000000..c7cd6535e9 --- /dev/null +++ b/ulib/axstd/src/macros.rs @@ -0,0 +1,23 @@ +//! Standard library macros + +/// Prints to the standard output. +/// +/// Equivalent to the [`println!`] macro except that a newline is not printed at +/// the end of the message. +/// +/// [`println!`]: crate::println +#[macro_export] +macro_rules! print { + ($($arg:tt)*) => { + $crate::io::__print_impl(format_args!($($arg)*)); + } +} + +/// Prints to the standard output, with a newline. +#[macro_export] +macro_rules! println { + () => { $crate::print!("\n") }; + ($($arg:tt)*) => { + $crate::io::__print_impl(format_args!("{}\n", format_args!($($arg)*))); + } +} diff --git a/ulib/axstd/src/os.rs b/ulib/axstd/src/os.rs new file mode 100644 index 0000000000..bb15590d63 --- /dev/null +++ b/ulib/axstd/src/os.rs @@ -0,0 +1,11 @@ +//! OS-specific functionality. + +/// ArceOS-specific definitions. +pub mod arceos { + #[cfg(feature = "display")] + pub use axdisplay; + #[cfg(feature = "fs")] + pub use axfs; + #[cfg(feature = "multitask")] + pub use axtask; +} diff --git a/ulib/axstd/src/process.rs b/ulib/axstd/src/process.rs new file mode 100644 index 0000000000..932f889254 --- /dev/null +++ b/ulib/axstd/src/process.rs @@ -0,0 +1,10 @@ +//! A module for working with processes. +//! +//! Since ArceOS is a unikernel, there is no concept of processes. The +//! process-related functions will affect the entire system, such as [`exit`] +//! will shutdown the whole system. + +/// Shutdown the whole system. +pub fn exit(_exit_code: i32) -> ! { + axhal::misc::terminate(); +} diff --git a/ulib/axstd/src/sync/mod.rs b/ulib/axstd/src/sync/mod.rs index 20b1962b78..720fac554e 100644 --- a/ulib/axstd/src/sync/mod.rs +++ b/ulib/axstd/src/sync/mod.rs @@ -1,12 +1,14 @@ //! Useful synchronization primitives. -#[cfg(feature = "multitask")] -pub use axsync::{Mutex, MutexGuard}; +#[doc(no_inline)] +pub use core::sync::atomic; -#[cfg(feature = "multitask")] -pub use axtask::WaitQueue; +#[cfg(feature = "alloc")] +#[doc(no_inline)] +pub use alloc::sync::{Arc, Weak}; -pub use spinlock as spin; +#[cfg(feature = "multitask")] +pub use axsync::{Mutex, MutexGuard}; #[cfg(not(feature = "multitask"))] pub use spinlock::{SpinNoIrq as Mutex, SpinNoIrqGuard as MutexGuard}; diff --git a/ulib/axstd/src/thread/mod.rs b/ulib/axstd/src/thread/mod.rs index 7c8fd2bdee..1c690108af 100644 --- a/ulib/axstd/src/thread/mod.rs +++ b/ulib/axstd/src/thread/mod.rs @@ -1,96 +1,9 @@ //! Native threads. -use crate::io; -use alloc::{string::String, sync::Arc}; -use axerrno::ax_err_type; -use axtask::AxTaskRef; -use core::cell::UnsafeCell; - -#[doc(cfg(feature = "multitask"))] -pub use axtask::{current, set_priority, TaskId as ThreadId}; - -/// Thread factory, which can be used in order to configure the properties of -/// a new thread. -/// -/// Methods can be chained on it in order to configure it. -#[derive(Debug)] -#[doc(cfg(feature = "multitask"))] -pub struct Builder { - // A name for the thread-to-be, for identification in panic messages - name: Option, - // The size of the stack for the spawned thread in bytes - stack_size: Option, -} - -impl Builder { - /// Generates the base configuration for spawning a thread, from which - /// configuration methods can be chained. - pub const fn new() -> Builder { - Builder { - name: None, - stack_size: None, - } - } - - /// Names the thread-to-be. - pub fn name(mut self, name: String) -> Builder { - self.name = Some(name); - self - } - - /// Sets the size of the stack (in bytes) for the new thread. - pub fn stack_size(mut self, size: usize) -> Builder { - self.stack_size = Some(size); - self - } - - /// Spawns a new thread by taking ownership of the `Builder`, and returns an - /// [`io::Result`] to its [`JoinHandle`]. - /// - /// The spawned thread may outlive the caller (unless the caller thread - /// is the main thread; the whole process is terminated when the main - /// thread finishes). The join handle can be used to block on - /// termination of the spawned thread. - pub fn spawn(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - unsafe { self.spawn_unchecked(f) } - } - - unsafe fn spawn_unchecked(self, f: F) -> io::Result> - where - F: FnOnce() -> T, - F: Send + 'static, - T: Send + 'static, - { - let name = self.name.unwrap_or_default(); - let stack_size = self.stack_size.unwrap_or(axconfig::TASK_STACK_SIZE); - - let my_packet = Arc::new(Packet { - result: UnsafeCell::new(None), - }); - let their_packet = my_packet.clone(); - - let main = move || { - let ret = f(); - // SAFETY: `their_packet` as been built just above and moved by the - // closure (it is an Arc<...>) and `my_packet` will be stored in the - // same `JoinHandle` as this closure meaning the mutation will be - // safe (not modify it and affect a value far away). - unsafe { *their_packet.result.get() = Some(ret) }; - drop(their_packet); - }; - - let task = axtask::spawn_raw(main, name, stack_size); - Ok(JoinHandle { - task, - packet: my_packet, - }) - } -} +#[cfg(feature = "multitask")] +mod multi; +#[cfg(feature = "multitask")] +pub use multi::*; /// Current thread gives up the CPU time voluntarily, and switches to another /// ready thread. @@ -98,15 +11,25 @@ impl Builder { /// For single-threaded configuration (`multitask` feature is disabled), we just /// relax the CPU and wait for incoming interrupts. pub fn yield_now() { + #[cfg(feature = "multitask")] axtask::yield_now(); + #[cfg(not(feature = "multitask"))] + if cfg!(feature = "irq") { + axhal::arch::wait_for_irqs(); + } else { + core::hint::spin_loop(); + } } /// Exits the current thread. /// /// For single-threaded configuration (`multitask` feature is disabled), /// it directly terminates the main thread and shutdown. -pub fn exit(exit_code: i32) -> ! { - axtask::exit(exit_code); +pub fn exit(_exit_code: i32) -> ! { + #[cfg(feature = "multitask")] + axtask::exit(_exit_code); + #[cfg(not(feature = "multitask"))] + axhal::misc::terminate(); } /// Current thread is going to sleep for the given duration. @@ -122,66 +45,8 @@ pub fn sleep(dur: core::time::Duration) { /// If one of `multitask` or `irq` features is not enabled, it uses busy-wait /// instead. pub fn sleep_until(deadline: axhal::time::TimeValue) { + #[cfg(feature = "multitask")] axtask::sleep_until(deadline); -} - -/// Spawns a new thread, returning a [`JoinHandle`] for it. -/// -/// The join handle provides a [`join`] method that can be used to join the -/// spawned thread. -/// -/// The default task name is an empty string. The default thread stack size is -/// [`axconfig::TASK_STACK_SIZE`]. -/// -/// [`join`]: JoinHandle::join -#[doc(cfg(feature = "multitask"))] -pub fn spawn(f: F) -> JoinHandle -where - F: FnOnce() -> T + Send + 'static, - T: Send + 'static, -{ - Builder::new().spawn(f).expect("failed to spawn thread") -} - -struct Packet { - result: UnsafeCell>, -} - -unsafe impl Sync for Packet {} - -/// An owned permission to join on a thread (block on its termination). -/// -/// A `JoinHandle` *detaches* the associated thread when it is dropped, which -/// means that there is no longer any handle to the thread and no way to `join` -/// on it. -#[doc(cfg(feature = "multitask"))] -pub struct JoinHandle { - task: AxTaskRef, - packet: Arc>, -} - -unsafe impl Send for JoinHandle {} -unsafe impl Sync for JoinHandle {} - -impl JoinHandle { - /// Extracts a handle to the underlying thread. - /// - /// TODO: do not export the type `AxTaskRef`. - pub fn thread(&self) -> &AxTaskRef { - &self.task - } - - /// Waits for the associated thread to finish. - /// - /// This function will return immediately if the associated thread has - /// already finished. - pub fn join(mut self) -> io::Result { - self.task.join(); - Arc::get_mut(&mut self.packet) - .unwrap() - .result - .get_mut() - .take() - .ok_or_else(|| ax_err_type!(BadState)) - } + #[cfg(not(feature = "multitask"))] + axhal::time::busy_wait_until(deadline); } diff --git a/ulib/axstd/src/thread/multi.rs b/ulib/axstd/src/thread/multi.rs new file mode 100644 index 0000000000..5f6c9d610b --- /dev/null +++ b/ulib/axstd/src/thread/multi.rs @@ -0,0 +1,187 @@ +//! Thread APIs for multi-threading configuration. + +extern crate alloc; + +use crate::io; +use alloc::{string::String, sync::Arc}; +use core::{cell::UnsafeCell, num::NonZeroU64}; + +use axerrno::ax_err_type; +use axtask::AxTaskRef; + +/// A unique identifier for a running thread. +#[derive(Eq, PartialEq, Clone, Copy, Debug)] +pub struct ThreadId(NonZeroU64); + +/// A handle to a thread. +pub struct Thread { + id: ThreadId, +} + +impl ThreadId { + /// This returns a numeric identifier for the thread identified by this + /// `ThreadId`. + pub fn as_u64(&self) -> NonZeroU64 { + self.0 + } +} + +impl Thread { + fn from_id(id: u64) -> Self { + Self { + id: ThreadId(NonZeroU64::new(id).unwrap()), + } + } + + /// Gets the thread's unique identifier. + pub fn id(&self) -> ThreadId { + self.id + } +} + +/// Thread factory, which can be used in order to configure the properties of +/// a new thread. +/// +/// Methods can be chained on it in order to configure it. +#[derive(Debug)] +pub struct Builder { + // A name for the thread-to-be, for identification in panic messages + name: Option, + // The size of the stack for the spawned thread in bytes + stack_size: Option, +} + +impl Builder { + /// Generates the base configuration for spawning a thread, from which + /// configuration methods can be chained. + pub const fn new() -> Builder { + Builder { + name: None, + stack_size: None, + } + } + + /// Names the thread-to-be. + pub fn name(mut self, name: String) -> Builder { + self.name = Some(name); + self + } + + /// Sets the size of the stack (in bytes) for the new thread. + pub fn stack_size(mut self, size: usize) -> Builder { + self.stack_size = Some(size); + self + } + + /// Spawns a new thread by taking ownership of the `Builder`, and returns an + /// [`io::Result`] to its [`JoinHandle`]. + /// + /// The spawned thread may outlive the caller (unless the caller thread + /// is the main thread; the whole process is terminated when the main + /// thread finishes). The join handle can be used to block on + /// termination of the spawned thread. + pub fn spawn(self, f: F) -> io::Result> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + unsafe { self.spawn_unchecked(f) } + } + + unsafe fn spawn_unchecked(self, f: F) -> io::Result> + where + F: FnOnce() -> T, + F: Send + 'static, + T: Send + 'static, + { + let name = self.name.unwrap_or_default(); + let stack_size = self.stack_size.unwrap_or(axconfig::TASK_STACK_SIZE); + + let my_packet = Arc::new(Packet { + result: UnsafeCell::new(None), + }); + let their_packet = my_packet.clone(); + + let main = move || { + let ret = f(); + // SAFETY: `their_packet` as been built just above and moved by the + // closure (it is an Arc<...>) and `my_packet` will be stored in the + // same `JoinHandle` as this closure meaning the mutation will be + // safe (not modify it and affect a value far away). + unsafe { *their_packet.result.get() = Some(ret) }; + drop(their_packet); + }; + + let task = axtask::spawn_raw(main, name, stack_size); + Ok(JoinHandle { + thread: Thread::from_id(task.id().as_u64()), + native: task, + packet: my_packet, + }) + } +} + +/// Gets a handle to the thread that invokes it. +pub fn current() -> Thread { + let id = axtask::current().id().as_u64(); + Thread::from_id(id) +} + +/// Spawns a new thread, returning a [`JoinHandle`] for it. +/// +/// The join handle provides a [`join`] method that can be used to join the +/// spawned thread. +/// +/// The default task name is an empty string. The default thread stack size is +/// [`arceos_api::config::TASK_STACK_SIZE`]. +/// +/// [`join`]: JoinHandle::join +pub fn spawn(f: F) -> JoinHandle +where + F: FnOnce() -> T + Send + 'static, + T: Send + 'static, +{ + Builder::new().spawn(f).expect("failed to spawn thread") +} + +struct Packet { + result: UnsafeCell>, +} + +unsafe impl Sync for Packet {} + +/// An owned permission to join on a thread (block on its termination). +/// +/// A `JoinHandle` *detaches* the associated thread when it is dropped, which +/// means that there is no longer any handle to the thread and no way to `join` +/// on it. +pub struct JoinHandle { + native: AxTaskRef, + thread: Thread, + packet: Arc>, +} + +unsafe impl Send for JoinHandle {} +unsafe impl Sync for JoinHandle {} + +impl JoinHandle { + /// Extracts a handle to the underlying thread. + pub fn thread(&self) -> &Thread { + &self.thread + } + + /// Waits for the associated thread to finish. + /// + /// This function will return immediately if the associated thread has + /// already finished. + pub fn join(mut self) -> io::Result { + self.native.join().ok_or_else(|| ax_err_type!(BadState))?; + Arc::get_mut(&mut self.packet) + .unwrap() + .result + .get_mut() + .take() + .ok_or_else(|| ax_err_type!(BadState)) + } +} diff --git a/ulib/axstd/src/thread/single.rs b/ulib/axstd/src/thread/single.rs deleted file mode 100644 index e6b1a6d575..0000000000 --- a/ulib/axstd/src/thread/single.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! Thread APIs for single-threaded configuration. - -/// Current thread gives up the CPU time voluntarily, and switches to another -/// ready thread. -/// -/// For single-threaded configuration (`multitask` feature is disabled), we just -/// relax the CPU and wait for incoming interrupts. -pub fn yield_now() { - if cfg!(feature = "irq") { - axhal::arch::wait_for_irqs(); - } else { - core::hint::spin_loop(); - } -} - -/// Exits the current thread. -/// -/// For single-threaded configuration (`multitask` feature is disabled), -/// it directly terminates the main thread and shutdown. -pub fn exit(exit_code: i32) -> ! { - axlog::debug!("main task exited: exit_code={}", exit_code); - axhal::misc::terminate() -} - -/// Current thread is going to sleep for the given duration. -/// -/// If one of `multitask` or `irq` features is not enabled, it uses busy-wait -/// instead. -pub fn sleep(dur: core::time::Duration) { - sleep_until(axhal::time::current_time() + dur); -} - -/// Current thread is going to sleep, it will be woken up at the given deadline. -/// -/// If one of `multitask` or `irq` features is not enabled, it uses busy-wait -/// instead. -pub fn sleep_until(deadline: axhal::time::TimeValue) { - axhal::time::busy_wait_until(deadline); -} diff --git a/ulib/axstd/src/time.rs b/ulib/axstd/src/time.rs index 9baf656c28..a2f25d20a1 100644 --- a/ulib/axstd/src/time.rs +++ b/ulib/axstd/src/time.rs @@ -1,11 +1,14 @@ //! Temporal quantification. +use axhal::time::TimeValue; +use core::ops::{Add, AddAssign, Sub, SubAssign}; + pub use core::time::Duration; /// A measurement of a monotonically nondecreasing clock. /// Opaque and useful only with [`Duration`]. #[derive(Clone, Copy)] -pub struct Instant(Duration); +pub struct Instant(TimeValue); impl Instant { /// Returns an instant corresponding to "now". @@ -13,12 +16,6 @@ impl Instant { Instant(axhal::time::current_time()) } - /// Converts an `Instant` to a `Duration` directly. - #[allow(dead_code)] - pub fn as_duration(&self) -> Duration { - self.0 - } - /// Returns the amount of time elapsed from another instant to this one, /// or zero duration if that instant is later than this one. /// @@ -27,7 +24,7 @@ impl Instant { /// Previous rust versions panicked when `earlier` was later than `self`. Currently this /// method saturates. Future versions may reintroduce the panic in some circumstances. pub fn duration_since(&self, earlier: Instant) -> Duration { - self.0 - earlier.0 + self.0.checked_sub(earlier.0).unwrap_or_default() } /// Returns the amount of time elapsed since this instant was created. @@ -37,6 +34,64 @@ impl Instant { /// Previous rust versions panicked when the current time was earlier than self. Currently this /// method returns a Duration of zero in that case. Future versions may reintroduce the panic. pub fn elapsed(&self) -> Duration { - Instant::now().0 - self.0 + Instant::now() - *self + } + + /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + pub fn checked_add(&self, duration: Duration) -> Option { + self.0.checked_add(duration).map(Instant) + } + + /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as + /// `Instant` (which means it's inside the bounds of the underlying data structure), `None` + /// otherwise. + pub fn checked_sub(&self, duration: Duration) -> Option { + self.0.checked_sub(duration).map(Instant) + } +} + +impl Add for Instant { + type Output = Instant; + + /// # Panics + /// + /// This function may panic if the resulting point in time cannot be represented by the + /// underlying data structure. + fn add(self, other: Duration) -> Instant { + self.checked_add(other) + .expect("overflow when adding duration to instant") + } +} + +impl AddAssign for Instant { + fn add_assign(&mut self, other: Duration) { + *self = *self + other; + } +} + +impl Sub for Instant { + type Output = Instant; + + fn sub(self, other: Duration) -> Instant { + self.checked_sub(other) + .expect("overflow when subtracting duration from instant") + } +} + +impl SubAssign for Instant { + fn sub_assign(&mut self, other: Duration) { + *self = *self - other; + } +} + +impl Sub for Instant { + type Output = Duration; + + /// Returns the amount of time elapsed from another instant to this one, + /// or zero duration if that instant is later than this one. + fn sub(self, other: Instant) -> Duration { + self.duration_since(other) } }