volatile/volatile_ptr/
operations.rs

1use core::{
2    marker::PhantomData,
3    ptr::{self, NonNull},
4};
5
6use crate::{
7    access::{Access, ReadOnly, ReadWrite, Readable, RestrictAccess, Writable, WriteOnly},
8    VolatilePtr,
9};
10
11/// Constructor functions.
12///
13/// These functions construct new `VolatilePtr` values. While the `new`
14/// function creates a `VolatilePtr` instance with unrestricted access, there
15/// are also functions for creating read-only or write-only instances.
16impl<'a, T> VolatilePtr<'a, T>
17where
18    T: ?Sized,
19{
20    /// Turns the given pointer into a `VolatilePtr`.
21    ///
22    /// ## Safety
23    ///
24    /// - The given pointer must be valid.
25    /// - No other thread must have access to the given pointer. This must remain true
26    ///   for the whole lifetime of the `VolatilePtr`.
27    pub unsafe fn new(pointer: NonNull<T>) -> VolatilePtr<'a, T, ReadWrite> {
28        unsafe { VolatilePtr::new_restricted(ReadWrite, pointer) }
29    }
30
31    /// Creates a new read-only volatile pointer from the given raw pointer.
32    ///
33    /// ## Safety
34    ///
35    /// The requirements for [`Self::new`] apply to this function too.
36    pub const unsafe fn new_read_only(pointer: NonNull<T>) -> VolatilePtr<'a, T, ReadOnly> {
37        unsafe { Self::new_restricted(ReadOnly, pointer) }
38    }
39
40    /// Creates a new volatile pointer with restricted access from the given raw pointer.
41    ///
42    /// ## Safety
43    ///
44    /// The requirements for [`Self::new`] apply to this function too.
45    pub const unsafe fn new_restricted<A>(access: A, pointer: NonNull<T>) -> VolatilePtr<'a, T, A>
46    where
47        A: Access,
48    {
49        let _ = access;
50        unsafe { Self::new_generic(pointer) }
51    }
52
53    pub(super) const unsafe fn new_generic<A>(pointer: NonNull<T>) -> VolatilePtr<'a, T, A> {
54        VolatilePtr {
55            pointer,
56            reference: PhantomData,
57            access: PhantomData,
58        }
59    }
60}
61
62impl<'a, T, A> VolatilePtr<'a, T, A>
63where
64    T: ?Sized,
65{
66    /// Performs a volatile read of the contained value.
67    ///
68    /// Returns a copy of the read value. Volatile reads are guaranteed not to be optimized
69    /// away by the compiler, but by themselves do not have atomic ordering
70    /// guarantees. To also get atomicity, consider looking at the `Atomic` wrapper types of
71    /// the standard/`core` library.
72    ///
73    /// ## Examples
74    ///
75    /// ```rust
76    /// use volatile::{VolatilePtr, access};
77    /// use core::ptr::NonNull;
78    ///
79    /// let value = 42;
80    /// let pointer = unsafe {
81    ///     VolatilePtr::new_restricted(access::ReadOnly, NonNull::from(&value))
82    /// };
83    /// assert_eq!(pointer.read(), 42);
84    /// ```
85    #[must_use]
86    pub fn read(self) -> T
87    where
88        T: Copy,
89        A: Readable,
90    {
91        unsafe { ptr::read_volatile(self.pointer.as_ptr()) }
92    }
93
94    /// Performs a volatile write, setting the contained value to the given `value`.
95    ///
96    /// Volatile writes are guaranteed to not be optimized away by the compiler, but by
97    /// themselves do not have atomic ordering guarantees. To also get atomicity, consider
98    /// looking at the `Atomic` wrapper types of the standard/`core` library.
99    ///
100    /// ## Example
101    ///
102    /// ```rust
103    /// use volatile::VolatilePtr;
104    ///
105    /// let mut value = 42;
106    /// let volatile = unsafe { VolatilePtr::new((&mut value).into()) };
107    /// volatile.write(50);
108    ///
109    /// assert_eq!(volatile.read(), 50);
110    /// ```
111    pub fn write(self, value: T)
112    where
113        T: Copy,
114        A: Writable,
115    {
116        unsafe { ptr::write_volatile(self.pointer.as_ptr(), value) };
117    }
118
119    /// Updates the contained value using the given closure and volatile instructions.
120    ///
121    /// Performs a volatile read of the contained value, passes it to the
122    /// function `f`, and then performs a volatile write of the returned value back to
123    /// the target.
124    ///
125    /// ```rust
126    /// use volatile::VolatilePtr;
127    ///
128    /// let mut value = 42;
129    /// let volatile = unsafe { VolatilePtr::new((&mut value).into()) };
130    /// volatile.update(|val| val + 1);
131    ///
132    /// assert_eq!(volatile.read(), 43);
133    /// ```
134    pub fn update<F>(self, f: F)
135    where
136        T: Copy,
137        A: Readable + Writable,
138        F: FnOnce(T) -> T,
139    {
140        let new = f(self.read());
141        self.write(new);
142    }
143
144    /// Extracts the wrapped raw pointer.
145    ///
146    /// ## Example
147    ///
148    /// ```
149    /// use volatile::VolatilePtr;
150    ///
151    /// let mut value = 42;
152    /// let volatile = unsafe { VolatilePtr::new((&mut value).into()) };
153    /// volatile.write(50);
154    /// let unwrapped: *mut i32 = volatile.as_raw_ptr().as_ptr();
155    ///
156    /// assert_eq!(unsafe { *unwrapped }, 50); // non volatile access, be careful!
157    /// ```
158    #[must_use]
159    pub fn as_raw_ptr(self) -> NonNull<T> {
160        self.pointer
161    }
162
163    /// Constructs a new `VolatilePtr` by mapping the wrapped pointer.
164    ///
165    /// This method is useful for accessing only a part of a volatile value, e.g. a subslice or
166    /// a struct field. For struct field access, there is also the safe
167    /// [`map_field`][crate::map_field] macro that wraps this function.
168    ///
169    /// ## Examples
170    ///
171    /// Accessing a struct field:
172    ///
173    /// ```
174    /// use volatile::VolatilePtr;
175    /// use core::ptr::NonNull;
176    ///
177    /// struct Example { field_1: u32, field_2: u8, }
178    /// let mut value = Example { field_1: 15, field_2: 255 };
179    /// let volatile = unsafe { VolatilePtr::new((&mut value).into()) };
180    ///
181    /// // construct a volatile pointer to a field
182    /// let field_2 = unsafe { volatile.map(|ptr| NonNull::new(core::ptr::addr_of_mut!((*ptr.as_ptr()).field_2)).unwrap()) };
183    /// assert_eq!(field_2.read(), 255);
184    /// ```
185    ///
186    /// Don't misuse this method to do a non-volatile read of the referenced value:
187    ///
188    /// ```
189    /// use volatile::VolatilePtr;
190    ///
191    /// let mut value = 5;
192    /// let volatile = unsafe { VolatilePtr::new((&mut value).into()) };
193    ///
194    /// // DON'T DO THIS:
195    /// let mut readout = 0;
196    /// unsafe {
197    ///     let _ = volatile.map(|value| {
198    ///         readout = *value.as_ptr(); // non-volatile read, might lead to bugs
199    ///         value
200    ///     });
201    /// };
202    /// ```
203    ///
204    /// ## Safety
205    ///
206    /// The pointer returned by `f` must satisfy the requirements of [`Self::new`].
207    pub unsafe fn map<F, U>(self, f: F) -> VolatilePtr<'a, U, A>
208    where
209        F: FnOnce(NonNull<T>) -> NonNull<U>,
210        A: Access,
211        U: ?Sized,
212    {
213        unsafe { VolatilePtr::new_restricted(A::default(), f(self.pointer)) }
214    }
215}
216
217/// Methods for restricting access.
218impl<'a, T, A> VolatilePtr<'a, T, A>
219where
220    T: ?Sized,
221{
222    /// Restricts access permissions to `A`.
223    ///
224    /// ## Example
225    ///
226    /// ```
227    /// use volatile::access::{ReadOnly, WriteOnly};
228    /// use volatile::VolatilePtr;
229    ///
230    /// let mut value: i16 = -4;
231    /// let volatile = unsafe { VolatilePtr::new((&mut value).into()) };
232    ///
233    /// let read_only = volatile.restrict::<ReadOnly>();
234    /// assert_eq!(read_only.read(), -4);
235    /// // read_only.write(10); // compile-time error
236    ///
237    /// let no_access = read_only.restrict::<WriteOnly>();
238    /// // no_access.read(); // compile-time error
239    /// // no_access.write(10); // compile-time error
240    /// ```
241    pub fn restrict<To>(self) -> VolatilePtr<'a, T, A::Restricted>
242    where
243        A: RestrictAccess<To>,
244    {
245        unsafe { VolatilePtr::new_restricted(Default::default(), self.pointer) }
246    }
247}
248
249/// Methods for restricting access.
250impl<'a, T> VolatilePtr<'a, T, ReadWrite>
251where
252    T: ?Sized,
253{
254    /// Restricts access permissions to read-only.
255    ///
256    /// ## Example
257    ///
258    /// ```
259    /// use volatile::VolatilePtr;
260    ///
261    /// let mut value: i16 = -4;
262    /// let volatile = unsafe { VolatilePtr::new((&mut value).into()) };
263    ///
264    /// let read_only = volatile.read_only();
265    /// assert_eq!(read_only.read(), -4);
266    /// // read_only.write(10); // compile-time error
267    /// ```
268    pub fn read_only(self) -> VolatilePtr<'a, T, ReadOnly> {
269        self.restrict()
270    }
271
272    /// Restricts access permissions to write-only.
273    ///
274    /// ## Example
275    ///
276    /// Creating a write-only pointer to a struct field:
277    ///
278    /// ```
279    /// use volatile::{VolatilePtr, map_field};
280    ///
281    /// struct Example { field_1: u32, field_2: u8, }
282    /// let mut value = Example { field_1: 15, field_2: 255 };
283    /// let volatile = unsafe { VolatilePtr::new((&mut value).into()) };
284    ///
285    /// // construct a volatile write-only pointer to `field_2`
286    /// let field_2 = map_field!(volatile.field_2).write_only();
287    /// field_2.write(14);
288    /// // field_2.read(); // compile-time error
289    /// ```
290    pub fn write_only(self) -> VolatilePtr<'a, T, WriteOnly> {
291        self.restrict()
292    }
293}