From d33752b5ceecaaf56ab7d43606a6e06b8a2706f4 Mon Sep 17 00:00:00 2001 From: Zander Brown Date: Tue, 31 Oct 2023 03:49:28 +0000 Subject: [PATCH] gtask: Add g_task_return_prefixed_error() An equivalent to g_propagate_prefixed_error, but for errors sent though GTask, allowing tasks to easily provide extra context to callers. --- gio/gtask.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ gio/gtask.h | 6 ++++++ gio/tests/task.c | 23 +++++++++++++++++++++++ 3 files changed, 77 insertions(+) diff --git a/gio/gtask.c b/gio/gtask.c index 12727007a..37006a916 100644 --- a/gio/gtask.c +++ b/gio/gtask.c @@ -2057,6 +2057,54 @@ g_task_return_error (GTask *task, g_task_return (task, G_TASK_RETURN_ERROR); } +/** + * g_task_return_prefixed_error: + * @task: a #GTask. + * @error: (transfer full): the #GError result of a task function. + * @format: a string with format characters. + * @...: a list of values to insert into @format. + * + * Sets @task's result to @error (which @task assumes ownership of), with + * the message prefixed according to @format, and completes the task + * (see g_task_return_pointer() for more discussion of exactly what this + * means). + * + * Note that since the task takes ownership of @error, and since the + * task may be completed before returning from g_task_return_prefixed_error(), + * you cannot assume that @error is still valid after calling this. + * Call g_error_copy() on the error if you need to keep a local copy + * as well. + * + * See also g_task_return_error(), g_prefix_error(). + * + * Since: 2.80 + */ +void +g_task_return_prefixed_error (GTask *task, + GError *error, + const char *format, + ...) +{ + char *prefix; + va_list ap; + + g_return_if_fail (G_IS_TASK (task)); + g_return_if_fail (!task->ever_returned); + g_return_if_fail (error != NULL); + + task->error = error; + + va_start (ap, format); + prefix = g_strdup_vprintf (format, ap); + va_end (ap); + + g_prefix_error_literal (&task->error, prefix); + + g_free (prefix); + + g_task_return (task, G_TASK_RETURN_ERROR); +} + /** * g_task_return_new_error: * @task: a #GTask. diff --git a/gio/gtask.h b/gio/gtask.h index 6d7fa82ff..3c2c97490 100644 --- a/gio/gtask.h +++ b/gio/gtask.h @@ -163,6 +163,12 @@ void g_task_return_int (GTask *task, GIO_AVAILABLE_IN_2_36 void g_task_return_error (GTask *task, GError *error); +GIO_AVAILABLE_IN_2_80 +void g_task_return_prefixed_error (GTask *task, + GError *error, + const char *format, + ...) G_GNUC_PRINTF (3, 4); + GIO_AVAILABLE_IN_2_36 void g_task_return_new_error (GTask *task, GQuark domain, diff --git a/gio/tests/task.c b/gio/tests/task.c index 8dfc0e983..c164a0631 100644 --- a/gio/tests/task.c +++ b/gio/tests/task.c @@ -2112,6 +2112,28 @@ test_return_value (void) g_assert_null (object); } +static void +test_return_prefixed_error (void) +{ + GTask *task; + GError *original_error = NULL; + GError *error = NULL; + + g_set_error (&original_error, G_IO_ERROR, G_IO_ERROR_UNKNOWN, "oh no!"); + + task = g_task_new (NULL, NULL, NULL, NULL); + g_task_return_prefixed_error (task, original_error, "task %s: ", "failed"); + + wait_for_completed_notification (task); + + g_assert_null (g_task_propagate_pointer (task, &error)); + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN); + g_assert_cmpstr (error->message, ==, "task failed: oh no!"); + + g_assert_finalize_object (task); + g_clear_error (&error); +} + /* test_object_keepalive: GTask takes a ref on its source object */ static GObject *keepalive_object; @@ -2525,6 +2547,7 @@ main (int argc, char **argv) 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/return-prefixed-error", test_return_prefixed_error); 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);