Skip to content

Commit

Permalink
impl Valuable for atomic types (#31)
Browse files Browse the repository at this point in the history
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 #12.
  • Loading branch information
taiki-e authored May 17, 2021
1 parent fd0fb8e commit 322ee49
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 45 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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...
Expand All @@ -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:
Expand Down
57 changes: 57 additions & 0 deletions ci/no_atomic.sh
Original file line number Diff line number Diff line change
@@ -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"
28 changes: 0 additions & 28 deletions ci/no_atomic_cas.sh

This file was deleted.

28 changes: 27 additions & 1 deletion tests/tests/value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use valuable::*;

use core::{isize, usize};
use core::sync::atomic;

macro_rules! assert_visit_call {
($v:expr) => {
Expand Down Expand Up @@ -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<T: PartialEq>(a: &T, b: &T) -> bool {
*a == *b
}
Expand Down
14 changes: 11 additions & 3 deletions valuable/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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");
}
57 changes: 57 additions & 0 deletions valuable/no_atomic.rs
Original file line number Diff line number Diff line change
@@ -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",
];
11 changes: 0 additions & 11 deletions valuable/no_atomic_cas.rs

This file was deleted.

43 changes: 43 additions & 0 deletions valuable/src/valuable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T: Valuable> Valuable for Wrapping<T> {
fn as_value(&self) -> Value<'_> {
self.0.as_value()
Expand Down

0 comments on commit 322ee49

Please sign in to comment.