1 use traits::FnBox; 2 3 /// `SendBoxFnOnce` boxes any `FnOnce + Send` function up to a certain 4 /// number of arguments (10 as of now). 5 /// 6 /// As `Box<FnOnce()>` doesn't work yet, and `Box<FnBox()>` will not be 7 /// available in stable rust, `SendBoxFnOnce` tries to provide a safe 8 /// implementation. 9 /// 10 /// Instead of `Box<FnOnce(Args...) -> Result + 'a>` (or 11 /// `Box<FnBox(Args...) -> Result + 'a>`) the box type is 12 /// `SendBoxFnOnce<'a, (Args...,), Result>` (the arguments are always given 13 /// as tuple type). If the function doesn't return a value (i.e. the 14 /// empty tuple) `Result` can be omitted: `SendBoxFnOnce<'a, (Args...,)>`. 15 /// 16 /// Internally it is implemented similar to `Box<FnBox()>`, but there is 17 /// no `FnOnce` implementation for `SendBoxFnOnce`. 18 /// 19 /// You can build boxes for diverging functions too, but specifying the 20 /// type (like `SendBoxFnOnce<(), !>`) is not possible as the `!` type 21 /// is experimental. 22 /// 23 /// # Examples 24 /// 25 /// Move value into closure to return it, box the closure and send it: 26 /// 27 /// ``` 28 /// use boxfnonce::SendBoxFnOnce; 29 /// use std::thread; 30 /// 31 /// let s = String::from("foo"); 32 /// let f : SendBoxFnOnce<(), String> = SendBoxFnOnce::from(|| { 33 /// println!("Got called: {}", s); 34 /// s 35 /// }); 36 /// let result = thread::Builder::new().spawn(move || { 37 /// f.call() 38 /// }).unwrap().join().unwrap(); 39 /// assert_eq!(result, "foo".to_string()); 40 /// ``` 41 pub struct SendBoxFnOnce<'a, Arguments, Result = ()> (pub(crate) Box<FnBox<Arguments, Result> + Send + 'a>); 42 43 impl<'a, Args, Result> SendBoxFnOnce<'a, Args, Result> { 44 /// call inner function, consumes the box. 45 /// 46 /// `call_tuple` can be used if the arguments are available as 47 /// tuple. Each usable instance of SendBoxFnOnce<(...), Result> has 48 /// a separate `call` method for passing arguments "untupled". 49 #[inline] call_tuple(self, args: Args) -> Result50 pub fn call_tuple(self, args: Args) -> Result { 51 self.0.call(args) 52 } 53 54 /// `SendBoxFnOnce::new` is an alias for `SendBoxFnOnce::from`. 55 #[inline] new<F>(func: F) -> Self where Self: From<F>56 pub fn new<F>(func: F) -> Self 57 where Self: From<F> 58 { 59 Self::from(func) 60 } 61 } 62 63 build_n_args!(SendBoxFnOnce[+Send]: ); 64 build_n_args!(SendBoxFnOnce[+Send]: a1: A1); 65 build_n_args!(SendBoxFnOnce[+Send]: a1: A1, a2: A2); 66 build_n_args!(SendBoxFnOnce[+Send]: a1: A1, a2: A2, a3: A3); 67 build_n_args!(SendBoxFnOnce[+Send]: a1: A1, a2: A2, a3: A3, a4: A4); 68 build_n_args!(SendBoxFnOnce[+Send]: a1: A1, a2: A2, a3: A3, a4: A4, a5: A5); 69 build_n_args!(SendBoxFnOnce[+Send]: a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6); 70 build_n_args!(SendBoxFnOnce[+Send]: a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7); 71 build_n_args!(SendBoxFnOnce[+Send]: a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8); 72 build_n_args!(SendBoxFnOnce[+Send]: a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9); 73 build_n_args!(SendBoxFnOnce[+Send]: a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8, a9: A9, a10: A10); 74 75 #[cfg(test)] 76 mod test { 77 use super::super::BoxFnOnce; 78 use super::SendBoxFnOnce; 79 80 use std::thread; 81 use std::sync::Arc; 82 83 struct SendString(String, Arc<()>); 84 impl SendString { into(self) -> String85 fn into(self) -> String { 86 self.0 87 } 88 } 89 closure_string() -> SendString90 fn closure_string() -> SendString { 91 SendString(String::from("abc"), Arc::new(())) 92 } 93 try_send<Result, F>(name: &str, func: F) -> thread::Result<Result> where Result: 'static + Send, F: 'static + Send + FnOnce() -> Result94 fn try_send<Result, F>(name: &str, func: F) -> thread::Result<Result> 95 where 96 Result: 'static + Send, 97 F: 'static + Send + FnOnce() -> Result 98 { 99 thread::Builder::new().name(name.to_string()).spawn(func).unwrap().join() 100 } 101 send<Result, F>(func: F) -> Result where Result: 'static + Send, F: 'static + Send + FnOnce() -> Result102 fn send<Result, F>(func: F) -> Result 103 where 104 Result: 'static + Send, 105 F: 'static + Send + FnOnce() -> Result 106 { 107 try_send("test thread", func).unwrap() 108 } 109 110 #[test] test_arg0()111 fn test_arg0() { 112 let f = SendBoxFnOnce::from({ 113 let s = closure_string(); 114 || -> String { 115 s.into() 116 } 117 }); 118 let result = send(|| { 119 f.call() 120 }); 121 assert_eq!(result, "abc"); 122 } 123 124 #[test] test_arg1()125 fn test_arg1() { 126 let f = SendBoxFnOnce::from({ 127 let s = closure_string(); 128 |_| -> String { 129 s.into() 130 } 131 }); 132 let result = send(|| { 133 f.call(0) 134 }); 135 assert_eq!(result, "abc"); 136 } 137 138 #[test] test_arg1_fixed_argument_type()139 fn test_arg1_fixed_argument_type() { 140 let f : SendBoxFnOnce<(i32,), String> = SendBoxFnOnce::from({ 141 let s = closure_string(); 142 |_| -> String { 143 s.into() 144 } 145 }); 146 let result = send(|| { 147 f.call(0) 148 }); 149 assert_eq!(result, "abc"); 150 } 151 152 #[test] test_arg2()153 fn test_arg2() { 154 let f = SendBoxFnOnce::from({ 155 let s = closure_string(); 156 |_, _| -> String { 157 s.into() 158 } 159 }); 160 let result = send(|| { 161 f.call(0, 0) 162 }); 163 assert_eq!(result, "abc"); 164 } 165 166 #[test] test_arg3()167 fn test_arg3() { 168 let f = SendBoxFnOnce::from({ 169 let s = closure_string(); 170 |_, _, _| -> String { 171 s.into() 172 } 173 }); 174 let result = send(|| { 175 f.call(0, 0, 0) 176 }); 177 assert_eq!(result, "abc"); 178 } 179 180 #[test] test_arg4_void()181 fn test_arg4_void() { 182 let f = SendBoxFnOnce::from({ 183 let s = closure_string(); 184 |_, _, _, _| { 185 drop(s); 186 } 187 }); 188 send(|| { 189 f.call(0, 0, 0, 0); 190 }); 191 } 192 193 #[test] test_arg4_diverging()194 fn test_arg4_diverging() { 195 use std::panic; 196 197 let f = SendBoxFnOnce::from({ 198 let s = closure_string(); 199 |_, _, _, _| -> ! { 200 drop(s); 201 // a panic! but without the default hook printing stuff 202 panic::resume_unwind(Box::new("inner diverging")); 203 } 204 }); 205 let result: Result<(), Box<&str>> = try_send("diverging test thread", || { 206 f.call(0, 0, 0, 0); 207 }).map_err(|e| e.downcast::<&str>().unwrap()); 208 assert_eq!(result, Err(Box::new("inner diverging"))); 209 } 210 211 #[test] test_arg4_void_to_nosend()212 fn test_arg4_void_to_nosend() { 213 let f = SendBoxFnOnce::from({ 214 let s = closure_string(); 215 |_, _, _, _| { 216 drop(s); 217 } 218 }); 219 let f = BoxFnOnce::from(f); 220 f.call(0, 0, 0, 0); 221 } 222 } 223