crossbeam_epoch/
deferred.rs1use alloc::boxed::Box;
2use core::fmt;
3use core::marker::PhantomData;
4use core::mem::{self, MaybeUninit};
5use core::ptr;
6
7const DATA_WORDS: usize = 3;
12
13type Data = [usize; DATA_WORDS];
15
16pub(crate) struct Deferred {
20 call: unsafe fn(*mut u8),
21 data: MaybeUninit<Data>,
22 _marker: PhantomData<*mut ()>, }
24
25impl fmt::Debug for Deferred {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
27 f.pad("Deferred { .. }")
28 }
29}
30
31impl Deferred {
32 pub(crate) const NO_OP: Self = {
33 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 pub(crate) fn new<F: FnOnce()>(f: F) -> Self {
43 let size = mem::size_of::<F>();
44 let align = mem::align_of::<F>();
45
46 unsafe {
47 if size <= mem::size_of::<Data>() && align <= mem::align_of::<Data>() {
48 let mut data = MaybeUninit::<Data>::uninit();
49 ptr::write(data.as_mut_ptr().cast::<F>(), f);
50
51 unsafe fn call<F: FnOnce()>(raw: *mut u8) {
52 let f: F = ptr::read(raw.cast::<F>());
53 f();
54 }
55
56 Deferred {
57 call: call::<F>,
58 data,
59 _marker: PhantomData,
60 }
61 } else {
62 let b: Box<F> = Box::new(f);
63 let mut data = MaybeUninit::<Data>::uninit();
64 ptr::write(data.as_mut_ptr().cast::<Box<F>>(), b);
65
66 unsafe fn call<F: FnOnce()>(raw: *mut u8) {
67 let b: Box<F> = ptr::read(raw.cast::<Box<F>>());
70 (*b)();
71 }
72
73 Deferred {
74 call: call::<F>,
75 data,
76 _marker: PhantomData,
77 }
78 }
79 }
80 }
81
82 #[inline]
84 pub(crate) fn call(mut self) {
85 let call = self.call;
86 unsafe { call(self.data.as_mut_ptr().cast::<u8>()) };
87 }
88}
89
90#[cfg(all(test, not(crossbeam_loom)))]
91mod tests {
92 use super::Deferred;
93 use std::boxed::Box;
94 use std::cell::Cell;
95 use std::convert::identity;
96 use std::string::ToString;
97 use std::vec;
98
99 #[test]
100 fn on_stack() {
101 let fired = &Cell::new(false);
102 let a = [0usize; 1];
103
104 let d = Deferred::new(move || {
105 let _ = identity(a);
106 fired.set(true);
107 });
108
109 assert!(!fired.get());
110 d.call();
111 assert!(fired.get());
112 }
113
114 #[test]
115 fn on_heap() {
116 let fired = &Cell::new(false);
117 let a = [0usize; 10];
118
119 let d = Deferred::new(move || {
120 let _ = identity(a);
121 fired.set(true);
122 });
123
124 assert!(!fired.get());
125 d.call();
126 assert!(fired.get());
127 }
128
129 #[test]
130 fn string() {
131 let a = "hello".to_string();
132 let d = Deferred::new(move || assert_eq!(a, "hello"));
133 d.call();
134 }
135
136 #[test]
137 fn boxed_slice_i32() {
138 let a: Box<[i32]> = vec![2, 3, 5, 7].into_boxed_slice();
139 let d = Deferred::new(move || assert_eq!(*a, [2, 3, 5, 7]));
140 d.call();
141 }
142
143 #[test]
144 fn long_slice_usize() {
145 let a: [usize; 5] = [2, 3, 5, 7, 11];
146 let d = Deferred::new(move || assert_eq!(a, [2, 3, 5, 7, 11]));
147 d.call();
148 }
149}