1 // Take a look at the license at the top of the repository in the LICENSE file.
2 
3 use crate::AsyncResult;
4 use crate::Cancellable;
5 use crate::Task;
6 use glib::object::IsA;
7 use glib::translate::*;
8 use libc::c_void;
9 use std::boxed::Box as Box_;
10 use std::ptr;
11 
12 impl Task {
13     #[doc(alias = "g_task_new")]
new<P: IsA<Cancellable>, Q: FnOnce(&AsyncResult, Option<&glib::Object>) + 'static>( source_object: Option<&glib::Object>, cancellable: Option<&P>, callback: Q, ) -> Task14     pub fn new<P: IsA<Cancellable>, Q: FnOnce(&AsyncResult, Option<&glib::Object>) + 'static>(
15         source_object: Option<&glib::Object>,
16         cancellable: Option<&P>,
17         callback: Q,
18     ) -> Task {
19         let callback_data = Box_::new(callback);
20         unsafe extern "C" fn trampoline<
21             Q: FnOnce(&AsyncResult, Option<&glib::Object>) + 'static,
22         >(
23             source_object: *mut glib::gobject_ffi::GObject,
24             res: *mut ffi::GAsyncResult,
25             user_data: glib::ffi::gpointer,
26         ) {
27             let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
28             let res = AsyncResult::from_glib_borrow(res);
29             let callback: Box_<Q> = Box::from_raw(user_data as *mut _);
30             callback(&res, source_object.as_ref().as_ref());
31         }
32         let callback = trampoline::<Q>;
33         unsafe {
34             from_glib_full(ffi::g_task_new(
35                 source_object.to_glib_none().0,
36                 cancellable.map(|p| p.as_ref()).to_glib_none().0,
37                 Some(callback),
38                 Box_::into_raw(callback_data) as *mut _,
39             ))
40         }
41     }
42 
43     #[doc(alias = "g_task_return_error")]
return_error(&self, error: glib::Error)44     pub fn return_error(&self, error: glib::Error) {
45         unsafe {
46             ffi::g_task_return_error(self.to_glib_none().0, error.to_glib_full() as *mut _);
47         }
48     }
49 
50     #[doc(alias = "get_priority")]
51     #[doc(alias = "g_task_get_priority")]
priority(&self) -> glib::source::Priority52     pub fn priority(&self) -> glib::source::Priority {
53         unsafe { FromGlib::from_glib(ffi::g_task_get_priority(self.to_glib_none().0)) }
54     }
55 
56     #[doc(alias = "g_task_set_priority")]
set_priority(&self, priority: glib::source::Priority)57     pub fn set_priority(&self, priority: glib::source::Priority) {
58         unsafe {
59             ffi::g_task_set_priority(self.to_glib_none().0, priority.into_glib());
60         }
61     }
62 
63     #[doc(alias = "g_task_run_in_thread")]
run_in_thread<Q>(&self, task_func: Q) where Q: FnOnce(&Self, Option<&glib::Object>, Option<&Cancellable>), Q: Send + 'static,64     pub fn run_in_thread<Q>(&self, task_func: Q)
65     where
66         Q: FnOnce(&Self, Option<&glib::Object>, Option<&Cancellable>),
67         Q: Send + 'static,
68     {
69         let task_func_data = Box_::new(task_func);
70 
71         // We store the func pointer into the task data.
72         // We intentionally do not expose a way to set the task data in the bindings.
73         // If we detect that the task data is set, there is not much we can do, so we panic.
74         unsafe {
75             if !ffi::g_task_get_task_data(self.to_glib_none().0).is_null() {
76                 panic!("Task data was manually set or the task was run thread multiple times");
77             }
78 
79             ffi::g_task_set_task_data(
80                 self.to_glib_none().0,
81                 Box_::into_raw(task_func_data) as *mut _,
82                 None,
83             );
84         }
85 
86         unsafe extern "C" fn trampoline<Q>(
87             task: *mut ffi::GTask,
88             source_object: *mut glib::gobject_ffi::GObject,
89             user_data: glib::ffi::gpointer,
90             cancellable: *mut ffi::GCancellable,
91         ) where
92             Q: FnOnce(&Task, Option<&glib::Object>, Option<&Cancellable>),
93             Q: Send + 'static,
94         {
95             let task = Task::from_glib_borrow(task);
96             let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
97             let cancellable = Option::<Cancellable>::from_glib_borrow(cancellable);
98             let task_func: Box_<Q> = Box::from_raw(user_data as *mut _);
99             task_func(
100                 task.as_ref(),
101                 source_object.as_ref().as_ref(),
102                 cancellable.as_ref().as_ref(),
103             );
104         }
105 
106         let task_func = trampoline::<Q>;
107         unsafe {
108             ffi::g_task_run_in_thread(self.to_glib_none().0, Some(task_func));
109         }
110     }
111 
return_value(&self, result: &glib::Value)112     pub fn return_value(&self, result: &glib::Value) {
113         unsafe extern "C" fn value_free(value: *mut c_void) {
114             glib::gobject_ffi::g_value_unset(value as *mut glib::gobject_ffi::GValue);
115             glib::ffi::g_free(value);
116         }
117         unsafe {
118             let value: *mut glib::gobject_ffi::GValue =
119                 <&glib::Value>::to_glib_full_from_slice(&[result]);
120             ffi::g_task_return_pointer(
121                 self.to_glib_none().0,
122                 value as *mut c_void,
123                 Some(value_free),
124             )
125         }
126     }
127 
propagate_value(&self) -> Result<glib::Value, glib::Error>128     pub fn propagate_value(&self) -> Result<glib::Value, glib::Error> {
129         unsafe {
130             let mut error = ptr::null_mut();
131             let value = ffi::g_task_propagate_pointer(self.to_glib_none().0, &mut error);
132             if !error.is_null() {
133                 return Err(from_glib_full(error));
134             }
135             let value = from_glib_full(value as *mut glib::gobject_ffi::GValue);
136             match value {
137                 Some(value) => Ok(value),
138                 None => Ok(glib::Value::from_type(glib::types::Type::UNIT)),
139             }
140         }
141     }
142 }
143 
144 #[cfg(test)]
145 mod test {
146     use super::*;
147     use crate::prelude::*;
148     use crate::test_util::run_async_local;
149 
150     #[test]
test_int_async_result()151     fn test_int_async_result() {
152         match run_async_local(|tx, l| {
153             let c = crate::Cancellable::new();
154             let t = crate::Task::new(
155                 None,
156                 Some(&c),
157                 move |a: &AsyncResult, _b: Option<&glib::Object>| {
158                     let t = a.downcast_ref::<crate::Task>().unwrap();
159                     tx.send(t.propagate_value()).unwrap();
160                     l.quit();
161                 },
162             );
163             t.return_value(&100_i32.to_value());
164         }) {
165             Err(_) => panic!(),
166             Ok(i) => {
167                 assert_eq!(i.get::<i32>().unwrap(), 100);
168             }
169         }
170     }
171 
172     #[test]
test_object_async_result()173     fn test_object_async_result() {
174         use glib::subclass::prelude::*;
175         pub struct MySimpleObjectPrivate {
176             pub size: std::cell::RefCell<Option<i64>>,
177         }
178 
179         #[glib::object_subclass]
180         impl ObjectSubclass for MySimpleObjectPrivate {
181             const NAME: &'static str = "MySimpleObjectPrivate";
182             type ParentType = glib::Object;
183             type Type = MySimpleObject;
184 
185             fn new() -> Self {
186                 Self {
187                     size: std::cell::RefCell::new(Some(100)),
188                 }
189             }
190         }
191 
192         impl ObjectImpl for MySimpleObjectPrivate {}
193 
194         glib::wrapper! {
195             pub struct MySimpleObject(ObjectSubclass<MySimpleObjectPrivate>);
196         }
197 
198         impl MySimpleObject {
199             pub fn new() -> Self {
200                 glib::Object::new(&[]).expect("Failed to create MySimpleObject")
201             }
202 
203             #[doc(alias = "get_size")]
204             pub fn size(&self) -> Option<i64> {
205                 let imp = MySimpleObjectPrivate::from_instance(self);
206                 *imp.size.borrow()
207             }
208 
209             pub fn set_size(&self, size: i64) {
210                 let imp = MySimpleObjectPrivate::from_instance(self);
211                 imp.size.borrow_mut().replace(size);
212             }
213         }
214 
215         impl Default for MySimpleObject {
216             fn default() -> Self {
217                 Self::new()
218             }
219         }
220 
221         match run_async_local(|tx, l| {
222             let c = crate::Cancellable::new();
223             let t = crate::Task::new(
224                 None,
225                 Some(&c),
226                 move |a: &AsyncResult, _b: Option<&glib::Object>| {
227                     let t = a.downcast_ref::<crate::Task>().unwrap();
228                     tx.send(t.propagate_value()).unwrap();
229                     l.quit();
230                 },
231             );
232             let my_object = MySimpleObject::new();
233             my_object.set_size(100);
234             t.return_value(&my_object.upcast::<glib::Object>().to_value());
235         }) {
236             Err(_) => panic!(),
237             Ok(o) => {
238                 let o = o
239                     .get::<glib::Object>()
240                     .unwrap()
241                     .downcast::<MySimpleObject>()
242                     .unwrap();
243 
244                 assert_eq!(o.size(), Some(100));
245             }
246         }
247     }
248 
249     #[test]
test_error()250     fn test_error() {
251         match run_async_local(|tx, l| {
252             let c = crate::Cancellable::new();
253             let t = crate::Task::new(
254                 None,
255                 Some(&c),
256                 move |a: &AsyncResult, _b: Option<&glib::Object>| {
257                     let t = a.downcast_ref::<crate::Task>().unwrap();
258                     tx.send(t.propagate_value()).unwrap();
259                     l.quit();
260                 },
261             );
262             t.return_error(glib::Error::new(
263                 crate::IOErrorEnum::WouldBlock,
264                 "WouldBlock",
265             ));
266         }) {
267             Err(e) => match e.kind().unwrap() {
268                 crate::IOErrorEnum::WouldBlock => {}
269                 _ => panic!(),
270             },
271             Ok(_) => panic!(),
272         }
273     }
274 
275     #[test]
test_cancelled()276     fn test_cancelled() {
277         match run_async_local(|tx, l| {
278             let c = crate::Cancellable::new();
279             let t = crate::Task::new(
280                 None,
281                 Some(&c),
282                 move |a: &AsyncResult, _b: Option<&glib::Object>| {
283                     let t = a.downcast_ref::<crate::Task>().unwrap();
284                     tx.send(t.propagate_value()).unwrap();
285                     l.quit();
286                 },
287             );
288             c.cancel();
289             t.return_error_if_cancelled();
290         }) {
291             Err(e) => match e.kind().unwrap() {
292                 crate::IOErrorEnum::Cancelled => {}
293                 _ => panic!(),
294             },
295             Ok(_) => panic!(),
296         }
297     }
298 }
299