From 322ee49d82ab3202224709b8cabb2b1b7b0a36c9 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 18 May 2021 02:01:20 +0900 Subject: [PATCH] impl Valuable for atomic types (#31) This implements Valuable for the following types. - AtomicBool - AtomicI8 - AtomicI16 - AtomicI32 - AtomicI64 - AtomicIsize - AtomicU8 - AtomicU16 - AtomicU32 - AtomicU64 - AtomicUsize In some targets, atomic u64/i64 is not available or atomic is not available at all, so this detects those targets by using the same way as https://github.com/tokio-rs/valuable/pull/12. --- .github/workflows/ci.yml | 8 ++++-- ci/no_atomic.sh | 57 +++++++++++++++++++++++++++++++++++++++ ci/no_atomic_cas.sh | 28 ------------------- tests/tests/value.rs | 28 ++++++++++++++++++- valuable/build.rs | 14 +++++++--- valuable/no_atomic.rs | 57 +++++++++++++++++++++++++++++++++++++++ valuable/no_atomic_cas.rs | 11 -------- valuable/src/valuable.rs | 43 +++++++++++++++++++++++++++++ 8 files changed, 201 insertions(+), 45 deletions(-) create mode 100755 ci/no_atomic.sh delete mode 100755 ci/no_atomic_cas.sh create mode 100644 valuable/no_atomic.rs delete mode 100644 valuable/no_atomic_cas.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d5a7fc..345d95a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,9 +25,13 @@ jobs: no-std: strategy: matrix: + # thumbv7m-none-eabi supports atomic CAS. + # thumbv6m-none-eabi supports atomic, but not atomic CAS. + # riscv32i-unknown-none-elf does not support atomic at all. target: - thumbv6m-none-eabi - thumbv7m-none-eabi + - riscv32i-unknown-none-elf runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -53,7 +57,7 @@ jobs: run: cargo install cargo-hack - run: cargo hack build --workspace --feature-powerset - # When this job failed, run ci/no_atomic_cas.sh and commit result changes. + # When this job failed, run ci/no_atomic.sh and commit result changes. # TODO(taiki-e): Ideally, this should be automated using a bot that creates # PR when failed, but there is no bandwidth to implement it # right now... @@ -63,7 +67,7 @@ jobs: - uses: actions/checkout@v2 - name: Install Rust run: rustup update nightly && rustup default nightly - - run: ci/no_atomic_cas.sh + - run: ci/no_atomic.sh - run: git diff --exit-code fmt: diff --git a/ci/no_atomic.sh b/ci/no_atomic.sh new file mode 100755 index 0000000..929440d --- /dev/null +++ b/ci/no_atomic.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Update the list of targets that do not support atomic/CAS operations. +# +# Usage: +# ./ci/no_atomic.sh + +set -euo pipefail +IFS=$'\n\t' + +cd "$(cd "$(dirname "$0")" && pwd)"/.. + +file="valuable/no_atomic.rs" + +{ + echo "// This file is @generated by $(basename "$0")." + echo "// It is not intended for manual editing." + echo "" +} >"$file" + +echo "const NO_ATOMIC_CAS: &[&str] = &[" >>"$file" +for target in $(rustc --print target-list); do + res=$(rustc --print target-spec-json -Z unstable-options --target "$target" \ + | jq -r "select(.\"atomic-cas\" == false)") + [[ -z "$res" ]] || echo " \"$target\"," >>"$file" +done +echo "];" >>"$file" + +echo "const NO_ATOMIC_64: &[&str] = &[" >>"$file" +for target in $(rustc --print target-list); do + res=$(rustc --print target-spec-json -Z unstable-options --target "$target" \ + | jq -r "select(.\"max-atomic-width\" == 32)") + [[ -z "$res" ]] || echo " \"$target\"," >>"$file" +done +# It is not clear exactly what `"max-atomic-width" == null` means, but they +# actually seem to have the same max-atomic-width as the target-pointer-width. +# The targets currently included in this group are "mipsel-sony-psp", +# "thumbv4t-none-eabi", "thumbv6m-none-eabi", all of which are +# `"target-pointer-width" == "32"`, so assuming them `"max-atomic-width" == 32` +# for now. +for target in $(rustc --print target-list); do + res=$(rustc --print target-spec-json -Z unstable-options --target "$target" \ + | jq -r "select(.\"max-atomic-width\" == null)") + [[ -z "$res" ]] || echo " \"$target\"," >>"$file" +done +echo "];" >>"$file" + +# There is no `"max-atomic-width" == 16` or `"max-atomic-width" == 8` targets. + +# `"max-atomic-width" == 0` means that atomic is not supported at all. +echo "const NO_ATOMIC: &[&str] = &[" >>"$file" +for target in $(rustc --print target-list); do + res=$(rustc --print target-spec-json -Z unstable-options --target "$target" \ + | jq -r "select(.\"max-atomic-width\" == 0)") + [[ -z "$res" ]] || echo " \"$target\"," >>"$file" +done +echo "];" >>"$file" diff --git a/ci/no_atomic_cas.sh b/ci/no_atomic_cas.sh deleted file mode 100755 index 761e1c2..0000000 --- a/ci/no_atomic_cas.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -# Update the list of targets that do not support atomic CAS operations. -# -# Usage: -# ./ci/no_atomic_cas.sh - -set -euo pipefail -IFS=$'\n\t' - -cd "$(cd "$(dirname "$0")" && pwd)"/.. - -file="valuable/no_atomic_cas.rs" - -{ - echo "// This file is @generated by $(basename "$0")." - echo "// It is not intended for manual editing." - echo "" - echo "const NO_ATOMIC_CAS_TARGETS: &[&str] = &[" -} >"$file" - -for target in $(rustc --print target-list); do - res=$(rustc --print target-spec-json -Z unstable-options --target "$target" \ - | jq -r "select(.\"atomic-cas\" == false)") - [[ -z "$res" ]] || echo " \"$target\"," >>"$file" -done - -echo "];" >>"$file" diff --git a/tests/tests/value.rs b/tests/tests/value.rs index 29edc37..ad1c2fe 100644 --- a/tests/tests/value.rs +++ b/tests/tests/value.rs @@ -1,6 +1,6 @@ use valuable::*; -use core::{isize, usize}; +use core::sync::atomic; macro_rules! assert_visit_call { ($v:expr) => { @@ -311,6 +311,32 @@ fn test_option() { assert!(matches!(val, Value::Unit)); } +#[test] +fn test_atomic() { + let val = atomic::AtomicBool::new(true); + assert!(matches!(val.as_value(), Value::Bool(v) if v)); + let val = atomic::AtomicI8::new(i8::MAX); + assert!(matches!(val.as_value(), Value::I8(v) if v == i8::MAX)); + let val = atomic::AtomicI16::new(i16::MAX); + assert!(matches!(val.as_value(), Value::I16(v) if v == i16::MAX)); + let val = atomic::AtomicI32::new(i32::MAX); + assert!(matches!(val.as_value(), Value::I32(v) if v == i32::MAX)); + let val = atomic::AtomicI64::new(i64::MAX); + assert!(matches!(val.as_value(), Value::I64(v) if v == i64::MAX)); + let val = atomic::AtomicIsize::new(isize::MAX); + assert!(matches!(val.as_value(), Value::Isize(v) if v == isize::MAX)); + let val = atomic::AtomicU8::new(u8::MAX); + assert!(matches!(val.as_value(), Value::U8(v) if v == u8::MAX)); + let val = atomic::AtomicU16::new(u16::MAX); + assert!(matches!(val.as_value(), Value::U16(v) if v == u16::MAX)); + let val = atomic::AtomicU32::new(u32::MAX); + assert!(matches!(val.as_value(), Value::U32(v) if v == u32::MAX)); + let val = atomic::AtomicU64::new(u64::MAX); + assert!(matches!(val.as_value(), Value::U64(v) if v == u64::MAX)); + let val = atomic::AtomicUsize::new(usize::MAX); + assert!(matches!(val.as_value(), Value::Usize(v) if v == usize::MAX)); +} + fn eq(a: &T, b: &T) -> bool { *a == *b } diff --git a/valuable/build.rs b/valuable/build.rs index 029d644..ad0dd06 100644 --- a/valuable/build.rs +++ b/valuable/build.rs @@ -2,7 +2,7 @@ use std::env; -include!("no_atomic_cas.rs"); +include!("no_atomic.rs"); // The rustc-cfg strings below are *not* public API. Please let us know by // opening a GitHub issue if your build environment requires some way to enable @@ -23,9 +23,17 @@ fn main() { // `cfg(target_has_atomic = "ptr")` as true when the build script doesn't // run. This is needed for compatibility with non-cargo build systems that // don't run the build script. - if NO_ATOMIC_CAS_TARGETS.contains(&&*target) { + if NO_ATOMIC_CAS.contains(&&*target) { println!("cargo:rustc-cfg=valuable_no_atomic_cas"); } + if NO_ATOMIC.contains(&&*target) { + println!("cargo:rustc-cfg=valuable_no_atomic"); + println!("cargo:rustc-cfg=valuable_no_atomic_64"); + } else if NO_ATOMIC_64.contains(&&*target) { + println!("cargo:rustc-cfg=valuable_no_atomic_64"); + } else { + // Otherwise, assuming `"max-atomic-width" == 64`. + } - println!("cargo:rerun-if-changed=no_atomic_cas.rs"); + println!("cargo:rerun-if-changed=no_atomic.rs"); } diff --git a/valuable/no_atomic.rs b/valuable/no_atomic.rs new file mode 100644 index 0000000..832a60a --- /dev/null +++ b/valuable/no_atomic.rs @@ -0,0 +1,57 @@ +// This file is @generated by no_atomic.sh. +// It is not intended for manual editing. + +const NO_ATOMIC_CAS: &[&str] = &[ + "avr-unknown-gnu-atmega328", + "msp430-none-elf", + "riscv32i-unknown-none-elf", + "riscv32imc-unknown-none-elf", + "thumbv4t-none-eabi", + "thumbv6m-none-eabi", +]; +const NO_ATOMIC_64: &[&str] = &[ + "arm-linux-androideabi", + "armebv7r-none-eabi", + "armebv7r-none-eabihf", + "armv4t-unknown-linux-gnueabi", + "armv5te-unknown-linux-gnueabi", + "armv5te-unknown-linux-musleabi", + "armv5te-unknown-linux-uclibceabi", + "armv7r-none-eabi", + "armv7r-none-eabihf", + "hexagon-unknown-linux-musl", + "mips-unknown-linux-gnu", + "mips-unknown-linux-musl", + "mips-unknown-linux-uclibc", + "mipsel-unknown-linux-gnu", + "mipsel-unknown-linux-musl", + "mipsel-unknown-linux-uclibc", + "mipsel-unknown-none", + "mipsisa32r6-unknown-linux-gnu", + "mipsisa32r6el-unknown-linux-gnu", + "powerpc-unknown-linux-gnu", + "powerpc-unknown-linux-gnuspe", + "powerpc-unknown-linux-musl", + "powerpc-unknown-netbsd", + "powerpc-unknown-openbsd", + "powerpc-wrs-vxworks", + "powerpc-wrs-vxworks-spe", + "riscv32gc-unknown-linux-gnu", + "riscv32gc-unknown-linux-musl", + "riscv32imac-unknown-none-elf", + "thumbv7em-none-eabi", + "thumbv7em-none-eabihf", + "thumbv7m-none-eabi", + "thumbv8m.base-none-eabi", + "thumbv8m.main-none-eabi", + "thumbv8m.main-none-eabihf", + "mipsel-sony-psp", + "thumbv4t-none-eabi", + "thumbv6m-none-eabi", +]; +const NO_ATOMIC: &[&str] = &[ + "avr-unknown-gnu-atmega328", + "msp430-none-elf", + "riscv32i-unknown-none-elf", + "riscv32imc-unknown-none-elf", +]; diff --git a/valuable/no_atomic_cas.rs b/valuable/no_atomic_cas.rs deleted file mode 100644 index 0819af1..0000000 --- a/valuable/no_atomic_cas.rs +++ /dev/null @@ -1,11 +0,0 @@ -// This file is @generated by no_atomic_cas.sh. -// It is not intended for manual editing. - -const NO_ATOMIC_CAS_TARGETS: &[&str] = &[ - "avr-unknown-gnu-atmega328", - "msp430-none-elf", - "riscv32i-unknown-none-elf", - "riscv32imc-unknown-none-elf", - "thumbv4t-none-eabi", - "thumbv6m-none-eabi", -]; diff --git a/valuable/src/valuable.rs b/valuable/src/valuable.rs index dd32d05..dfbda80 100644 --- a/valuable/src/valuable.rs +++ b/valuable/src/valuable.rs @@ -143,6 +143,49 @@ nonzero! { Usize(NonZeroUsize), } +#[cfg(not(valuable_no_atomic))] +macro_rules! atomic { + ( + $( + $(#[$attrs:meta])* + $variant:ident($ty:ident), + )* + ) => { + $( + $(#[$attrs])* + impl Valuable for core::sync::atomic::$ty { + fn as_value(&self) -> Value<'_> { + // Use SeqCst to match Debug and serde which use SeqCst. + // https://github.com/rust-lang/rust/blob/1.52.1/library/core/src/sync/atomic.rs#L1361-L1366 + // https://github.com/serde-rs/serde/issues/1496 + Value::$variant(self.load(core::sync::atomic::Ordering::SeqCst)) + } + + fn visit(&self, visit: &mut dyn Visit) { + visit.visit_value(self.as_value()); + } + } + )* + }; +} + +#[cfg(not(valuable_no_atomic))] +atomic! { + Bool(AtomicBool), + I8(AtomicI8), + I16(AtomicI16), + I32(AtomicI32), + #[cfg(not(valuable_no_atomic_64))] + I64(AtomicI64), + Isize(AtomicIsize), + U8(AtomicU8), + U16(AtomicU16), + U32(AtomicU32), + #[cfg(not(valuable_no_atomic_64))] + U64(AtomicU64), + Usize(AtomicUsize), +} + impl Valuable for Wrapping { fn as_value(&self) -> Value<'_> { self.0.as_value()