1use std::{boxed::Box as Box_, future::Future, mem::transmute, panic, ptr};
4
5use glib::{
6 prelude::*,
7 signal::{SignalHandlerId, connect_raw},
8 translate::*,
9};
10
11use futures_channel::oneshot;
12
13use crate::{AsyncResult, Cancellable, ffi};
14
15glib::wrapper! {
16 #[doc(alias = "GTask")]
28 pub struct LocalTask<V: ValueType>(Object<ffi::GTask, ffi::GTaskClass>) @implements AsyncResult;
29
30 match fn {
31 type_ => || ffi::g_task_get_type(),
32 }
33}
34
35glib::wrapper! {
36 #[doc(alias = "GTask")]
86 pub struct Task<V: ValueType + Send>(Object<ffi::GTask, ffi::GTaskClass>) @implements AsyncResult;
87
88 match fn {
89 type_ => || ffi::g_task_get_type(),
90 }
91}
92
93macro_rules! task_impl {
94 ($name:ident $(, @bound: $bound:tt)? $(, @safety: $safety:tt)?) => {
95 impl <V: Into<glib::Value> + ValueType $(+ $bound)?> $name<V> {
96 #[doc(alias = "g_task_new")]
97 #[allow(unused_unsafe)]
98 pub unsafe fn new<S, P, Q>(
99 source_object: Option<&S>,
100 cancellable: Option<&P>,
101 callback: Q,
102 ) -> Self
103 where
104 S: IsA<glib::Object> $(+ $bound)?,
105 P: IsA<Cancellable>,
106 Q: FnOnce($name<V>, Option<&S>) $(+ $bound)? + 'static,
107 {
108 let callback_data = Box_::new(callback);
109 unsafe extern "C" fn trampoline<
110 S: IsA<glib::Object> $(+ $bound)?,
111 V: ValueType $(+ $bound)?,
112 Q: FnOnce($name<V>, Option<&S>) $(+ $bound)? + 'static,
113 >(
114 source_object: *mut glib::gobject_ffi::GObject,
115 res: *mut ffi::GAsyncResult,
116 user_data: glib::ffi::gpointer,
117 ) { unsafe {
118 let callback: Box_<Q> = Box::from_raw(user_data as *mut _);
119 let task = AsyncResult::from_glib_none(res)
120 .downcast::<$name<V>>()
121 .unwrap();
122 let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
123 callback(
124 task,
125 source_object.as_ref().as_ref().map(|s| s.unsafe_cast_ref()),
126 );
127 }}
128 let callback = trampoline::<S, V, Q>;
129 unsafe {
130 from_glib_full(ffi::g_task_new(
131 source_object.map(|p| p.as_ref()).to_glib_none().0,
132 cancellable.map(|p| p.as_ref()).to_glib_none().0,
133 Some(callback),
134 Box_::into_raw(callback_data) as *mut _,
135 ))
136 }
137 }
138
139 #[doc(alias = "g_task_get_cancellable")]
140 #[doc(alias = "get_cancellable")]
141 pub fn cancellable(&self) -> Option<Cancellable> {
142 unsafe { from_glib_none(ffi::g_task_get_cancellable(self.to_glib_none().0)) }
143 }
144
145 #[doc(alias = "g_task_get_check_cancellable")]
146 #[doc(alias = "get_check_cancellable")]
147 pub fn is_check_cancellable(&self) -> bool {
148 unsafe { from_glib(ffi::g_task_get_check_cancellable(self.to_glib_none().0)) }
149 }
150
151 #[doc(alias = "g_task_set_check_cancellable")]
152 pub fn set_check_cancellable(&self, check_cancellable: bool) {
153 unsafe {
154 ffi::g_task_set_check_cancellable(self.to_glib_none().0, check_cancellable.into_glib());
155 }
156 }
157
158 #[cfg(feature = "v2_60")]
159 #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
160 #[doc(alias = "g_task_set_name")]
161 pub fn set_name(&self, name: Option<&str>) {
162 unsafe {
163 ffi::g_task_set_name(self.to_glib_none().0, name.to_glib_none().0);
164 }
165 }
166
167 #[doc(alias = "g_task_set_return_on_cancel")]
168 pub fn set_return_on_cancel(&self, return_on_cancel: bool) -> bool {
169 unsafe {
170 from_glib(ffi::g_task_set_return_on_cancel(
171 self.to_glib_none().0,
172 return_on_cancel.into_glib(),
173 ))
174 }
175 }
176
177 #[doc(alias = "g_task_is_valid")]
178 pub fn is_valid(
179 result: &impl IsA<AsyncResult>,
180 source_object: Option<&impl IsA<glib::Object>>,
181 ) -> bool {
182 unsafe {
183 from_glib(ffi::g_task_is_valid(
184 result.as_ref().to_glib_none().0,
185 source_object.map(|p| p.as_ref()).to_glib_none().0,
186 ))
187 }
188 }
189
190 #[doc(alias = "get_priority")]
191 #[doc(alias = "g_task_get_priority")]
192 pub fn priority(&self) -> glib::source::Priority {
193 unsafe { FromGlib::from_glib(ffi::g_task_get_priority(self.to_glib_none().0)) }
194 }
195
196 #[doc(alias = "g_task_set_priority")]
197 pub fn set_priority(&self, priority: glib::source::Priority) {
198 unsafe {
199 ffi::g_task_set_priority(self.to_glib_none().0, priority.into_glib());
200 }
201 }
202
203 #[doc(alias = "g_task_get_completed")]
204 #[doc(alias = "get_completed")]
205 pub fn is_completed(&self) -> bool {
206 unsafe { from_glib(ffi::g_task_get_completed(self.to_glib_none().0)) }
207 }
208
209 #[doc(alias = "g_task_get_context")]
210 #[doc(alias = "get_context")]
211 pub fn context(&self) -> glib::MainContext {
212 unsafe { from_glib_none(ffi::g_task_get_context(self.to_glib_none().0)) }
213 }
214
215 #[cfg(feature = "v2_60")]
216 #[cfg_attr(docsrs, doc(cfg(feature = "v2_60")))]
217 #[doc(alias = "g_task_get_name")]
218 #[doc(alias = "get_name")]
219 pub fn name(&self) -> Option<glib::GString> {
220 unsafe { from_glib_none(ffi::g_task_get_name(self.to_glib_none().0)) }
221 }
222
223 #[doc(alias = "g_task_get_return_on_cancel")]
224 #[doc(alias = "get_return_on_cancel")]
225 pub fn is_return_on_cancel(&self) -> bool {
226 unsafe { from_glib(ffi::g_task_get_return_on_cancel(self.to_glib_none().0)) }
227 }
228
229 #[doc(alias = "g_task_had_error")]
230 pub fn had_error(&self) -> bool {
231 unsafe { from_glib(ffi::g_task_had_error(self.to_glib_none().0)) }
232 }
233
234 #[doc(alias = "completed")]
235 pub fn connect_completed_notify<F>(&self, f: F) -> SignalHandlerId
236 where
237 F: Fn(&$name<V>) $(+ $bound)? + 'static,
238 {
239 unsafe extern "C" fn notify_completed_trampoline<V, F>(
240 this: *mut ffi::GTask,
241 _param_spec: glib::ffi::gpointer,
242 f: glib::ffi::gpointer,
243 ) where
244 V: ValueType $(+ $bound)?,
245 F: Fn(&$name<V>) + 'static,
246 { unsafe {
247 let f: &F = &*(f as *const F);
248 f(&from_glib_borrow(this))
249 }}
250 unsafe {
251 let f: Box_<F> = Box_::new(f);
252 connect_raw(
253 self.as_ptr() as *mut _,
254 b"notify::completed\0".as_ptr() as *const _,
255 Some(transmute::<*const (), unsafe extern "C" fn()>(
256 notify_completed_trampoline::<V, F> as *const (),
257 )),
258 Box_::into_raw(f),
259 )
260 }
261 }
262
263 #[doc(alias = "g_task_return_error_if_cancelled")]
267 #[allow(unused_unsafe)]
268 pub $($safety)? fn return_error_if_cancelled(&self) -> bool {
269 unsafe { from_glib(ffi::g_task_return_error_if_cancelled(self.to_glib_none().0)) }
270 }
271
272 #[doc(alias = "g_task_return_value")]
280 #[doc(alias = "g_task_return_pointer")]
281 #[doc(alias = "g_task_return_error")]
282 #[allow(unused_unsafe)]
283 pub $($safety)? fn return_result(self, result: Result<V, glib::Error>) {
284 #[cfg(not(feature = "v2_64"))]
285 unsafe extern "C" fn value_free(value: *mut libc::c_void) { unsafe {
286 let _: glib::Value = from_glib_full(value as *mut glib::gobject_ffi::GValue);
287 }}
288
289 match result {
290 #[cfg(feature = "v2_64")]
291 Ok(v) => unsafe {
292 ffi::g_task_return_value(
293 self.to_glib_none().0,
294 v.to_value().to_glib_none().0 as *mut _,
295 )
296 },
297 #[cfg(not(feature = "v2_64"))]
298 Ok(v) => unsafe {
299 let v: glib::Value = v.into();
300 ffi::g_task_return_pointer(
301 self.to_glib_none().0,
302 <glib::Value as glib::translate::IntoGlibPtr::<*mut glib::gobject_ffi::GValue>>::into_glib_ptr(v) as glib::ffi::gpointer,
303 Some(value_free),
304 )
305 },
306 Err(e) => unsafe {
307 ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr());
308 },
309 }
310 }
311
312 #[doc(alias = "g_task_return_boolean")]
320 #[allow(unused_unsafe)]
321 pub $($safety)? fn return_boolean_result(self, result: Result<bool, glib::Error>) {
322 match result {
323 Ok(v) => unsafe { ffi::g_task_return_boolean(self.to_glib_none().0, v as i32) },
324 Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) },
325 }
326 }
327
328 #[doc(alias = "g_task_return_int")]
336 #[allow(unused_unsafe)]
337 pub $($safety)? fn return_int_result(self, result: Result<isize, glib::Error>) {
338 match result {
339 Ok(v) => unsafe { ffi::g_task_return_int(self.to_glib_none().0, v) },
340 Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) },
341 }
342 }
343
344
345 #[doc(alias = "g_task_propagate_value")]
354 #[doc(alias = "g_task_propagate_pointer")]
355 #[allow(unused_unsafe)]
356 pub unsafe fn propagate(self) -> Result<V, glib::Error> {
357 let mut error = ptr::null_mut();
358
359 unsafe {
360 #[cfg(feature = "v2_64")]
361 {
362 let mut value = glib::Value::uninitialized();
363 ffi::g_task_propagate_value(
364 self.to_glib_none().0,
365 value.to_glib_none_mut().0,
366 &mut error,
367 );
368
369 if error.is_null() {
370 Ok(V::from_value(&value))
371 } else {
372 Err(from_glib_full(error))
373 }
374 }
375
376 #[cfg(not(feature = "v2_64"))]
377 {
378 let value = ffi::g_task_propagate_pointer(self.to_glib_none().0, &mut error);
379
380 if error.is_null() {
381 let value = Option::<glib::Value>::from_glib_full(
382 value as *mut glib::gobject_ffi::GValue,
383 )
384 .expect("Task::propagate() called before Task::return_result()");
385 Ok(V::from_value(&value))
386 } else {
387 Err(from_glib_full(error))
388 }
389 }
390 }
391 }
392
393 #[doc(alias = "g_task_propagate_boolean")]
401 #[allow(unused_unsafe)]
402 pub unsafe fn propagate_boolean(self) -> Result<bool, glib::Error> {
403 let mut error = ptr::null_mut();
404
405 unsafe {
406 let res = ffi::g_task_propagate_boolean(self.to_glib_none().0, &mut error);
407
408 if error.is_null() {
409 Ok(res != 0)
410 } else {
411 Err(from_glib_full(error))
412 }
413 }
414 }
415
416 #[doc(alias = "g_task_propagate_int")]
424 #[allow(unused_unsafe)]
425 pub unsafe fn propagate_int(self) -> Result<isize, glib::Error> {
426 let mut error = ptr::null_mut();
427
428 unsafe {
429 let res = ffi::g_task_propagate_int(self.to_glib_none().0, &mut error);
430
431 if error.is_null() {
432 Ok(res)
433 } else {
434 Err(from_glib_full(error))
435 }
436 }
437 }
438 }
439 }
440}
441
442task_impl!(LocalTask);
443task_impl!(Task, @bound: Send, @safety: unsafe);
444
445impl<V: ValueType + Send> Task<V> {
446 #[doc(alias = "g_task_run_in_thread")]
447 pub fn run_in_thread<S, Q>(&self, task_func: Q)
448 where
449 S: IsA<glib::Object> + Send,
450 Q: FnOnce(Self, Option<&S>, Option<&Cancellable>) + Send + 'static,
451 {
452 let task_func_data = Box_::new(task_func);
453
454 unsafe {
458 assert!(
459 ffi::g_task_get_task_data(self.to_glib_none().0).is_null(),
460 "Task data was manually set or the task was run thread multiple times"
461 );
462
463 ffi::g_task_set_task_data(
464 self.to_glib_none().0,
465 Box_::into_raw(task_func_data) as *mut _,
466 None,
467 );
468 }
469
470 unsafe extern "C" fn trampoline<V, S, Q>(
471 task: *mut ffi::GTask,
472 source_object: *mut glib::gobject_ffi::GObject,
473 user_data: glib::ffi::gpointer,
474 cancellable: *mut ffi::GCancellable,
475 ) where
476 V: ValueType + Send,
477 S: IsA<glib::Object> + Send,
478 Q: FnOnce(Task<V>, Option<&S>, Option<&Cancellable>) + Send + 'static,
479 {
480 unsafe {
481 let task = Task::from_glib_none(task);
482 let source_object = Option::<glib::Object>::from_glib_borrow(source_object);
483 let cancellable = Option::<Cancellable>::from_glib_borrow(cancellable);
484 let task_func: Box_<Q> = Box::from_raw(user_data as *mut _);
485 task_func(
486 task,
487 source_object.as_ref().as_ref().map(|s| s.unsafe_cast_ref()),
488 cancellable.as_ref().as_ref(),
489 );
490 }
491 }
492
493 let task_func = trampoline::<V, S, Q>;
494 unsafe {
495 ffi::g_task_run_in_thread(self.to_glib_none().0, Some(task_func));
496 }
497 }
498}
499
500unsafe impl<V: ValueType + Send> Send for Task<V> {}
501unsafe impl<V: ValueType + Send> Sync for Task<V> {}
502
503#[derive(Debug)]
510pub struct JoinHandle<T> {
511 rx: oneshot::Receiver<std::thread::Result<T>>,
512}
513
514impl<T> JoinHandle<T> {
515 #[inline]
516 fn new() -> (Self, oneshot::Sender<std::thread::Result<T>>) {
517 let (tx, rx) = oneshot::channel();
518 (Self { rx }, tx)
519 }
520}
521
522impl<T> Future for JoinHandle<T> {
523 type Output = std::thread::Result<T>;
524 #[inline]
525 fn poll(
526 mut self: std::pin::Pin<&mut Self>,
527 cx: &mut std::task::Context<'_>,
528 ) -> std::task::Poll<Self::Output> {
529 std::pin::Pin::new(&mut self.rx)
530 .poll(cx)
531 .map(|r| r.unwrap())
532 }
533}
534
535impl<T> futures_core::FusedFuture for JoinHandle<T> {
536 #[inline]
537 fn is_terminated(&self) -> bool {
538 self.rx.is_terminated()
539 }
540}
541
542pub fn spawn_blocking<T, F>(func: F) -> JoinHandle<T>
552where
553 T: Send + 'static,
554 F: FnOnce() -> T + Send + 'static,
555{
556 let task = unsafe { Task::<bool>::new(Cancellable::NONE, Cancellable::NONE, |_, _| {}) };
558 let (join, tx) = JoinHandle::new();
559 task.run_in_thread(move |task, _: Option<&Cancellable>, _| {
560 let res = panic::catch_unwind(panic::AssertUnwindSafe(func));
561 let _ = tx.send(res);
562 unsafe { ffi::g_task_return_pointer(task.to_glib_none().0, ptr::null_mut(), None) }
563 });
564
565 join
566}
567
568#[cfg(test)]
569mod test {
570 use super::*;
571 use crate::{prelude::*, test_util::run_async_local};
572
573 #[test]
574 fn test_int_value_async_result() {
575 let fut = run_async_local(|tx, l| {
576 let cancellable = crate::Cancellable::new();
577 let task = unsafe {
578 crate::LocalTask::new(
579 None,
580 Some(&cancellable),
581 move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
582 tx.send(t.propagate()).unwrap();
583 l.quit();
584 },
585 )
586 };
587 task.return_result(Ok(100_i32));
588 });
589
590 match fut {
591 Err(_) => panic!(),
592 Ok(i) => assert_eq!(i, 100),
593 }
594 }
595
596 #[test]
597 fn test_boolean_async_result() {
598 let fut = run_async_local(|tx, l| {
599 let cancellable = crate::Cancellable::new();
600 let task = unsafe {
601 crate::LocalTask::new(
602 None,
603 Some(&cancellable),
604 move |t: LocalTask<bool>, _b: Option<&glib::Object>| {
605 tx.send(t.propagate_boolean()).unwrap();
606 l.quit();
607 },
608 )
609 };
610 task.return_boolean_result(Ok(true));
611 });
612
613 match fut {
614 Err(_) => panic!(),
615 Ok(i) => assert!(i),
616 }
617 }
618
619 #[test]
620 fn test_int_async_result() {
621 let fut = run_async_local(|tx, l| {
622 let cancellable = crate::Cancellable::new();
623 let task = unsafe {
624 crate::LocalTask::new(
625 None,
626 Some(&cancellable),
627 move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
628 tx.send(t.propagate_int()).unwrap();
629 l.quit();
630 },
631 )
632 };
633 task.return_int_result(Ok(100_isize));
634 });
635
636 match fut {
637 Err(_) => panic!(),
638 Ok(i) => assert_eq!(i, 100),
639 }
640 }
641
642 #[test]
643 fn test_object_async_result() {
644 use glib::subclass::prelude::*;
645 pub struct MySimpleObjectPrivate {
646 pub size: std::cell::RefCell<Option<i64>>,
647 }
648
649 #[glib::object_subclass]
650 impl ObjectSubclass for MySimpleObjectPrivate {
651 const NAME: &'static str = "MySimpleObjectPrivate";
652 type Type = MySimpleObject;
653
654 fn new() -> Self {
655 Self {
656 size: std::cell::RefCell::new(Some(100)),
657 }
658 }
659 }
660
661 impl ObjectImpl for MySimpleObjectPrivate {}
662
663 glib::wrapper! {
664 pub struct MySimpleObject(ObjectSubclass<MySimpleObjectPrivate>);
665 }
666
667 impl MySimpleObject {
668 pub fn new() -> Self {
669 glib::Object::new()
670 }
671
672 #[doc(alias = "get_size")]
673 pub fn size(&self) -> Option<i64> {
674 *self.imp().size.borrow()
675 }
676
677 pub fn set_size(&self, size: i64) {
678 self.imp().size.borrow_mut().replace(size);
679 }
680 }
681
682 impl Default for MySimpleObject {
683 fn default() -> Self {
684 Self::new()
685 }
686 }
687
688 let fut = run_async_local(|tx, l| {
689 let cancellable = crate::Cancellable::new();
690 let task = unsafe {
691 crate::LocalTask::new(
692 None,
693 Some(&cancellable),
694 move |t: LocalTask<glib::Object>, _b: Option<&glib::Object>| {
695 tx.send(t.propagate()).unwrap();
696 l.quit();
697 },
698 )
699 };
700 let my_object = MySimpleObject::new();
701 my_object.set_size(100);
702 task.return_result(Ok(my_object.upcast::<glib::Object>()));
703 });
704
705 match fut {
706 Err(_) => panic!(),
707 Ok(o) => {
708 let o = o.downcast::<MySimpleObject>().unwrap();
709 assert_eq!(o.size(), Some(100));
710 }
711 }
712 }
713
714 #[test]
715 fn test_error() {
716 let fut = run_async_local(|tx, l| {
717 let cancellable = crate::Cancellable::new();
718 let task = unsafe {
719 crate::LocalTask::new(
720 None,
721 Some(&cancellable),
722 move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
723 tx.send(t.propagate()).unwrap();
724 l.quit();
725 },
726 )
727 };
728 task.return_result(Err(glib::Error::new(
729 crate::IOErrorEnum::WouldBlock,
730 "WouldBlock",
731 )));
732 });
733
734 match fut {
735 Err(e) => match e.kind().unwrap() {
736 crate::IOErrorEnum::WouldBlock => {}
737 _ => panic!(),
738 },
739 Ok(_) => panic!(),
740 }
741 }
742
743 #[test]
744 fn test_cancelled() {
745 let fut = run_async_local(|tx, l| {
746 let cancellable = crate::Cancellable::new();
747 let task = unsafe {
748 crate::LocalTask::new(
749 None,
750 Some(&cancellable),
751 move |t: LocalTask<i32>, _b: Option<&glib::Object>| {
752 tx.send(t.propagate()).unwrap();
753 l.quit();
754 },
755 )
756 };
757 cancellable.cancel();
758 task.return_error_if_cancelled();
759 });
760
761 match fut {
762 Err(e) => match e.kind().unwrap() {
763 crate::IOErrorEnum::Cancelled => {}
764 _ => panic!(),
765 },
766 Ok(_) => panic!(),
767 }
768 }
769}