diff --git a/docs/reference/gio/gio-sections-common.txt b/docs/reference/gio/gio-sections-common.txt index 718ea08ce..b91377d0f 100644 --- a/docs/reference/gio/gio-sections-common.txt +++ b/docs/reference/gio/gio-sections-common.txt @@ -4459,6 +4459,7 @@ g_task_get_name g_task_return_boolean g_task_return_int g_task_return_pointer +g_task_return_value g_task_return_error g_task_return_new_error g_task_return_error_if_cancelled @@ -4466,6 +4467,7 @@ g_task_return_error_if_cancelled g_task_propagate_boolean g_task_propagate_int g_task_propagate_pointer +g_task_propagate_value g_task_had_error g_task_get_completed diff --git a/gio/gtask.c b/gio/gtask.c index 06f63e7b1..4acb09df8 100644 --- a/gio/gtask.c +++ b/gio/gtask.c @@ -27,6 +27,8 @@ #include "glibintl.h" +#include + /** * SECTION:gtask * @short_description: Cancellable synchronous or asynchronous task @@ -1959,6 +1961,100 @@ g_task_had_error (GTask *task) return FALSE; } +static void +value_free (gpointer value) +{ + g_value_unset (value); + g_free (value); +} + +/** + * g_task_return_value: + * @task: a #GTask + * @result: (nullable) (transfer none): the #GValue result of + * a task function + * + * Sets @task's result to @result (by copying it) and completes the task. + * + * If @result is %NULL then a #GValue of type #G_TYPE_POINTER + * with a value of %NULL will be used for the result. + * + * This is a very generic low-level method intended primarily for use + * by language bindings; for C code, g_task_return_pointer() and the + * like will normally be much easier to use. + * + * Since: 2.64 + */ +void +g_task_return_value (GTask *task, + GValue *result) +{ + GValue *value; + + g_return_if_fail (G_IS_TASK (task)); + g_return_if_fail (!task->ever_returned); + + value = g_new0 (GValue, 1); + + if (result == NULL) + { + g_value_init (value, G_TYPE_POINTER); + g_value_set_pointer (value, NULL); + } + else + { + g_value_init (value, G_VALUE_TYPE (result)); + g_value_copy (result, value); + } + + g_task_return_pointer (task, value, value_free); +} + +/** + * g_task_propagate_value: + * @task: a #GTask + * @value: (out) (caller-allocates): return location for the #GValue + * @error: return location for a #GError + * + * Gets the result of @task as a #GValue, and transfers ownership of + * that value to the caller. As with g_task_return_value(), this is + * a generic low-level method; g_task_propagate_pointer() and the like + * will usually be more useful for C code. + * + * If the task resulted in an error, or was cancelled, then this will + * instead set @error and return %FALSE. + * + * Since this method transfers ownership of the return value (or + * error) to the caller, you may only call it once. + * + * Returns: %TRUE if @task succeeded, %FALSE on error. + * + * Since: 2.64 + */ +gboolean +g_task_propagate_value (GTask *task, + GValue *value, + GError **error) +{ + g_return_val_if_fail (G_IS_TASK (task), FALSE); + g_return_val_if_fail (value != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + if (g_task_propagate_error (task, error)) + return FALSE; + + g_return_val_if_fail (task->result_set, FALSE); + g_return_val_if_fail (task->result_destroy == value_free, FALSE); + + memcpy (value, task->result.pointer, sizeof (GValue)); + g_free (task->result.pointer); + + task->result_destroy = NULL; + task->result_set = FALSE; + + return TRUE; +} + /** * g_task_get_completed: * @task: a #GTask. diff --git a/gio/gtask.h b/gio/gtask.h index 4fc1c859e..73a31e157 100644 --- a/gio/gtask.h +++ b/gio/gtask.h @@ -142,6 +142,9 @@ void g_task_return_new_error (GTask *task, gint code, const char *format, ...) G_GNUC_PRINTF (4, 5); +GLIB_AVAILABLE_IN_2_64 +void g_task_return_value (GTask *task, + GValue *result); GLIB_AVAILABLE_IN_2_36 gboolean g_task_return_error_if_cancelled (GTask *task); @@ -155,6 +158,10 @@ gboolean g_task_propagate_boolean (GTask *task, GLIB_AVAILABLE_IN_2_36 gssize g_task_propagate_int (GTask *task, GError **error); +GLIB_AVAILABLE_IN_2_64 +gboolean g_task_propagate_value (GTask *task, + GValue *value, + GError **error); GLIB_AVAILABLE_IN_2_36 gboolean g_task_had_error (GTask *task); GLIB_AVAILABLE_IN_2_44 diff --git a/gio/tests/task.c b/gio/tests/task.c index 0caed4403..cca05ced1 100644 --- a/gio/tests/task.c +++ b/gio/tests/task.c @@ -2009,6 +2009,47 @@ test_return_pointer (void) g_assert_null (task); } +static void +test_return_value (void) +{ + GObject *object; + GValue value = G_VALUE_INIT; + GValue ret = G_VALUE_INIT; + GTask *task; + GError *error = NULL; + + object = (GObject *)g_dummy_object_new (); + g_assert_cmpint (object->ref_count, ==, 1); + g_object_add_weak_pointer (object, (gpointer *)&object); + + g_value_init (&value, G_TYPE_OBJECT); + g_value_set_object (&value, object); + g_assert_cmpint (object->ref_count, ==, 2); + + task = g_task_new (NULL, NULL, NULL, NULL); + g_object_add_weak_pointer (G_OBJECT (task), (gpointer *)&task); + g_task_return_value (task, &value); + g_assert_cmpint (object->ref_count, ==, 3); + + g_assert_true (g_task_propagate_value (task, &ret, &error)); + g_assert_no_error (error); + g_assert_true (g_value_get_object (&ret) == object); + g_assert_cmpint (object->ref_count, ==, 3); + + g_object_unref (task); + g_assert_nonnull (task); + wait_for_completed_notification (task); + g_assert_null (task); + + g_assert_cmpint (object->ref_count, ==, 3); + g_value_unset (&ret); + g_assert_cmpint (object->ref_count, ==, 2); + g_value_unset (&value); + g_assert_cmpint (object->ref_count, ==, 1); + g_object_unref (object); + g_assert_null (object); +} + /* test_object_keepalive: GTask takes a ref on its source object */ static GObject *keepalive_object; @@ -2348,6 +2389,7 @@ main (int argc, char **argv) g_test_add_func ("/gtask/return-on-cancel-sync", test_return_on_cancel_sync); g_test_add_func ("/gtask/return-on-cancel-atomic", test_return_on_cancel_atomic); g_test_add_func ("/gtask/return-pointer", test_return_pointer); + g_test_add_func ("/gtask/return-value", test_return_value); g_test_add_func ("/gtask/object-keepalive", test_object_keepalive); g_test_add_func ("/gtask/legacy-error", test_legacy_error); g_test_add_func ("/gtask/return/in-idle/error-first", test_return_in_idle_error_first);