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