gtask: Add a g_task_set_name() method

Similarly to g_source_set_name(), this sets a name on a GTask for
debugging and profiling. Importantly, this name is propagated to the
GSource for idle callbacks for the GTask, ending the glorious reign of
`[gio] complete_in_idle_cb`.

The name can be queried using g_task_get_name(). Locking is avoided by
only allowing the name to be set before the GTask is used from another
thread.

Includes tests.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
This commit is contained in:
Philip Withnall 2018-10-07 17:37:58 +01:00
parent f65adb48e1
commit e89128a935
4 changed files with 109 additions and 1 deletions

View File

@ -4396,6 +4396,7 @@ g_task_set_priority
g_task_set_check_cancellable g_task_set_check_cancellable
g_task_set_return_on_cancel g_task_set_return_on_cancel
g_task_set_source_tag g_task_set_source_tag
g_task_set_name
<SUBSECTION> <SUBSECTION>
g_task_report_error g_task_report_error
g_task_report_new_error g_task_report_new_error
@ -4408,6 +4409,7 @@ g_task_get_return_on_cancel
g_task_get_context g_task_get_context
g_task_get_source_object g_task_get_source_object
g_task_get_source_tag g_task_get_source_tag
g_task_get_name
<SUBSECTION> <SUBSECTION>
g_task_return_boolean g_task_return_boolean
g_task_return_int g_task_return_int

View File

@ -543,6 +543,7 @@ struct _GTask {
gpointer source_object; gpointer source_object;
gpointer source_tag; gpointer source_tag;
gchar *name; /* (owned); may only be modified before the #GTask is threaded */
gpointer task_data; gpointer task_data;
GDestroyNotify task_data_destroy; GDestroyNotify task_data_destroy;
@ -637,6 +638,7 @@ g_task_finalize (GObject *object)
g_clear_object (&task->source_object); g_clear_object (&task->source_object);
g_clear_object (&task->cancellable); g_clear_object (&task->cancellable);
g_free (task->name);
if (task->context) if (task->context)
g_main_context_unref (task->context); g_main_context_unref (task->context);
@ -978,6 +980,36 @@ g_task_set_source_tag (GTask *task,
TRACE (GIO_TASK_SET_SOURCE_TAG (task, source_tag)); TRACE (GIO_TASK_SET_SOURCE_TAG (task, source_tag));
} }
/**
* g_task_set_name:
* @task: a #GTask
* @name: (nullable): a human readable name for the task, or %NULL to unset it
*
* Sets @tasks name, used in debugging and profiling. The name defaults to
* %NULL.
*
* The task name should describe in a human readable way what the task does.
* For example, Open file or Connect to network host. It is used to set the
* name of the #GSource used for idle completion of the task.
*
* This function may only be called before the @task is first used in a thread
* other than the one it was constructed in.
*
* Since: 2.60
*/
void
g_task_set_name (GTask *task,
const gchar *name)
{
gchar *new_name;
g_return_if_fail (G_IS_TASK (task));
new_name = g_strdup (name);
g_free (task->name);
task->name = g_steal_pointer (&new_name);
}
/** /**
* g_task_get_source_object: * g_task_get_source_object:
* @task: a #GTask * @task: a #GTask
@ -1138,6 +1170,22 @@ g_task_get_source_tag (GTask *task)
return task->source_tag; return task->source_tag;
} }
/**
* g_task_get_name:
* @task: a #GTask
*
* Gets @tasks name. See g_task_set_name().
*
* Returns: (nullable) (transfer none): @tasks name, or %NULL
* Since: 2.60
*/
const gchar *
g_task_get_name (GTask *task)
{
g_return_val_if_fail (G_IS_TASK (task), NULL);
return task->name;
}
static void static void
g_task_return_now (GTask *task) g_task_return_now (GTask *task)
@ -1527,6 +1575,9 @@ g_task_run_in_thread_sync (GTask *task,
* #GMainContext with @task's [priority][io-priority], and sets @source's * #GMainContext with @task's [priority][io-priority], and sets @source's
* callback to @callback, with @task as the callback's `user_data`. * callback to @callback, with @task as the callback's `user_data`.
* *
* It will set the @sources name to the tasks name (as set with
* g_task_set_name()), if one has been set.
*
* This takes a reference on @task until @source is destroyed. * This takes a reference on @task until @source is destroyed.
* *
* Since: 2.36 * Since: 2.36
@ -1541,6 +1592,9 @@ g_task_attach_source (GTask *task,
g_source_set_callback (source, callback, g_source_set_callback (source, callback,
g_object_ref (task), g_object_unref); g_object_ref (task), g_object_unref);
g_source_set_priority (source, task->priority); g_source_set_priority (source, task->priority);
if (task->name != NULL)
g_source_set_name (source, task->name);
g_source_attach (source, task->context); g_source_attach (source, task->context);
} }

View File

@ -74,6 +74,9 @@ void g_task_set_check_cancellable (GTask *task,
GLIB_AVAILABLE_IN_2_36 GLIB_AVAILABLE_IN_2_36
void g_task_set_source_tag (GTask *task, void g_task_set_source_tag (GTask *task,
gpointer source_tag); gpointer source_tag);
GLIB_AVAILABLE_IN_2_60
void g_task_set_name (GTask *task,
const gchar *name);
GLIB_AVAILABLE_IN_2_36 GLIB_AVAILABLE_IN_2_36
gpointer g_task_get_source_object (GTask *task); gpointer g_task_get_source_object (GTask *task);
@ -89,6 +92,8 @@ GLIB_AVAILABLE_IN_2_36
gboolean g_task_get_check_cancellable (GTask *task); gboolean g_task_get_check_cancellable (GTask *task);
GLIB_AVAILABLE_IN_2_36 GLIB_AVAILABLE_IN_2_36
gpointer g_task_get_source_tag (GTask *task); gpointer g_task_get_source_tag (GTask *task);
GLIB_AVAILABLE_IN_2_60
const gchar *g_task_get_name (GTask *task);
GLIB_AVAILABLE_IN_2_36 GLIB_AVAILABLE_IN_2_36
gboolean g_task_is_valid (gpointer result, gboolean g_task_is_valid (gpointer result,

View File

@ -614,6 +614,46 @@ test_priority (void)
g_assert_cmpint (ret3, ==, 3); g_assert_cmpint (ret3, ==, 3);
} }
/* Test that getting and setting the task name works. */
static void name_callback (GObject *object,
GAsyncResult *result,
gpointer user_data);
static void
test_name (void)
{
GTask *t1 = NULL;
gchar *name1 = NULL;
t1 = g_task_new (NULL, NULL, name_callback, &name1);
g_task_set_name (t1, "some task");
g_task_return_boolean (t1, TRUE);
g_object_unref (t1);
g_main_loop_run (loop);
g_assert_cmpstr (name1, ==, "some task");
g_free (name1);
}
static void
name_callback (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
gchar **name_out = user_data;
GError *local_error = NULL;
g_assert_null (*name_out);
*name_out = g_strdup (g_task_get_name (G_TASK (result)));
g_task_propagate_boolean (G_TASK (result), &local_error);
g_assert_no_error (local_error);
g_main_loop_quit (loop);
}
/* test_check_cancellable: cancellation overrides return value */ /* test_check_cancellable: cancellation overrides return value */
enum { enum {
@ -793,6 +833,7 @@ run_in_thread_callback (GObject *object,
g_assert (g_async_result_get_user_data (result) == user_data); g_assert (g_async_result_get_user_data (result) == user_data);
g_assert (!g_task_had_error (G_TASK (result))); g_assert (!g_task_had_error (G_TASK (result)));
g_assert_false (g_task_get_completed (G_TASK (result))); g_assert_false (g_task_get_completed (G_TASK (result)));
g_assert_cmpstr (g_task_get_name (G_TASK (result)), ==, "test_run_in_thread name");
ret = g_task_propagate_int (G_TASK (result), &error); ret = g_task_propagate_int (G_TASK (result), &error);
g_assert_no_error (error); g_assert_no_error (error);
@ -816,6 +857,7 @@ run_in_thread_thread (GTask *task,
g_assert (task_data == g_task_get_task_data (task)); g_assert (task_data == g_task_get_task_data (task));
g_assert (cancellable == g_task_get_cancellable (task)); g_assert (cancellable == g_task_get_cancellable (task));
g_assert_false (g_task_get_completed (task)); g_assert_false (g_task_get_completed (task));
g_assert_cmpstr (g_task_get_name (task), ==, "test_run_in_thread name");
g_assert (g_thread_self () != main_thread); g_assert (g_thread_self () != main_thread);
@ -837,13 +879,13 @@ test_run_in_thread (void)
gboolean done = FALSE; gboolean done = FALSE;
task = g_task_new (NULL, NULL, run_in_thread_callback, &done); task = g_task_new (NULL, NULL, run_in_thread_callback, &done);
g_task_set_name (task, "test_run_in_thread name");
g_object_weak_ref (G_OBJECT (task), task_weak_notify, (gpointer)&weak_notify_ran); g_object_weak_ref (G_OBJECT (task), task_weak_notify, (gpointer)&weak_notify_ran);
g_signal_connect (task, "notify::completed", g_signal_connect (task, "notify::completed",
(GCallback) completed_cb, &notification_emitted); (GCallback) completed_cb, &notification_emitted);
g_task_set_task_data (task, (gpointer)&thread_ran, NULL); g_task_set_task_data (task, (gpointer)&thread_ran, NULL);
g_task_run_in_thread (task, run_in_thread_thread); g_task_run_in_thread (task, run_in_thread_thread);
g_object_unref (task);
g_mutex_lock (&run_in_thread_mutex); g_mutex_lock (&run_in_thread_mutex);
while (!thread_ran) while (!thread_ran)
@ -858,6 +900,10 @@ test_run_in_thread (void)
g_assert (done == TRUE); g_assert (done == TRUE);
g_assert_true (notification_emitted); g_assert_true (notification_emitted);
g_assert_cmpstr (g_task_get_name (task), ==, "test_run_in_thread name");
g_object_unref (task);
g_mutex_lock (&run_in_thread_mutex); g_mutex_lock (&run_in_thread_mutex);
while (!weak_notify_ran) while (!weak_notify_ran)
g_cond_wait (&run_in_thread_cond, &run_in_thread_mutex); g_cond_wait (&run_in_thread_cond, &run_in_thread_mutex);
@ -2139,6 +2185,7 @@ main (int argc, char **argv)
g_test_add_func ("/gtask/no-callback", test_no_callback); g_test_add_func ("/gtask/no-callback", test_no_callback);
g_test_add_func ("/gtask/report-error", test_report_error); g_test_add_func ("/gtask/report-error", test_report_error);
g_test_add_func ("/gtask/priority", test_priority); g_test_add_func ("/gtask/priority", test_priority);
g_test_add_func ("/gtask/name", test_name);
g_test_add_func ("/gtask/check-cancellable", test_check_cancellable); g_test_add_func ("/gtask/check-cancellable", test_check_cancellable);
g_test_add_func ("/gtask/return-if-cancelled", test_return_if_cancelled); g_test_add_func ("/gtask/return-if-cancelled", test_return_if_cancelled);
g_test_add_func ("/gtask/run-in-thread", test_run_in_thread); g_test_add_func ("/gtask/run-in-thread", test_run_in_thread);