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 { fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error>26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { 27 f.pad("Deferred { .. }") 28 } 29 } 30 31 impl Deferred { 32 /// Constructs a new `Deferred` from a `FnOnce()`. new<F: FnOnce()>(f: F) -> Self33 pub(crate) fn new<F: FnOnce()>(f: F) -> Self { 34 let size = mem::size_of::<F>(); 35 let align = mem::align_of::<F>(); 36 37 unsafe { 38 if size <= mem::size_of::<Data>() && align <= mem::align_of::<Data>() { 39 let mut data = MaybeUninit::<Data>::uninit(); 40 ptr::write(data.as_mut_ptr() as *mut F, f); 41 42 unsafe fn call<F: FnOnce()>(raw: *mut u8) { 43 let f: F = ptr::read(raw as *mut F); 44 f(); 45 } 46 47 Deferred { 48 call: call::<F>, 49 data, 50 _marker: PhantomData, 51 } 52 } else { 53 let b: Box<F> = Box::new(f); 54 let mut data = MaybeUninit::<Data>::uninit(); 55 ptr::write(data.as_mut_ptr() as *mut Box<F>, b); 56 57 unsafe fn call<F: FnOnce()>(raw: *mut u8) { 58 // It's safe to cast `raw` from `*mut u8` to `*mut Box<F>`, because `raw` is 59 // originally derived from `*mut Box<F>`. 60 let b: Box<F> = ptr::read(raw as *mut Box<F>); 61 (*b)(); 62 } 63 64 Deferred { 65 call: call::<F>, 66 data, 67 _marker: PhantomData, 68 } 69 } 70 } 71 } 72 73 /// Calls the function. 74 #[inline] call(mut self)75 pub(crate) fn call(mut self) { 76 let call = self.call; 77 unsafe { call(self.data.as_mut_ptr() as *mut u8) }; 78 } 79 } 80 81 #[cfg(all(test, not(crossbeam_loom)))] 82 mod tests { 83 #![allow(clippy::drop_copy)] 84 85 use super::Deferred; 86 use std::cell::Cell; 87 88 #[test] on_stack()89 fn on_stack() { 90 let fired = &Cell::new(false); 91 let a = [0usize; 1]; 92 93 let d = Deferred::new(move || { 94 drop(a); 95 fired.set(true); 96 }); 97 98 assert!(!fired.get()); 99 d.call(); 100 assert!(fired.get()); 101 } 102 103 #[test] on_heap()104 fn on_heap() { 105 let fired = &Cell::new(false); 106 let a = [0usize; 10]; 107 108 let d = Deferred::new(move || { 109 drop(a); 110 fired.set(true); 111 }); 112 113 assert!(!fired.get()); 114 d.call(); 115 assert!(fired.get()); 116 } 117 118 #[test] string()119 fn string() { 120 let a = "hello".to_string(); 121 let d = Deferred::new(move || assert_eq!(a, "hello")); 122 d.call(); 123 } 124 125 #[test] boxed_slice_i32()126 fn boxed_slice_i32() { 127 let a: Box<[i32]> = vec![2, 3, 5, 7].into_boxed_slice(); 128 let d = Deferred::new(move || assert_eq!(*a, [2, 3, 5, 7])); 129 d.call(); 130 } 131 132 #[test] long_slice_usize()133 fn long_slice_usize() { 134 let a: [usize; 5] = [2, 3, 5, 7, 11]; 135 let d = Deferred::new(move || assert_eq!(a, [2, 3, 5, 7, 11])); 136 d.call(); 137 } 138 } 139