Coverage Report

Created: 2025-02-25 06:39

/rust/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-epoch-0.9.18/src/deferred.rs
Line
Count
Source (jump to first uncovered line)
1
use alloc::boxed::Box;
2
use core::fmt;
3
use core::marker::PhantomData;
4
use core::mem::{self, MaybeUninit};
5
use core::ptr;
6
7
/// Number of words a piece of `Data` can hold.
8
///
9
/// Three words should be enough for the majority of cases. For example, you can fit inside it the
10
/// function pointer together with a fat pointer representing an object that needs to be destroyed.
11
const DATA_WORDS: usize = 3;
12
13
/// Some space to keep a `FnOnce()` object on the stack.
14
type Data = [usize; DATA_WORDS];
15
16
/// A `FnOnce()` that is stored inline if small, or otherwise boxed on the heap.
17
///
18
/// This is a handy way of keeping an unsized `FnOnce()` within a sized structure.
19
pub(crate) struct Deferred {
20
    call: unsafe fn(*mut u8),
21
    data: MaybeUninit<Data>,
22
    _marker: PhantomData<*mut ()>, // !Send + !Sync
23
}
24
25
impl fmt::Debug for Deferred {
26
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
27
0
        f.pad("Deferred { .. }")
28
0
    }
29
}
30
31
impl Deferred {
32
    pub(crate) const NO_OP: Self = {
33
0
        fn no_op_call(_raw: *mut u8) {}
34
        Self {
35
            call: no_op_call,
36
            data: MaybeUninit::uninit(),
37
            _marker: PhantomData,
38
        }
39
    };
40
41
    /// Constructs a new `Deferred` from a `FnOnce()`.
42
0
    pub(crate) fn new<F: FnOnce()>(f: F) -> Self {
43
0
        let size = mem::size_of::<F>();
44
0
        let align = mem::align_of::<F>();
45
0
46
0
        unsafe {
47
0
            if size <= mem::size_of::<Data>() && align <= mem::align_of::<Data>() {
48
0
                let mut data = MaybeUninit::<Data>::uninit();
49
0
                ptr::write(data.as_mut_ptr().cast::<F>(), f);
50
51
0
                unsafe fn call<F: FnOnce()>(raw: *mut u8) {
52
0
                    let f: F = ptr::read(raw.cast::<F>());
53
0
                    f();
54
0
                }
Unexecuted instantiation: <crossbeam_epoch::deferred::Deferred>::new::call::<<crossbeam_epoch::guard::Guard>::defer_unchecked<<crossbeam_deque::deque::Worker<rayon_core::job::JobRef>>::resize::{closure#0}, ()>::{closure#0}>
Unexecuted instantiation: <crossbeam_epoch::deferred::Deferred>::new::call::<<crossbeam_epoch::guard::Guard>::defer_unchecked<<crossbeam_epoch::guard::Guard>::defer_destroy<crossbeam_epoch::sync::queue::Node<crossbeam_epoch::internal::SealedBag>>::{closure#0}, crossbeam_epoch::atomic::Owned<crossbeam_epoch::sync::queue::Node<crossbeam_epoch::internal::SealedBag>>>::{closure#0}>
Unexecuted instantiation: <crossbeam_epoch::deferred::Deferred>::new::call::<<crossbeam_epoch::guard::Guard>::defer_unchecked<<crossbeam_epoch::guard::Guard>::defer_destroy<crossbeam_epoch::internal::Local>::{closure#0}, crossbeam_epoch::atomic::Owned<crossbeam_epoch::internal::Local>>::{closure#0}>
55
56
0
                Deferred {
57
0
                    call: call::<F>,
58
0
                    data,
59
0
                    _marker: PhantomData,
60
0
                }
61
            } else {
62
0
                let b: Box<F> = Box::new(f);
63
0
                let mut data = MaybeUninit::<Data>::uninit();
64
0
                ptr::write(data.as_mut_ptr().cast::<Box<F>>(), b);
65
66
0
                unsafe fn call<F: FnOnce()>(raw: *mut u8) {
67
0
                    // It's safe to cast `raw` from `*mut u8` to `*mut Box<F>`, because `raw` is
68
0
                    // originally derived from `*mut Box<F>`.
69
0
                    let b: Box<F> = ptr::read(raw.cast::<Box<F>>());
70
0
                    (*b)();
71
0
                }
Unexecuted instantiation: <crossbeam_epoch::deferred::Deferred>::new::call::<<crossbeam_epoch::guard::Guard>::defer_unchecked<<crossbeam_deque::deque::Worker<rayon_core::job::JobRef>>::resize::{closure#0}, ()>::{closure#0}>
Unexecuted instantiation: <crossbeam_epoch::deferred::Deferred>::new::call::<<crossbeam_epoch::guard::Guard>::defer_unchecked<<crossbeam_epoch::guard::Guard>::defer_destroy<crossbeam_epoch::sync::queue::Node<crossbeam_epoch::internal::SealedBag>>::{closure#0}, crossbeam_epoch::atomic::Owned<crossbeam_epoch::sync::queue::Node<crossbeam_epoch::internal::SealedBag>>>::{closure#0}>
Unexecuted instantiation: <crossbeam_epoch::deferred::Deferred>::new::call::<<crossbeam_epoch::guard::Guard>::defer_unchecked<<crossbeam_epoch::guard::Guard>::defer_destroy<crossbeam_epoch::internal::Local>::{closure#0}, crossbeam_epoch::atomic::Owned<crossbeam_epoch::internal::Local>>::{closure#0}>
72
73
0
                Deferred {
74
0
                    call: call::<F>,
75
0
                    data,
76
0
                    _marker: PhantomData,
77
0
                }
78
            }
79
        }
80
0
    }
Unexecuted instantiation: <crossbeam_epoch::deferred::Deferred>::new::<<crossbeam_epoch::guard::Guard>::defer_unchecked<<crossbeam_deque::deque::Worker<rayon_core::job::JobRef>>::resize::{closure#0}, ()>::{closure#0}>
Unexecuted instantiation: <crossbeam_epoch::deferred::Deferred>::new::<<crossbeam_epoch::guard::Guard>::defer_unchecked<<crossbeam_epoch::guard::Guard>::defer_destroy<crossbeam_epoch::sync::queue::Node<crossbeam_epoch::internal::SealedBag>>::{closure#0}, crossbeam_epoch::atomic::Owned<crossbeam_epoch::sync::queue::Node<crossbeam_epoch::internal::SealedBag>>>::{closure#0}>
Unexecuted instantiation: <crossbeam_epoch::deferred::Deferred>::new::<<crossbeam_epoch::guard::Guard>::defer_unchecked<<crossbeam_epoch::guard::Guard>::defer_destroy<crossbeam_epoch::internal::Local>::{closure#0}, crossbeam_epoch::atomic::Owned<crossbeam_epoch::internal::Local>>::{closure#0}>
81
82
    /// Calls the function.
83
    #[inline]
84
0
    pub(crate) fn call(mut self) {
85
0
        let call = self.call;
86
0
        unsafe { call(self.data.as_mut_ptr().cast::<u8>()) };
87
0
    }
88
}
89
90
#[cfg(all(test, not(crossbeam_loom)))]
91
mod tests {
92
    use super::Deferred;
93
    use std::cell::Cell;
94
    use std::convert::identity;
95
96
    #[test]
97
    fn on_stack() {
98
        let fired = &Cell::new(false);
99
        let a = [0usize; 1];
100
101
        let d = Deferred::new(move || {
102
            let _ = identity(a);
103
            fired.set(true);
104
        });
105
106
        assert!(!fired.get());
107
        d.call();
108
        assert!(fired.get());
109
    }
110
111
    #[test]
112
    fn on_heap() {
113
        let fired = &Cell::new(false);
114
        let a = [0usize; 10];
115
116
        let d = Deferred::new(move || {
117
            let _ = identity(a);
118
            fired.set(true);
119
        });
120
121
        assert!(!fired.get());
122
        d.call();
123
        assert!(fired.get());
124
    }
125
126
    #[test]
127
    fn string() {
128
        let a = "hello".to_string();
129
        let d = Deferred::new(move || assert_eq!(a, "hello"));
130
        d.call();
131
    }
132
133
    #[test]
134
    fn boxed_slice_i32() {
135
        let a: Box<[i32]> = vec![2, 3, 5, 7].into_boxed_slice();
136
        let d = Deferred::new(move || assert_eq!(*a, [2, 3, 5, 7]));
137
        d.call();
138
    }
139
140
    #[test]
141
    fn long_slice_usize() {
142
        let a: [usize; 5] = [2, 3, 5, 7, 11];
143
        let d = Deferred::new(move || assert_eq!(a, [2, 3, 5, 7, 11]));
144
        d.call();
145
    }
146
}