write_only/reference/
volatile.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use core::marker::PhantomData;
6
7use crate::Write;
8
9/// A write-only **reference** with **non-dropping volatile** write access.
10pub struct VolatileWriteOnlyRef<'a, T: 'a> {
11    data: *mut T,
12    _phantom: PhantomData<&'a T>,
13}
14
15impl<'a, T: 'a> VolatileWriteOnlyRef<'a, T> {
16    /// Forms a write-only reference from a pointer.
17    ///
18    /// # Safety
19    ///
20    /// Behavior is undefined if any of the following conditions are violated:
21    ///
22    /// * `data` must be [valid](http://doc.rust-lang.org/core/ptr/index.html#safety) for reads for `len * mem::size_of::<T>()` many bytes,
23    ///   and it must be properly aligned. This means in particular:
24    ///
25    ///     * `data` must be non-null and aligned. One reason for this is that enum
26    ///       layout optimizations may rely on references being aligned and non-null
27    ///       to distinguish them from other data.
28    ///
29    /// * `data` must point to a properly initialized guard of type `T`.
30    ///
31    /// * The memory referenced by the returned reference must not be mutated for the duration
32    ///   of lifetime `'a`, except inside an `UnsafeCell`.
33    ///
34    /// # Caveat
35    ///
36    /// The lifetime for the returned reference is inferred from its usage. To
37    /// prevent accidental misuse, it's suggested to tie the lifetime to whichever
38    /// source lifetime is safe in the context, such as by providing a helper
39    /// function taking the lifetime of a host guard for the reference, or by explicit
40    /// annotation.
41    #[inline]
42    pub unsafe fn from_ptr(data: *mut T) -> Self {
43        Self {
44            data,
45            _phantom: PhantomData,
46        }
47    }
48}
49
50impl<'a, T: 'a> Write<T> for VolatileWriteOnlyRef<'a, T> {
51    #[inline]
52    fn write(&mut self, guard: T) {
53        unsafe {
54            self.data.write_volatile(guard);
55        }
56    }
57}
58
59impl<'a, T: 'a> From<&'a mut T> for VolatileWriteOnlyRef<'a, T> {
60    #[inline]
61    fn from(borrow: &'a mut T) -> Self {
62        unsafe { Self::from_ptr(borrow as *mut T) }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    use droptest::prelude::*;
71
72    #[test]
73    fn from_ptr() {
74        let registry = DropRegistry::default();
75        let (id, mut guard) = registry.new_guard_for(1).by_id();
76
77        let reference = unsafe { VolatileWriteOnlyRef::from_ptr(&mut guard) };
78
79        std::mem::drop(reference);
80
81        assert_no_drop!(registry, id);
82    }
83
84    #[test]
85    fn from() {
86        let registry = DropRegistry::default();
87        let (id, mut guard) = registry.new_guard_for(1).by_id();
88
89        let reference = VolatileWriteOnlyRef::from(&mut guard);
90
91        std::mem::drop(reference);
92
93        assert_no_drop!(registry, id);
94    }
95
96    #[test]
97    fn write() {
98        let registry = DropRegistry::default();
99        let (old_id, mut guard) = registry.new_guard_for(1).by_id();
100        let (new_id, new_guard) = registry.new_guard_for(2).by_id();
101
102        let mut reference = VolatileWriteOnlyRef::from(&mut guard);
103
104        reference.write(new_guard);
105
106        assert_eq!(guard.value(), &2);
107
108        assert_no_drop!(registry, old_id);
109        assert_no_drop!(registry, new_id);
110
111        std::mem::drop(guard);
112
113        assert_no_drop!(registry, old_id);
114        assert_drop!(registry, new_id);
115    }
116}