Skip to content

Commit b4480de

Browse files
alexandruagalxiord
authored andcommitted
expand the AtomicInteger trait
Turn `AtomicInteger` into a trait that abstracts away atomic operations (strange that Rust does not have this already). We only include `load` and `store` for now, but others can be added later. Signed-off-by: Alexandru Agache <[email protected]>
1 parent 961e63e commit b4480de

File tree

3 files changed

+101
-36
lines changed

3 files changed

+101
-36
lines changed

src/atomic_integer.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
3+
4+
use std::sync::atomic::Ordering;
5+
6+
/// Objects that implement this trait must consist exclusively of atomic types
7+
/// from [`std::sync::atomic`](https://doc.rust-lang.org/std/sync/atomic/), except for
8+
/// [`AtomicPtr<T>`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicPtr.html) and
9+
/// [`AtomicBool`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html).
10+
pub unsafe trait AtomicInteger: Sync + Send {
11+
/// The raw value type associated with the atomic integer (i.e. `u16` for `AtomicU16`).
12+
type V;
13+
14+
/// Create a new instance of `Self`.
15+
fn new(v: Self::V) -> Self;
16+
17+
/// Loads a value from the atomic integer.
18+
fn load(&self, order: Ordering) -> Self::V;
19+
20+
/// Stores a value into the atomic integer.
21+
fn store(&self, val: Self::V, order: Ordering);
22+
}
23+
24+
macro_rules! impl_atomic_integer_ops {
25+
($T:path, $V:ty) => {
26+
unsafe impl AtomicInteger for $T {
27+
type V = $V;
28+
29+
fn new(v: Self::V) -> Self {
30+
Self::new(v)
31+
}
32+
33+
fn load(&self, order: Ordering) -> Self::V {
34+
self.load(order)
35+
}
36+
37+
fn store(&self, val: Self::V, order: Ordering) {
38+
self.store(val, order)
39+
}
40+
}
41+
};
42+
}
43+
44+
// TODO: Detect availability using #[cfg(target_has_atomic) when it is stabilized.
45+
// Right now we essentially assume we're running on either x86 or Arm (32 or 64 bit). AFAIK,
46+
// Rust starts using additional synchronization primitives to implement atomics when they're
47+
// not natively available, and that doesn't interact safely with how we cast pointers to
48+
// atomic value references. We should be wary of this when looking at a broader range of
49+
// platforms.
50+
51+
impl_atomic_integer_ops!(std::sync::atomic::AtomicI8, i8);
52+
impl_atomic_integer_ops!(std::sync::atomic::AtomicI16, i16);
53+
impl_atomic_integer_ops!(std::sync::atomic::AtomicI32, i32);
54+
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
55+
impl_atomic_integer_ops!(std::sync::atomic::AtomicI64, i64);
56+
57+
impl_atomic_integer_ops!(std::sync::atomic::AtomicU8, u8);
58+
impl_atomic_integer_ops!(std::sync::atomic::AtomicU16, u16);
59+
impl_atomic_integer_ops!(std::sync::atomic::AtomicU32, u32);
60+
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
61+
impl_atomic_integer_ops!(std::sync::atomic::AtomicU64, u64);
62+
63+
impl_atomic_integer_ops!(std::sync::atomic::AtomicIsize, isize);
64+
impl_atomic_integer_ops!(std::sync::atomic::AtomicUsize, usize);
65+
66+
#[cfg(test)]
67+
mod tests {
68+
use super::*;
69+
70+
use std::fmt::Debug;
71+
use std::sync::atomic::AtomicU32;
72+
73+
fn check_atomic_integer_ops<A: AtomicInteger>()
74+
where
75+
A::V: Copy + Debug + From<u8> + PartialEq,
76+
{
77+
let v = A::V::from(0);
78+
let a = A::new(v);
79+
assert_eq!(a.load(Ordering::Relaxed), v);
80+
81+
let v2 = A::V::from(100);
82+
a.store(v2, Ordering::Relaxed);
83+
assert_eq!(a.load(Ordering::Relaxed), v2);
84+
}
85+
86+
#[test]
87+
fn test_atomic_integer_ops() {
88+
check_atomic_integer_ops::<AtomicU32>()
89+
}
90+
}

src/lib.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
pub mod address;
2424
pub use address::{Address, AddressValue};
2525

26+
#[cfg(feature = "backend-atomic")]
27+
pub mod atomic;
28+
#[cfg(feature = "backend-atomic")]
29+
pub use atomic::{GuestMemoryAtomic, GuestMemoryLoadGuard};
30+
31+
mod atomic_integer;
32+
pub use atomic_integer::AtomicInteger;
33+
2634
pub mod bytes;
2735
pub use bytes::{ByteValued, Bytes};
2836

@@ -46,13 +54,8 @@ pub mod mmap;
4654
#[cfg(feature = "backend-mmap")]
4755
pub use mmap::{Error, GuestMemoryMmap, GuestRegionMmap, MmapRegion};
4856

49-
#[cfg(feature = "backend-atomic")]
50-
pub mod atomic;
51-
#[cfg(feature = "backend-atomic")]
52-
pub use atomic::{GuestMemoryAtomic, GuestMemoryLoadGuard};
53-
5457
pub mod volatile_memory;
5558
pub use volatile_memory::{
56-
AtomicInteger, Error as VolatileMemoryError, Result as VolatileMemoryResult, VolatileArrayRef,
57-
VolatileMemory, VolatileRef, VolatileSlice,
59+
Error as VolatileMemoryError, Result as VolatileMemoryResult, VolatileArrayRef, VolatileMemory,
60+
VolatileRef, VolatileSlice,
5861
};

src/volatile_memory.rs

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use std::result;
3636
use std::slice::{from_raw_parts, from_raw_parts_mut};
3737
use std::usize;
3838

39+
use crate::atomic_integer::AtomicInteger;
3940
use crate::{ByteValued, Bytes};
4041

4142
use copy_slice_impl::copy_slice;
@@ -119,35 +120,6 @@ pub fn compute_offset(base: usize, offset: usize) -> Result<usize> {
119120
}
120121
}
121122

122-
/// Types that can be read safely from a [`VolatileSlice`](struct.VolatileSlice.html).
123-
///
124-
/// Objects that implement this trait must consist exclusively of atomic types
125-
/// from [`std::sync::atomic`](https://doc.rust-lang.org/std/sync/atomic/), except for
126-
/// [`AtomicPtr<T>`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicPtr.html).
127-
pub unsafe trait AtomicInteger: Sync + Send {}
128-
129-
// TODO: Detect availability using #[cfg(target_has_atomic) when it is stabilized.
130-
// Right now we essentially assume we're running on either x86 or Arm (32 or 64 bit). AFAIK,
131-
// Rust starts using additional synchronization primitives to implement atomics when they're
132-
// not natively available, and that doesn't interact safely with how we cast pointers to
133-
// atomic value references. We should be wary of this when looking at a broader range of
134-
// platforms.
135-
136-
unsafe impl AtomicInteger for std::sync::atomic::AtomicI8 {}
137-
unsafe impl AtomicInteger for std::sync::atomic::AtomicI16 {}
138-
unsafe impl AtomicInteger for std::sync::atomic::AtomicI32 {}
139-
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
140-
unsafe impl AtomicInteger for std::sync::atomic::AtomicI64 {}
141-
142-
unsafe impl AtomicInteger for std::sync::atomic::AtomicU8 {}
143-
unsafe impl AtomicInteger for std::sync::atomic::AtomicU16 {}
144-
unsafe impl AtomicInteger for std::sync::atomic::AtomicU32 {}
145-
#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
146-
unsafe impl AtomicInteger for std::sync::atomic::AtomicU64 {}
147-
148-
unsafe impl AtomicInteger for std::sync::atomic::AtomicIsize {}
149-
unsafe impl AtomicInteger for std::sync::atomic::AtomicUsize {}
150-
151123
/// Types that support raw volatile access to their data.
152124
pub trait VolatileMemory {
153125
/// Gets the size of this slice.

0 commit comments

Comments
 (0)