write_only/reference/
non_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::{Put, Write};
8
9/// A write-only **reference** with **dropping non-volatile** write access.
10pub struct WriteOnlyRef<'a, T: 'a> {
11    data: *mut T,
12    _phantom: PhantomData<&'a T>,
13}
14
15impl<'a, T: 'a> WriteOnlyRef<'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    /// * The memory referenced by the returned reference must not be mutated for the duration
30    ///   of lifetime `'a`, except inside an `UnsafeCell`.
31    ///
32    /// # Caveat
33    ///
34    /// The lifetime for the returned reference is inferred from its usage. To
35    /// prevent accidental misuse, it's suggested to tie the lifetime to whichever
36    /// source lifetime is safe in the context, such as by providing a helper
37    /// function taking the lifetime of a host guard for the reference, or by explicit
38    /// annotation.
39    #[inline]
40    pub unsafe fn from_ptr(data: *mut T) -> Self {
41        Self {
42            data,
43            _phantom: PhantomData,
44        }
45    }
46}
47
48impl<'a, T: 'a> Put<T> for WriteOnlyRef<'a, T> {
49    #[inline]
50    fn put(&mut self, guard: T) {
51        unsafe {
52            *self.data = guard;
53        }
54    }
55}
56
57impl<'a, T: 'a> Write<T> for WriteOnlyRef<'a, T> {
58    #[inline]
59    fn write(&mut self, guard: T) {
60        unsafe {
61            self.data.write(guard);
62        }
63    }
64}
65
66impl<'a, T: 'a> From<&'a mut T> for WriteOnlyRef<'a, T> {
67    #[inline]
68    fn from(borrow: &'a mut T) -> Self {
69        unsafe { Self::from_ptr(borrow as *mut T) }
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    use droptest::prelude::*;
78
79    #[test]
80    fn from_ptr() {
81        let registry = DropRegistry::default();
82        let (id, mut guard) = registry.new_guard_for(1).by_id();
83
84        let reference = unsafe { WriteOnlyRef::from_ptr(&mut guard) };
85
86        std::mem::drop(reference);
87
88        assert_no_drop!(registry, id);
89    }
90
91    #[test]
92    fn from() {
93        let registry = DropRegistry::default();
94        let (id, mut guard) = registry.new_guard_for(1).by_id();
95
96        let reference = WriteOnlyRef::from(&mut guard);
97
98        std::mem::drop(reference);
99
100        assert_no_drop!(registry, id);
101    }
102
103    #[test]
104    fn put() {
105        let registry = DropRegistry::default();
106        let (old_id, mut guard) = registry.new_guard_for(1).by_id();
107        let (new_id, new_guard) = registry.new_guard_for(2).by_id();
108
109        let mut reference = WriteOnlyRef::from(&mut guard);
110        reference.put(new_guard);
111
112        assert_eq!(guard.value(), &2);
113
114        assert_drop!(registry, old_id);
115        assert_no_drop!(registry, new_id);
116
117        std::mem::drop(guard);
118
119        assert_drop!(registry, old_id);
120        assert_drop!(registry, new_id);
121    }
122
123    #[test]
124    fn write() {
125        let registry = DropRegistry::default();
126        let (old_id, mut guard) = registry.new_guard_for(1).by_id();
127        let (new_id, new_guard) = registry.new_guard_for(2).by_id();
128
129        let mut reference = WriteOnlyRef::from(&mut guard);
130        reference.write(new_guard);
131
132        assert_eq!(guard.value(), &2);
133
134        assert_no_drop!(registry, old_id);
135        assert_no_drop!(registry, new_id);
136
137        std::mem::drop(guard);
138
139        assert_no_drop!(registry, old_id);
140        assert_drop!(registry, new_id);
141    }
142}