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