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}