volatile_mem/lib.rs
1//! This crate provides a type, [`Volatile`], for managing volatile memory or
2//! data.
3//!
4//! The [`Volatile`] object does not contain a pointer or reference to the
5//! volatile memory, but is a container of the volatile data itself. This means
6//! that a pointer to a [`Volatile`] object is a pointer to the volatile memory.
7//! As such, it would make little or no sense to create a local variable or
8//! parameter of type [`Volatile`]. You would typically use some kind of pointer
9//! or reference to the [`Volatile`] object instead.
10//!
11//! Besides [`Volatile`], the crate provides two additional volatile types. They
12//! are [`VolatileReadOnly`], and [`VolatileWriteOnly`]. These are technically
13//! just type definitions which alias read-only and write-only variants of
14//! [`Volatile`], respectively. However, those variants are only available
15//! through these aliases. The default variant for [`Volatile`] allows both
16//! reads and writes.
17//!
18//! [`Volatile`] is meant for reading from or writing to memory used for
19//! communication with some process external to the program. A common use case
20//! would be memory-mapped I/O.
21//!
22//! # Safety
23//! Typically, [`Volatile`] would be created from a raw pointer, which carries
24//! with it the typical [pointer safety concerns](core::ptr#safety). In
25//! particular, the following must be guaranteed regarding the location of the
26//! volatile memory.
27//!
28//! - The memory must be [valid](core::ptr#safety) for reads and/or writes.
29//!
30//! - The memory must be properly aligned.
31//!
32//! - The memory must point to a properly initialized for the data type, unless
33//! the [`Volatile`] is [write-only](VolatileWriteOnly).
34//!
35//! Note that even if the data has size zero, the pointer must be non-NULL and
36//! properly aligned.
37//!
38//! Do not forget that even creating a reference to uninitialized data (even if
39//! that data is never used) is immediate undefined behavior. As such, do not at
40//! any point create a reference directly to uninitialized data (as opposed to a
41//! reference to [`VolatileWriteOnly`] or [`MaybeUninit`](core::mem::MaybeUninit),
42//! each of which can safely handle uninitialized data).
43//!
44//! Just like in C, whether an operation is volatile has no bearing whatsoever
45//! on questions involving concurrent access from multiple threads. Volatile
46//! accesses behave exactly like non-atomic accesses in that regard. In
47//! particular, a race between a write operation and any other operation
48//! (reading or writing) to the same location is undefined behavior.
49//!
50//! # Disclaimer
51//! The Rust documentation contains the following note regarding volatile reads
52//! and writes:
53//!
54//! > Rust does not currently have a rigorously and formally defined memory
55//! > model, so the precise semantics of what "volatile" means here is subject
56//! > to change over time. That being said, the semantics will almost always end
57//! > up pretty similar to [C11's definition of volatile][c11].
58//! >
59//! > The compiler shouldn't change the relative order or number of volatile
60//! > memory operations. However, volatile memory operations on zero-sized types
61//! > [...] are noops and may be ignored.
62//!
63//! [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
64#![no_std]
65#![allow(unused_unsafe)] // stable alternative to #![deny(unsafe_op_in_unsafe_fn)]
66#![warn(missing_debug_implementations)]
67#![warn(missing_docs)]
68#![warn(unused_extern_crates)]
69#![warn(clippy::todo)]
70#![warn(clippy::unimplemented)]
71#![warn(clippy::unwrap_used)]
72#![deny(safe_packed_borrows)]
73
74mod volatile;
75pub use volatile::{Volatile, VolatileReadOnly, VolatileWriteOnly};
76
77/// A marker trait for volatile types.
78///
79/// This trait must be implemented in order to implement [`VolatileRead`] and
80/// [`VolatileWrite`], which will read or write data of type `T`.
81pub trait VolatileData<T> {}
82
83/// Volatile data which can be read.
84///
85/// The data to be read is of type `T`.
86pub trait VolatileRead<T>
87where
88 Self: VolatileData<T>,
89 T: Copy,
90{
91 /// Performs a volatile read of the value in `self` without moving it. This
92 /// leaves the memory in `self` unchanged.
93 ///
94 /// # Safety
95 /// Just like in C, whether an operation is volatile has no bearing
96 /// whatsoever on questions involving concurrent access from multiple
97 /// threads. Volatile accesses behave exactly like non-atomic accesses in
98 /// that regard. In particular, a race between a read operation any write
99 /// operation to the same location is undefined behavior.
100 fn read(&self) -> T;
101}
102
103/// Volatile data which can be written.
104///
105/// The data to be written is of type `T`.
106pub trait VolatileWrite<T>
107where
108 Self: VolatileData<T>,
109 T: Copy,
110{
111 /// Performs a volatile write of `self` with the given value without reading
112 /// the old value.
113 ///
114 /// # Safety
115 /// Just like in C, whether an operation is volatile has no bearing
116 /// whatsoever on questions involving concurrent access from multiple
117 /// threads. Volatile accesses behave exactly like non-atomic accesses in
118 /// that regard. In particular, a race between a write operation any other
119 /// operation (reading or writing) to the same location is undefined
120 /// behavior.
121 fn write(&mut self, val: T);
122}
123
124/// Data which is, or can be treated as, a readable slice of volatile elements.
125///
126/// The data to be read is of type [`[U]`](slice).
127///
128/// This trait has a blanket implementation for all types which meet the
129/// criteria.
130pub trait VolatileReadSlice<T, U>
131where
132 Self: AsRef<[T]>,
133 T: VolatileRead<U>,
134 U: Copy,
135{
136 /// Performs a volatile read of each element of `self` copying the data to
137 /// `dst`. This leaves the memory in `self` unchanged.
138 ///
139 /// The length of `dst` must be the same as `self`.
140 ///
141 /// # Panics
142 ///
143 /// This function will panic if the two slices have different lengths.
144 ///
145 /// # Safety
146 /// Just like in C, whether an operation is volatile has no bearing
147 /// whatsoever on questions involving concurrent access from multiple
148 /// threads. Volatile accesses behave exactly like non-atomic accesses in
149 /// that regard. In particular, a race between a read operation any write
150 /// operation to the same location is undefined behavior.
151 fn read_slice_volatile(&self, dst: &mut [U]) {
152 let this = self.as_ref();
153 assert!(
154 this.len() == dst.len(),
155 "source slice length ({}) does not match destination slice length ({})",
156 this.len(),
157 dst.len()
158 );
159
160 for i in 0..this.len() {
161 dst[i] = this[i].read();
162 }
163 }
164}
165
166impl<S, T, U> VolatileReadSlice<T, U> for S
167where
168 S: AsRef<[T]>,
169 T: VolatileRead<U>,
170 U: Copy,
171{
172}
173
174/// Data which is, or can be treated as, a writable slice of volatile elements.
175///
176/// The data to be written is of type [`[U]`](slice).
177///
178/// This trait has a blanket implementation for all types which meet the
179/// criteria.
180pub trait VolatileWriteSlice<T, U>
181where
182 Self: AsMut<[T]>,
183 T: VolatileWrite<U>,
184 U: Copy,
185{
186 /// Performs a volatile write of each element of the slice with the given
187 /// value without reading the old data from `self`.
188 ///
189 /// # Safety
190 /// Just like in C, whether an operation is volatile has no bearing
191 /// whatsoever on questions involving concurrent access from multiple
192 /// threads. Volatile accesses behave exactly like non-atomic accesses in
193 /// that regard. In particular, a race between a write operation any other
194 /// operation (reading or writing) to the same location is undefined
195 /// behavior.
196 fn fill_volatile(&mut self, val: U) {
197 let this = self.as_mut();
198 for elem in this.iter_mut() {
199 elem.write(val);
200 }
201 }
202
203 /// Performs a volatile write of each element of `self`, copying the data
204 /// from `src`, without reading the old data from `self`.
205 ///
206 /// The length of `src` must be the same as `self`.
207 ///
208 /// # Panics
209 ///
210 /// This function will panic if the two slices have different lengths.
211 ///
212 /// # Safety
213 /// Just like in C, whether an operation is volatile has no bearing
214 /// whatsoever on questions involving concurrent access from multiple
215 /// threads. Volatile accesses behave exactly like non-atomic accesses in
216 /// that regard. In particular, a race between a write operation any other
217 /// operation (reading or writing) to the same location is undefined
218 /// behavior.
219 fn write_slice_volatile(&mut self, src: &[U]) {
220 let this = self.as_mut();
221 assert!(
222 this.len() == src.len(),
223 "source slice length ({}) does not match destination slice length ({})",
224 src.len(),
225 this.len()
226 );
227
228 for i in 0..this.len() {
229 this[i].write(src[i]);
230 }
231 }
232}
233
234impl<S, T, U> VolatileWriteSlice<T, U> for S
235where
236 S: AsMut<[T]>,
237 T: VolatileWrite<U>,
238 U: Copy,
239{
240}