summaryrefslogtreecommitdiff
path: root/gio
diff options
context:
space:
mode:
authorPhilip Withnall <pwithnall@endlessos.org>2023-04-25 17:26:28 +0100
committerPhilip Withnall <pwithnall@endlessos.org>2023-04-27 12:23:25 +0100
commitc3209f1d84a3d755bf1610a5d1c95f55ac914a4e (patch)
tree2d43e449df2d71334af45d2e169df6784bacef50 /gio
parent7b18e6205a03058541391e02c013c893b144e1ed (diff)
downloadglib-c3209f1d84a3d755bf1610a5d1c95f55ac914a4e.tar.gz
gthreadedresolver: Re-add cancellation and add timeout support
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Diffstat (limited to 'gio')
-rw-r--r--gio/gthreadedresolver.c97
1 files changed, 97 insertions, 0 deletions
diff --git a/gio/gthreadedresolver.c b/gio/gthreadedresolver.c
index 1277907e7..baa5a4d50 100644
--- a/gio/gthreadedresolver.c
+++ b/gio/gthreadedresolver.c
@@ -28,6 +28,7 @@
#include <stdio.h>
#include <string.h>
+#include "glib/glib-private.h"
#include "gthreadedresolver.h"
#include "gnetworkingprivate.h"
@@ -120,6 +121,9 @@ typedef struct {
GCond cond; /* used for signalling completion of the task when running it sync */
GMutex lock;
+ GSource *timeout_source; /* (nullable) (owned) */
+ GSource *cancellable_source; /* (nullable) (owned) */
+
/* This enum indicates that a particular code path has claimed the
* task and is shortly about to call g_task_return_*() on it.
* This must be accessed with GThreadedResolver.lock held. */
@@ -127,6 +131,8 @@ typedef struct {
{
NOT_YET,
COMPLETED, /* libc lookup call has completed successfully or errored */
+ TIMED_OUT,
+ CANCELLED,
} will_return;
/* Whether the thread pool thread executing this lookup has finished executing
@@ -189,6 +195,18 @@ lookup_data_free (LookupData *data)
g_assert_not_reached ();
}
+ if (data->timeout_source != NULL)
+ {
+ g_source_destroy (data->timeout_source);
+ g_clear_pointer (&data->timeout_source, g_source_unref);
+ }
+
+ if (data->cancellable_source != NULL)
+ {
+ g_source_destroy (data->cancellable_source);
+ g_clear_pointer (&data->cancellable_source, g_source_unref);
+ }
+
g_mutex_clear (&data->lock);
g_cond_clear (&data->cond);
@@ -1358,16 +1376,95 @@ lookup_records_finish (GResolver *resolver,
return g_task_propagate_pointer (G_TASK (result), error);
}
+/* Will be called in the GLib worker thread, so must lock all accesses to shared
+ * data. */
+static gboolean
+timeout_cb (gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ LookupData *data = g_task_get_task_data (task);
+ gboolean should_return;
+
+ g_mutex_lock (&data->lock);
+
+ should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, TIMED_OUT);
+ g_clear_pointer (&data->timeout_source, g_source_unref);
+
+ g_mutex_unlock (&data->lock);
+
+ if (should_return)
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ _("Socket I/O timed out"));
+
+ /* Signal completion of the task. */
+ g_mutex_lock (&data->lock);
+ g_assert (!data->has_returned);
+ data->has_returned = TRUE;
+ g_cond_broadcast (&data->cond);
+ g_mutex_unlock (&data->lock);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* Will be called in the GLib worker thread, so must lock all accesses to shared
+ * data. */
+static gboolean
+cancelled_cb (GCancellable *cancellable,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (user_data);
+ LookupData *data = g_task_get_task_data (task);
+ gboolean should_return;
+
+ g_mutex_lock (&data->lock);
+
+ g_assert (g_cancellable_is_cancelled (cancellable));
+ should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, CANCELLED);
+ g_clear_pointer (&data->cancellable_source, g_source_unref);
+
+ g_mutex_unlock (&data->lock);
+
+ if (should_return)
+ g_task_return_error_if_cancelled (task);
+
+ /* Signal completion of the task. */
+ g_mutex_lock (&data->lock);
+ g_assert (!data->has_returned);
+ data->has_returned = TRUE;
+ g_cond_broadcast (&data->cond);
+ g_mutex_unlock (&data->lock);
+
+ return G_SOURCE_REMOVE;
+}
+
static void
run_task_in_thread_pool_async (GThreadedResolver *self,
GTask *task)
{
LookupData *data = g_task_get_task_data (task);
+ guint timeout_ms = g_resolver_get_timeout (G_RESOLVER (self));
+ GCancellable *cancellable = g_task_get_cancellable (task);
g_mutex_lock (&data->lock);
g_thread_pool_push (self->thread_pool, g_object_ref (task), NULL);
+ if (timeout_ms != 0)
+ {
+ data->timeout_source = g_timeout_source_new (timeout_ms);
+ g_source_set_static_name (data->timeout_source, "[gio] threaded resolver timeout");
+ g_source_set_callback (data->timeout_source, G_SOURCE_FUNC (timeout_cb), task, NULL);
+ g_source_attach (data->timeout_source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
+ }
+
+ if (cancellable != NULL)
+ {
+ data->cancellable_source = g_cancellable_source_new (cancellable);
+ g_source_set_static_name (data->cancellable_source, "[gio] threaded resolver cancellable");
+ g_source_set_callback (data->cancellable_source, G_SOURCE_FUNC (cancelled_cb), task, NULL);
+ g_source_attach (data->cancellable_source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
+ }
+
g_mutex_unlock (&data->lock);
}