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