diff --git a/examples/gio_task/file_size/mod.rs b/examples/gio_task/file_size/mod.rs index ff7432d1c912..c99f7ae3d4e2 100644 --- a/examples/gio_task/file_size/mod.rs +++ b/examples/gio_task/file_size/mod.rs @@ -26,7 +26,9 @@ impl FileSize { callback: Q, ) { let closure = move |task: gio::LocalTask, source_object: Option<&glib::Object>| { - let value = task.propagate().unwrap(); + // SAFETY: this is safe because we call propagate just once and the + // task sets the result as a value + let value = unsafe { task.propagate() }.unwrap(); let source_object = source_object.unwrap().downcast_ref::().unwrap(); callback(value, source_object); }; @@ -68,7 +70,8 @@ impl FileSize { callback: Q, ) { let closure = move |task: gio::Task, source_object: Option<&FileSize>| { - // SAFETY: this is safe because we call propagate just once + // SAFETY: this is safe because we call propagate just once and the + // task sets the result as a value let value = unsafe { task.propagate().unwrap() }; let source_object = source_object.unwrap().downcast_ref::().unwrap(); callback(value, source_object); diff --git a/gio/src/task.rs b/gio/src/task.rs index 90d95fc39c41..c177461f8c20 100644 --- a/gio/src/task.rs +++ b/gio/src/task.rs @@ -237,9 +237,14 @@ macro_rules! task_impl { unsafe { from_glib(ffi::g_task_return_error_if_cancelled(self.to_glib_none().0)) } } + // rustdoc-stripper-ignore-next + /// Set the result of the task + /// + /// # Safety + /// + /// The value must be read with [`Task::propagate`], + /// `g_task_propagate_value` or `g_task_propagate_pointer`. #[doc(alias = "g_task_return_value")] - #[doc(alias = "g_task_return_boolean")] - #[doc(alias = "g_task_return_int")] #[doc(alias = "g_task_return_pointer")] #[doc(alias = "g_task_return_error")] #[allow(unused_unsafe)] @@ -272,12 +277,51 @@ macro_rules! task_impl { } } + // rustdoc-stripper-ignore-next + /// Set the result of the task as a boolean + /// + /// # Safety + /// + /// The value must be read with [`Task::propagate_boolean`], + /// or `g_task_propagate_boolean`. + #[doc(alias = "g_task_return_boolean")] + #[allow(unused_unsafe)] + pub $($safety)? fn return_boolean_result(self, result: Result) { + match result { + Ok(v) => unsafe { ffi::g_task_return_boolean(self.to_glib_none().0, v as i32) }, + Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) }, + } + } + + // rustdoc-stripper-ignore-next + /// Set the result of the task as an int + /// + /// # Safety + /// + /// The value must be read with [`Task::propagate_int`], + /// or `g_task_propagate_int`. + #[doc(alias = "g_task_return_int")] + #[allow(unused_unsafe)] + pub $($safety)? fn return_int_result(self, result: Result) { + match result { + Ok(v) => unsafe { ffi::g_task_return_int(self.to_glib_none().0, v) }, + Err(e) => unsafe { ffi::g_task_return_error(self.to_glib_none().0, e.into_glib_ptr()) }, + } + } + + + // rustdoc-stripper-ignore-next + /// Gets the result of the task and transfers ownership of it + /// + /// # Safety + /// + /// This must only be called once, and only if the result was set + /// via [`Task::return_result`], `g_task_return_value` or + /// `g_task_return_pointer`. #[doc(alias = "g_task_propagate_value")] - #[doc(alias = "g_task_propagate_boolean")] - #[doc(alias = "g_task_propagate_int")] #[doc(alias = "g_task_propagate_pointer")] #[allow(unused_unsafe)] - pub $($safety)? fn propagate(self) -> Result { + pub unsafe fn propagate(self) -> Result { let mut error = ptr::null_mut(); unsafe { @@ -313,6 +357,52 @@ macro_rules! task_impl { } } } + + // rustdoc-stripper-ignore-next + /// Gets the result of the task as a boolean, or the error + /// + /// # Safety + /// + /// This must only be called once, and only if the result was set + /// via [`Task::return_boolean_result`], or `g_task_return_boolean`. + #[doc(alias = "g_task_propagate_boolean")] + #[allow(unused_unsafe)] + pub unsafe fn propagate_boolean(self) -> Result { + let mut error = ptr::null_mut(); + + unsafe { + let res = ffi::g_task_propagate_boolean(self.to_glib_none().0, &mut error); + + if error.is_null() { + Ok(res != 0) + } else { + Err(from_glib_full(error)) + } + } + } + + // rustdoc-stripper-ignore-next + /// Gets the result of the task as an int, or the error + /// + /// # Safety + /// + /// This must only be called once, and only if the result was set + /// via [`Task::return_int_result`], or `g_task_return_int`. + #[doc(alias = "g_task_propagate_int")] + #[allow(unused_unsafe)] + pub unsafe fn propagate_int(self) -> Result { + let mut error = ptr::null_mut(); + + unsafe { + let res = ffi::g_task_propagate_int(self.to_glib_none().0, &mut error); + + if error.is_null() { + Ok(res) + } else { + Err(from_glib_full(error)) + } + } + } } } } @@ -447,7 +537,7 @@ mod test { use crate::{prelude::*, test_util::run_async_local}; #[test] - fn test_int_async_result() { + fn test_int_value_async_result() { let fut = run_async_local(|tx, l| { let cancellable = crate::Cancellable::new(); let task = unsafe { @@ -469,6 +559,52 @@ mod test { } } + #[test] + fn test_boolean_async_result() { + let fut = run_async_local(|tx, l| { + let cancellable = crate::Cancellable::new(); + let task = unsafe { + crate::LocalTask::new( + None, + Some(&cancellable), + move |t: LocalTask, _b: Option<&glib::Object>| { + tx.send(t.propagate_boolean()).unwrap(); + l.quit(); + }, + ) + }; + task.return_boolean_result(Ok(true)); + }); + + match fut { + Err(_) => panic!(), + Ok(i) => assert!(i), + } + } + + #[test] + fn test_int_async_result() { + let fut = run_async_local(|tx, l| { + let cancellable = crate::Cancellable::new(); + let task = unsafe { + crate::LocalTask::new( + None, + Some(&cancellable), + move |t: LocalTask, _b: Option<&glib::Object>| { + tx.send(t.propagate_int()).unwrap(); + l.quit(); + }, + ) + }; + task.return_int_result(Ok(100_isize)); + }); + + match fut { + Err(_) => panic!(), + Ok(i) => assert_eq!(i, 100), + } + } + #[test] fn test_object_async_result() { use glib::subclass::prelude::*;