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