From e5b2118b1a801d21f998d1ff03b0333b1cb0ec22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 5 Jul 2025 10:42:44 +0200 Subject: [PATCH 1/2] gio: add methods for returning and propagating boolean and int in/from a `Task` When using Rust we prefer to use a `Value` for everything but gio also lets you return explicitly a `gboolean` or `gssize` which avoid boxing. Usage of these functions to return these kinds of values requires a call to the corresponding propagate function and you cannot mix them. Trying to extract a `Value` when either of them were used leads to an incorrect value. Expose methods in `Task` and `LocalTask` to return and propagate these kinds instead of assuming everything can go through a `Value`. --- gio/src/task.rs | 103 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 5 deletions(-) diff --git a/gio/src/task.rs b/gio/src/task.rs index 90d95fc39c41..40522070a58a 100644 --- a/gio/src/task.rs +++ b/gio/src/task.rs @@ -238,8 +238,6 @@ macro_rules! task_impl { } #[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,9 +270,26 @@ macro_rules! task_impl { } } + #[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()) }, + } + } + + #[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()) }, + } + } + + #[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 { @@ -313,6 +328,38 @@ macro_rules! task_impl { } } } + + #[doc(alias = "g_task_propagate_boolean")] + #[allow(unused_unsafe)] + pub $($safety)? 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)) + } + } + } + + #[doc(alias = "g_task_propagate_int")] + #[allow(unused_unsafe)] + pub $($safety)? 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 +494,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 +516,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::*; From 48f9f4d095d004176af812cc91f9ead2d566d6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 5 Jul 2025 19:21:15 +0200 Subject: [PATCH 2/2] gio: document the interaction of the `Task` result setter and getters The three ways you can return a result from the task must be match in how you set and get the fields. Document this in the different functions and mark them as unsafe unconditionally to indicate that there is no way within the type system to ensure that you are using it safely. --- examples/gio_task/file_size/mod.rs | 7 +++-- gio/src/task.rs | 49 ++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) 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 40522070a58a..c177461f8c20 100644 --- a/gio/src/task.rs +++ b/gio/src/task.rs @@ -237,6 +237,13 @@ 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_pointer")] #[doc(alias = "g_task_return_error")] @@ -270,6 +277,13 @@ 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) { @@ -279,6 +293,13 @@ macro_rules! task_impl { } } + // 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) { @@ -289,10 +310,18 @@ macro_rules! task_impl { } + // 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_pointer")] #[allow(unused_unsafe)] - pub $($safety)? fn propagate(self) -> Result { + pub unsafe fn propagate(self) -> Result { let mut error = ptr::null_mut(); unsafe { @@ -329,9 +358,16 @@ 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 $($safety)? fn propagate_boolean(self) -> Result { + pub unsafe fn propagate_boolean(self) -> Result { let mut error = ptr::null_mut(); unsafe { @@ -345,9 +381,16 @@ macro_rules! task_impl { } } + // 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 $($safety)? fn propagate_int(self) -> Result { + pub unsafe fn propagate_int(self) -> Result { let mut error = ptr::null_mut(); unsafe {