summaryrefslogtreecommitdiff
path: root/gio
diff options
context:
space:
mode:
Diffstat (limited to 'gio')
-rw-r--r--gio/gapplication.c2
-rw-r--r--gio/gfile.c163
-rw-r--r--gio/gfileinfo.c8
-rw-r--r--gio/giomodule.c4
-rw-r--r--gio/gregistrysettingsbackend.c3
-rw-r--r--gio/gresolver.c124
-rw-r--r--gio/gresolver.h5
-rw-r--r--gio/gsocket.c2
-rw-r--r--gio/gtask.c72
-rw-r--r--gio/gtask.h6
-rw-r--r--gio/gtestdbus.c5
-rw-r--r--gio/gthreadedresolver.c585
-rw-r--r--gio/gthreadedresolver.h25
-rw-r--r--gio/gunixconnection.c23
-rw-r--r--gio/gunixmounts.c2
-rw-r--r--gio/gwin32networkmonitor.c24
-rw-r--r--gio/inotify/meson.build11
-rw-r--r--gio/kqueue/meson.build9
-rw-r--r--gio/meson.build26
-rw-r--r--gio/tests/appmonitor.c14
-rw-r--r--gio/tests/desktop-files/usr/applications/glade.desktop18
-rw-r--r--gio/tests/file.c86
-rw-r--r--gio/tests/gapplication.c6
-rw-r--r--gio/tests/gdbus-connection-slow.c10
-rw-r--r--gio/tests/gmenumodel.c6
-rw-r--r--gio/tests/gsubprocess.c12
-rw-r--r--gio/tests/meson.build72
-rw-r--r--gio/tests/resolver.c16
-rw-r--r--gio/tests/socket-common.c2
-rw-r--r--gio/tests/socket-testclient.c (renamed from gio/tests/socket-client.c)0
-rw-r--r--gio/tests/socket-testserver.c (renamed from gio/tests/socket-server.c)0
-rw-r--r--gio/win32/meson.build8
32 files changed, 1104 insertions, 245 deletions
diff --git a/gio/gapplication.c b/gio/gapplication.c
index 3708e812c..c52f94cba 100644
--- a/gio/gapplication.c
+++ b/gio/gapplication.c
@@ -2230,7 +2230,7 @@ g_application_register (GApplication *application,
* Increases the use count of @application.
*
* Use this function to indicate that the application has a reason to
- * continue to run. For example, g_application_hold() is called by GTK+
+ * continue to run. For example, g_application_hold() is called by GTK
* when a toplevel window is on the screen.
*
* To cancel the hold, call g_application_release().
diff --git a/gio/gfile.c b/gio/gfile.c
index 94786c84a..31da0b05e 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -2807,6 +2807,11 @@ g_file_build_attribute_list_for_copy (GFile *file,
first = TRUE;
s = g_string_new ("");
+ /* Always query the source file size, even though we can’t set that on the
+ * destination. This is useful for the copy functions. */
+ first = FALSE;
+ g_string_append (s, G_FILE_ATTRIBUTE_STANDARD_SIZE);
+
if (attributes)
{
for (i = 0; i < attributes->n_infos; i++)
@@ -3002,6 +3007,122 @@ copy_stream_with_progress (GInputStream *in,
return res;
}
+#ifdef HAVE_COPY_FILE_RANGE
+static gboolean
+do_copy_file_range (int fd_in,
+ loff_t *off_in,
+ int fd_out,
+ loff_t *off_out,
+ size_t len,
+ size_t *bytes_transferred,
+ GError **error)
+{
+ ssize_t result;
+
+ do
+ {
+ result = copy_file_range (fd_in, off_in, fd_out, off_out, len, 0);
+
+ if (result == -1)
+ {
+ int errsv = errno;
+
+ if (errsv == EINTR)
+ {
+ continue;
+ }
+ else if (errsv == ENOSYS || errsv == EINVAL || errsv == EOPNOTSUPP || errsv == EXDEV)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Copy file range not supported"));
+ }
+ else
+ {
+ g_set_error (error, G_IO_ERROR,
+ g_io_error_from_errno (errsv),
+ _("Error splicing file: %s"),
+ g_strerror (errsv));
+ }
+
+ return FALSE;
+ }
+ } while (result == -1);
+
+ g_assert (result >= 0);
+ *bytes_transferred = result;
+
+ return TRUE;
+}
+
+static gboolean
+copy_file_range_with_progress (GInputStream *in,
+ GFileInfo *in_info,
+ GOutputStream *out,
+ GCancellable *cancellable,
+ GFileProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ GError **error)
+{
+ goffset total_size, last_notified_size;
+ size_t copy_len;
+ loff_t offset_in;
+ loff_t offset_out;
+ int fd_in, fd_out;
+
+ fd_in = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (in));
+ fd_out = g_file_descriptor_based_get_fd (G_FILE_DESCRIPTOR_BASED (out));
+
+ g_assert (g_file_info_has_attribute (in_info, G_FILE_ATTRIBUTE_STANDARD_SIZE));
+ total_size = g_file_info_get_size (in_info);
+
+ /* Bail out if the reported size of the file is zero. It might be zero, but it
+ * might also just be a kernel file in /proc. They report their file size as
+ * zero, but then have data when you start reading. Go to the fallback code
+ * path for those. */
+ if (total_size == 0)
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Copy file range not supported"));
+ return FALSE;
+ }
+
+ offset_in = offset_out = 0;
+ copy_len = total_size;
+ last_notified_size = 0;
+
+ /* Call copy_file_range() in a loop until the whole contents are copied. For
+ * smaller files, this loop will iterate only once. For larger files, the
+ * kernel (at least, kernel 6.1.6) will return after 2GB anyway, so that gives
+ * us more loop iterations and more progress reporting. */
+ while (copy_len > 0)
+ {
+ size_t n_copied;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error) ||
+ !do_copy_file_range (fd_in, &offset_in, fd_out, &offset_out, copy_len, &n_copied, error))
+ return FALSE;
+
+ if (n_copied == 0)
+ break;
+
+ g_assert (n_copied <= copy_len);
+ copy_len -= n_copied;
+
+ if (progress_callback)
+ {
+ progress_callback (offset_in, total_size, progress_callback_data);
+ last_notified_size = total_size;
+ }
+ }
+
+ /* Make sure we send full copied size */
+ if (progress_callback && last_notified_size != total_size)
+ progress_callback (offset_in, total_size, progress_callback_data);
+
+ return TRUE;
+}
+#endif /* HAVE_COPY_FILE_RANGE */
+
#ifdef HAVE_SPLICE
static gboolean
@@ -3042,6 +3163,7 @@ retry:
static gboolean
splice_stream_with_progress (GInputStream *in,
+ GFileInfo *in_info,
GOutputStream *out,
GCancellable *cancellable,
GFileProgressCallback progress_callback,
@@ -3085,10 +3207,8 @@ splice_stream_with_progress (GInputStream *in,
/* avoid performance impact of querying total size when it's not needed */
if (progress_callback)
{
- struct stat sbuf;
-
- if (fstat (fd_in, &sbuf) == 0)
- total_size = sbuf.st_size;
+ g_assert (g_file_info_has_attribute (in_info, G_FILE_ATTRIBUTE_STANDARD_SIZE));
+ total_size = g_file_info_get_size (in_info);
}
if (total_size == -1)
@@ -3151,6 +3271,7 @@ splice_stream_with_progress (GInputStream *in,
#ifdef __linux__
static gboolean
btrfs_reflink_with_progress (GInputStream *in,
+ GFileInfo *in_info,
GOutputStream *out,
GFileInfo *info,
GCancellable *cancellable,
@@ -3169,10 +3290,8 @@ btrfs_reflink_with_progress (GInputStream *in,
/* avoid performance impact of querying total size when it's not needed */
if (progress_callback)
{
- struct stat sbuf;
-
- if (fstat (fd_in, &sbuf) == 0)
- total_size = sbuf.st_size;
+ g_assert (g_file_info_has_attribute (in_info, G_FILE_ATTRIBUTE_STANDARD_SIZE));
+ total_size = g_file_info_get_size (in_info);
}
if (total_size == -1)
@@ -3376,7 +3495,7 @@ file_copy_fallback (GFile *source,
{
GError *reflink_err = NULL;
- if (!btrfs_reflink_with_progress (in, out, info, cancellable,
+ if (!btrfs_reflink_with_progress (in, info, out, info, cancellable,
progress_callback, progress_callback_data,
&reflink_err))
{
@@ -3398,12 +3517,36 @@ file_copy_fallback (GFile *source,
}
#endif
+#ifdef HAVE_COPY_FILE_RANGE
+ if (G_IS_FILE_DESCRIPTOR_BASED (in) && G_IS_FILE_DESCRIPTOR_BASED (out))
+ {
+ GError *copy_file_range_error = NULL;
+
+ if (copy_file_range_with_progress (in, info, out, cancellable,
+ progress_callback, progress_callback_data,
+ &copy_file_range_error))
+ {
+ ret = TRUE;
+ goto out;
+ }
+ else if (!g_error_matches (copy_file_range_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+ {
+ g_propagate_error (error, g_steal_pointer (&copy_file_range_error));
+ goto out;
+ }
+ else
+ {
+ g_clear_error (&copy_file_range_error);
+ }
+ }
+#endif /* HAVE_COPY_FILE_RANGE */
+
#ifdef HAVE_SPLICE
if (G_IS_FILE_DESCRIPTOR_BASED (in) && G_IS_FILE_DESCRIPTOR_BASED (out))
{
GError *splice_err = NULL;
- if (!splice_stream_with_progress (in, out, cancellable,
+ if (!splice_stream_with_progress (in, info, out, cancellable,
progress_callback, progress_callback_data,
&splice_err))
{
diff --git a/gio/gfileinfo.c b/gio/gfileinfo.c
index d9c7d70f9..ca42add8e 100644
--- a/gio/gfileinfo.c
+++ b/gio/gfileinfo.c
@@ -1500,8 +1500,8 @@ g_file_info_set_attribute_int64 (GFileInfo *info,
*value_ptr = g_file_info_find_value (info, attr); \
if (G_UNLIKELY (*value_ptr == NULL)) \
{ \
- g_debug ("GFileInfo created without " attribute_name); \
- return error_value; \
+ g_critical ("GFileInfo created without " attribute_name); \
+ g_return_val_if_reached (error_value); \
} \
} G_STMT_END
@@ -1837,9 +1837,9 @@ g_file_info_get_modification_time (GFileInfo *info,
if (G_UNLIKELY (value == NULL))
{
- g_debug ("GFileInfo created without " G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ g_critical ("GFileInfo created without " G_FILE_ATTRIBUTE_TIME_MODIFIED);
result->tv_sec = result->tv_usec = 0;
- return;
+ g_return_if_reached ();
}
result->tv_sec = _g_file_attribute_value_get_uint64 (value);
diff --git a/gio/giomodule.c b/gio/giomodule.c
index 47b8de27e..17fabe640 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -1273,7 +1273,9 @@ get_gio_module_dir (void)
module_dir = g_strdup (GIO_MODULE_DIR);
#ifdef __APPLE__
#include "TargetConditionals.h"
-#if TARGET_OS_OSX
+/* Only auto-relocate on macOS, not watchOS etc; older macOS SDKs only define TARGET_OS_MAC */
+#if (defined (TARGET_OS_OSX) && TARGET_OS_OSX) || \
+ (!defined (TARGET_OS_OSX) && defined (TARGET_OS_MAC) && TARGET_OS_MAC)
#include <dlfcn.h>
{
g_autofree gchar *path = NULL;
diff --git a/gio/gregistrysettingsbackend.c b/gio/gregistrysettingsbackend.c
index 772fae037..88ae913ce 100644
--- a/gio/gregistrysettingsbackend.c
+++ b/gio/gregistrysettingsbackend.c
@@ -2319,8 +2319,7 @@ g_registry_settings_backend_class_init (GRegistrySettingsBackendClass *class)
g_object_class_install_property (object_class,
PROP_REGISTRY_KEY,
g_param_spec_string ("registry-key",
- P_("Root registry key"),
- P_("The path to the registry key where settings are stored"),
+ NULL, NULL,
"HKEY_CURRENT_USER\\Software\\GSettings",
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
diff --git a/gio/gresolver.c b/gio/gresolver.c
index 6a735e8d9..676f1e271 100644
--- a/gio/gresolver.c
+++ b/gio/gresolver.c
@@ -55,8 +55,18 @@
* #GNetworkAddress and #GNetworkService provide wrappers around
* #GResolver functionality that also implement #GSocketConnectable,
* making it easy to connect to a remote host/service.
+ *
+ * The default resolver (see g_resolver_get_default()) has a timeout of 30s set
+ * on it since GLib 2.78. Earlier versions of GLib did not support resolver
+ * timeouts.
*/
+typedef enum {
+ PROP_TIMEOUT = 1,
+} GResolverProperty;
+
+static GParamSpec *props[PROP_TIMEOUT + 1] = { NULL, };
+
enum {
RELOAD,
LAST_SIGNAL
@@ -65,11 +75,11 @@ enum {
static guint signals[LAST_SIGNAL] = { 0 };
struct _GResolverPrivate {
+ unsigned timeout_ms;
+
#ifdef G_OS_UNIX
GMutex mutex;
time_t resolv_conf_timestamp; /* protected by @mutex */
-#else
- int dummy;
#endif
};
@@ -152,6 +162,42 @@ g_resolver_real_lookup_service_finish (GResolver *resolver,
}
static void
+g_resolver_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GResolver *self = G_RESOLVER (object);
+
+ switch ((GResolverProperty) prop_id)
+ {
+ case PROP_TIMEOUT:
+ g_value_set_uint (value, g_resolver_get_timeout (self));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_resolver_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GResolver *self = G_RESOLVER (object);
+
+ switch ((GResolverProperty) prop_id)
+ {
+ case PROP_TIMEOUT:
+ g_resolver_set_timeout (self, g_value_get_uint (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
g_resolver_finalize (GObject *object)
{
#ifdef G_OS_UNIX
@@ -168,6 +214,8 @@ g_resolver_class_init (GResolverClass *resolver_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (resolver_class);
+ object_class->get_property = g_resolver_get_property;
+ object_class->set_property = g_resolver_set_property;
object_class->finalize = g_resolver_finalize;
/* Automatically pass these over to the lookup_records methods */
@@ -176,6 +224,31 @@ g_resolver_class_init (GResolverClass *resolver_class)
resolver_class->lookup_service_finish = g_resolver_real_lookup_service_finish;
/**
+ * GResolver:timeout:
+ *
+ * The timeout applied to all resolver lookups, in milliseconds.
+ *
+ * This may be changed through the lifetime of the #GResolver. The new value
+ * will apply to any lookups started after the change, but not to any
+ * already-ongoing lookups.
+ *
+ * If this is `0`, no timeout is applied to lookups.
+ *
+ * No timeout was applied to lookups before this property was added in
+ * GLib 2.78.
+ *
+ * Since: 2.78
+ */
+ props[PROP_TIMEOUT] =
+ g_param_spec_uint ("timeout",
+ P_("Timeout"),
+ P_("Timeout (ms) applied to all resolver lookups"),
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
+
+ /**
* GResolver::reload:
* @resolver: a #GResolver
*
@@ -230,7 +303,9 @@ g_resolver_get_default (void)
G_LOCK (default_resolver);
if (!default_resolver)
- default_resolver = g_object_new (G_TYPE_THREADED_RESOLVER, NULL);
+ default_resolver = g_object_new (G_TYPE_THREADED_RESOLVER,
+ "timeout", 30000,
+ NULL);
ret = g_object_ref (default_resolver);
G_UNLOCK (default_resolver);
@@ -1245,6 +1320,49 @@ g_resolver_get_serial (GResolver *resolver)
}
/**
+ * g_resolver_get_timeout:
+ * @resolver: a #GResolver
+ *
+ * Get the timeout applied to all resolver lookups. See #GResolver:timeout.
+ *
+ * Returns: the resolver timeout, in milliseconds, or `0` for no timeout
+ * Since: 2.78
+ */
+unsigned
+g_resolver_get_timeout (GResolver *resolver)
+{
+ GResolverPrivate *priv = g_resolver_get_instance_private (resolver);
+
+ g_return_val_if_fail (G_IS_RESOLVER (resolver), 0);
+
+ return priv->timeout_ms;
+}
+
+/**
+ * g_resolver_set_timeout:
+ * @resolver: a #GResolver
+ * @timeout_ms: timeout in milliseconds, or `0` for no timeouts
+ *
+ * Set the timeout applied to all resolver lookups. See #GResolver:timeout.
+ *
+ * Since: 2.78
+ */
+void
+g_resolver_set_timeout (GResolver *resolver,
+ unsigned timeout_ms)
+{
+ GResolverPrivate *priv = g_resolver_get_instance_private (resolver);
+
+ g_return_if_fail (G_IS_RESOLVER (resolver));
+
+ if (priv->timeout_ms == timeout_ms)
+ return;
+
+ priv->timeout_ms = timeout_ms;
+ g_object_notify_by_pspec (G_OBJECT (resolver), props[PROP_TIMEOUT]);
+}
+
+/**
* g_resolver_error_quark:
*
* Gets the #GResolver Error Quark.
diff --git a/gio/gresolver.h b/gio/gresolver.h
index 2dffeadbf..9b9a8a81a 100644
--- a/gio/gresolver.h
+++ b/gio/gresolver.h
@@ -277,6 +277,11 @@ GList *g_resolver_lookup_records_finish (GResolver
GIO_AVAILABLE_IN_ALL
void g_resolver_free_targets (GList *targets);
+GIO_AVAILABLE_IN_2_78
+unsigned g_resolver_get_timeout (GResolver *resolver);
+GIO_AVAILABLE_IN_2_78
+void g_resolver_set_timeout (GResolver *resolver,
+ unsigned timeout_ms);
/**
* G_RESOLVER_ERROR:
diff --git a/gio/gsocket.c b/gio/gsocket.c
index d25591a17..3411b7785 100644
--- a/gio/gsocket.c
+++ b/gio/gsocket.c
@@ -3400,7 +3400,7 @@ g_socket_receive_with_blocking (GSocket *socket,
* pointer, or %NULL
* @buffer: (array length=size) (element-type guint8) (out caller-allocates):
* a buffer to read data into (which should be at least @size bytes long).
- * @size: the number of bytes you want to read from the socket
+ * @size: (in): the number of bytes you want to read from the socket
* @cancellable: (nullable): a %GCancellable or %NULL
* @error: #GError for error reporting, or %NULL to ignore.
*
diff --git a/gio/gtask.c b/gio/gtask.c
index 80df75286..c436110aa 100644
--- a/gio/gtask.c
+++ b/gio/gtask.c
@@ -676,6 +676,58 @@ g_task_init (GTask *task)
task->check_cancellable = TRUE;
}
+#ifdef G_ENABLE_DEBUG
+G_LOCK_DEFINE_STATIC (task_list);
+static GPtrArray *task_list = NULL;
+
+void
+g_task_print_alive_tasks (void)
+{
+ GString *message_str = g_string_new ("");
+
+ G_LOCK (task_list);
+
+ if (task_list != NULL)
+ {
+ g_string_append_printf (message_str, "%u GTasks still alive:", task_list->len);
+ for (guint i = 0; i < task_list->len; i++)
+ {
+ GTask *task = g_ptr_array_index (task_list, i);
+ const gchar *name = g_task_get_name (task);
+ g_string_append_printf (message_str,
+ "\n • GTask %p, %s, ref count: %u, ever_returned: %u, completed: %u",
+ task, (name != NULL) ? name : "(no name set)",
+ ((GObject *) task)->ref_count,
+ task->ever_returned, task->completed);
+ }
+ }
+ else
+ {
+ g_string_append (message_str, "No GTasks still alive");
+ }
+
+ G_UNLOCK (task_list);
+
+ g_message ("%s", message_str->str);
+ g_string_free (message_str, TRUE);
+}
+
+static void
+g_task_constructed (GObject *object)
+{
+ GTask *task = G_TASK (object);
+
+ G_OBJECT_CLASS (g_task_parent_class)->constructed (object);
+
+ /* Track pending tasks for debugging purposes */
+ G_LOCK (task_list);
+ if (G_UNLIKELY (task_list == NULL))
+ task_list = g_ptr_array_new ();
+ g_ptr_array_add (task_list, task);
+ G_UNLOCK (task_list);
+}
+#endif /* G_ENABLE_DEBUG */
+
static void
g_task_finalize (GObject *object)
{
@@ -732,6 +784,16 @@ g_task_finalize (GObject *object)
g_cond_clear (&task->cond);
}
+ /* Track pending tasks for debugging purposes */
+#ifdef G_ENABLE_DEBUG
+ G_LOCK (task_list);
+ g_assert (task_list != NULL);
+ g_ptr_array_remove_fast (task_list, task);
+ if (G_UNLIKELY (task_list->len == 0))
+ g_clear_pointer (&task_list, g_ptr_array_unref);
+ G_UNLOCK (task_list);
+#endif /* G_ENABLE_DEBUG */
+
G_OBJECT_CLASS (g_task_parent_class)->finalize (object);
}
@@ -1634,6 +1696,13 @@ g_task_start_task_thread (GTask *task,
* tasks), but don't want them to all run at once, you should only queue a
* limited number of them (around ten) at a time.
*
+ * Be aware that if your task depends on other tasks to complete, use of this
+ * function could lead to a livelock if the other tasks also use this function
+ * and enough of them (around 10) execute in a dependency chain, as that will
+ * exhaust the thread pool. If this situation is possible, consider using a
+ * separate worker thread or thread pool explicitly, rather than using
+ * g_task_run_in_thread().
+ *
* Since: 2.36
*/
void
@@ -2309,6 +2378,9 @@ g_task_class_init (GTaskClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+#ifdef G_ENABLE_DEBUG
+ gobject_class->constructed = g_task_constructed;
+#endif
gobject_class->get_property = g_task_get_property;
gobject_class->finalize = g_task_finalize;
diff --git a/gio/gtask.h b/gio/gtask.h
index 1c2490fab..7642d2c68 100644
--- a/gio/gtask.h
+++ b/gio/gtask.h
@@ -194,6 +194,12 @@ gboolean g_task_had_error (GTask *task);
GIO_AVAILABLE_IN_2_44
gboolean g_task_get_completed (GTask *task);
+/*< private >*/
+#ifndef __GTK_DOC_IGNORE__
+/* Debugging API, not part of the public API */
+void g_task_print_alive_tasks (void);
+#endif /* !__GTK_DOC_IGNORE__ */
+
G_END_DECLS
#endif /* __G_TASK_H__ */
diff --git a/gio/gtestdbus.c b/gio/gtestdbus.c
index 8c5de3355..9ff74e653 100644
--- a/gio/gtestdbus.c
+++ b/gio/gtestdbus.c
@@ -62,13 +62,12 @@ typedef struct
gboolean timed_out;
} WeakNotifyData;
-static gboolean
+static void
on_weak_notify_timeout (gpointer user_data)
{
WeakNotifyData *data = user_data;
data->timed_out = TRUE;
g_main_loop_quit (data->loop);
- return FALSE;
}
static gboolean
@@ -95,7 +94,7 @@ _g_object_unref_and_wait_weak_notify (gpointer object)
g_idle_add (unref_on_idle, object);
/* Make sure we don't block forever */
- timeout_id = g_timeout_add_seconds (30, on_weak_notify_timeout, &data);
+ timeout_id = g_timeout_add_seconds_once (30, on_weak_notify_timeout, &data);
g_main_loop_run (data.loop);
diff --git a/gio/gthreadedresolver.c b/gio/gthreadedresolver.c
index 68b5c20d7..2d94531bf 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"
@@ -38,12 +39,83 @@
#include "gsocketaddress.h"
#include "gsrvtarget.h"
+/*
+ * GThreadedResolver is a threaded wrapper around the system libc’s
+ * `getaddrinfo()`.
+ *
+ * It has to be threaded, as `getaddrinfo()` is synchronous. libc does provide
+ * `getaddrinfo_a()` as an asynchronous version of `getaddrinfo()`, but it does
+ * not integrate with a poll loop. It requires use of sigevent to notify of
+ * completion of an asynchronous operation. That either emits a signal, or calls
+ * a callback function in a newly spawned thread.
+ *
+ * A signal (`SIGEV_SIGNAL`) can’t be used for completion as (aside from being
+ * another expensive round trip into the kernel) GLib cannot pick a `SIG*`
+ * number which is guaranteed to not be in use elsewhere in the process. Various
+ * other things could be interfering with signal dispositions, such as gdb or
+ * other libraries in the process. Using a `signalfd()`
+ * [cannot improve this situation](https://ldpreload.com/blog/signalfd-is-useless).
+ *
+ * A callback function in a newly spawned thread (`SIGEV_THREAD`) could be used,
+ * but that is very expensive. Internally, glibc currently also just implements
+ * `getaddrinfo_a()`
+ * [using its own thread pool](https://github.com/bminor/glibc/blob/master/resolv/gai_misc.c),
+ * and then
+ * [spawns an additional thread for each completion callback](https://github.com/bminor/glibc/blob/master/resolv/gai_notify.c).
+ * That is very expensive.
+ *
+ * No other appropriate sigevent callback types
+ * [currently exist](https://sourceware.org/bugzilla/show_bug.cgi?id=30287), and
+ * [others agree that sigevent is not great](http://davmac.org/davpage/linux/async-io.html#posixaio).
+ *
+ * Hence, #GThreadedResolver calls the normal synchronous `getaddrinfo()` in its
+ * own thread pool. Previously, #GThreadedResolver used the thread pool which is
+ * internal to #GTask by calling g_task_run_in_thread(). That lead to exhaustion
+ * of the #GTask thread pool in some situations, though, as DNS lookups are
+ * quite frequent leaf operations in some use cases. Now, #GThreadedResolver
+ * uses its own private thread pool.
+ *
+ * This is similar to what
+ * [libasyncns](http://git.0pointer.net/libasyncns.git/tree/libasyncns/asyncns.h)
+ * and other multi-threaded users of `getaddrinfo()` do.
+ */
+
+struct _GThreadedResolver
+{
+ GResolver parent_instance;
+
+ GThreadPool *thread_pool; /* (owned) */
+};
G_DEFINE_TYPE (GThreadedResolver, g_threaded_resolver, G_TYPE_RESOLVER)
+static void run_task_in_thread_pool_async (GThreadedResolver *self,
+ GTask *task);
+static void run_task_in_thread_pool_sync (GThreadedResolver *self,
+ GTask *task);
+static void threaded_resolver_worker_cb (gpointer task_data,
+ gpointer user_data);
+
static void
-g_threaded_resolver_init (GThreadedResolver *gtr)
+g_threaded_resolver_init (GThreadedResolver *self)
{
+ self->thread_pool = g_thread_pool_new_full (threaded_resolver_worker_cb,
+ self,
+ (GDestroyNotify) g_object_unref,
+ 20,
+ FALSE,
+ NULL);
+}
+
+static void
+g_threaded_resolver_finalize (GObject *object)
+{
+ GThreadedResolver *self = G_THREADED_RESOLVER (object);
+
+ g_thread_pool_free (self->thread_pool, TRUE, FALSE);
+ self->thread_pool = NULL;
+
+ G_OBJECT_CLASS (g_threaded_resolver_parent_class)->finalize (object);
}
static GResolverError
@@ -67,35 +139,127 @@ g_resolver_error_from_addrinfo_error (gint err)
}
typedef struct {
- char *hostname;
- int address_family;
+ enum {
+ LOOKUP_BY_NAME,
+ LOOKUP_BY_ADDRESS,
+ LOOKUP_RECORDS,
+ } lookup_type;
+
+ union {
+ struct {
+ char *hostname;
+ int address_family;
+ } lookup_by_name;
+ struct {
+ GInetAddress *address; /* (owned) */
+ } lookup_by_address;
+ struct {
+ char *rrname;
+ GResolverRecordType record_type;
+ } lookup_records;
+ };
+
+ 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. */
+ enum
+ {
+ 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
+ * it and g_task_return_*() has been called on it already.
+ * This must be accessed with GThreadedResolver.lock held. */
+ gboolean has_returned;
} LookupData;
static LookupData *
-lookup_data_new (const char *hostname,
- int address_family)
+lookup_data_new_by_name (const char *hostname,
+ int address_family)
{
- LookupData *data = g_new (LookupData, 1);
- data->hostname = g_strdup (hostname);
- data->address_family = address_family;
- return data;
+ LookupData *data = g_new0 (LookupData, 1);
+ data->lookup_type = LOOKUP_BY_NAME;
+ g_cond_init (&data->cond);
+ g_mutex_init (&data->lock);
+ data->lookup_by_name.hostname = g_strdup (hostname);
+ data->lookup_by_name.address_family = address_family;
+ return g_steal_pointer (&data);
+}
+
+static LookupData *
+lookup_data_new_by_address (GInetAddress *address)
+{
+ LookupData *data = g_new0 (LookupData, 1);
+ data->lookup_type = LOOKUP_BY_ADDRESS;
+ g_cond_init (&data->cond);
+ g_mutex_init (&data->lock);
+ data->lookup_by_address.address = g_object_ref (address);
+ return g_steal_pointer (&data);
+}
+
+static LookupData *
+lookup_data_new_records (const gchar *rrname,
+ GResolverRecordType record_type)
+{
+ LookupData *data = g_new0 (LookupData, 1);
+ data->lookup_type = LOOKUP_RECORDS;
+ g_cond_init (&data->cond);
+ g_mutex_init (&data->lock);
+ data->lookup_records.rrname = g_strdup (rrname);
+ data->lookup_records.record_type = record_type;
+ return g_steal_pointer (&data);
}
static void
lookup_data_free (LookupData *data)
{
- g_free (data->hostname);
+ switch (data->lookup_type) {
+ case LOOKUP_BY_NAME:
+ g_free (data->lookup_by_name.hostname);
+ break;
+ case LOOKUP_BY_ADDRESS:
+ g_clear_object (&data->lookup_by_address.address);
+ break;
+ case LOOKUP_RECORDS:
+ g_free (data->lookup_records.rrname);
+ break;
+ default:
+ 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);
+
g_free (data);
}
-static void
-do_lookup_by_name (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+static GList *
+do_lookup_by_name (const gchar *hostname,
+ int address_family,
+ GCancellable *cancellable,
+ GError **error)
{
- LookupData *lookup_data = task_data;
- const char *hostname = lookup_data->hostname;
struct addrinfo *res = NULL;
GList *addresses;
gint retval;
@@ -111,7 +275,7 @@ do_lookup_by_name (GTask *task,
addrinfo_hints.ai_socktype = SOCK_STREAM;
addrinfo_hints.ai_protocol = IPPROTO_TCP;
- addrinfo_hints.ai_family = lookup_data->address_family;
+ addrinfo_hints.ai_family = address_family;
retval = getaddrinfo (hostname, NULL, &addrinfo_hints, &res);
if (retval == 0)
@@ -137,21 +301,23 @@ do_lookup_by_name (GTask *task,
g_object_unref (sockaddr);
}
+ g_clear_pointer (&res, freeaddrinfo);
+
if (addresses != NULL)
{
addresses = g_list_reverse (addresses);
- g_task_return_pointer (task, addresses,
- (GDestroyNotify)g_resolver_free_addresses);
+ return g_steal_pointer (&addresses);
}
else
{
/* All addresses failed to be converted to GSocketAddresses. */
- g_task_return_new_error (task,
- G_RESOLVER_ERROR,
- G_RESOLVER_ERROR_NOT_FOUND,
- _("Error resolving “%s”: %s"),
- hostname,
- _("No valid addresses were found"));
+ g_set_error (error,
+ G_RESOLVER_ERROR,
+ G_RESOLVER_ERROR_NOT_FOUND,
+ _("Error resolving “%s”: %s"),
+ hostname,
+ _("No valid addresses were found"));
+ return NULL;
}
}
else
@@ -164,16 +330,17 @@ do_lookup_by_name (GTask *task,
error_message = g_strdup ("[Invalid UTF-8]");
#endif
- g_task_return_new_error (task,
- G_RESOLVER_ERROR,
- g_resolver_error_from_addrinfo_error (retval),
- _("Error resolving “%s”: %s"),
- hostname, error_message);
+ g_clear_pointer (&res, freeaddrinfo);
+
+ g_set_error (error,
+ G_RESOLVER_ERROR,
+ g_resolver_error_from_addrinfo_error (retval),
+ _("Error resolving “%s”: %s"),
+ hostname, error_message);
g_free (error_message);
- }
- if (res)
- freeaddrinfo (res);
+ return NULL;
+ }
}
static GList *
@@ -182,17 +349,19 @@ lookup_by_name (GResolver *resolver,
GCancellable *cancellable,
GError **error)
{
+ GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
GTask *task;
GList *addresses;
LookupData *data;
- data = lookup_data_new (hostname, AF_UNSPEC);
+ data = lookup_data_new_by_name (hostname, AF_UNSPEC);
task = g_task_new (resolver, cancellable, NULL, NULL);
g_task_set_source_tag (task, lookup_by_name);
g_task_set_name (task, "[gio] resolver lookup");
- g_task_set_task_data (task, data, (GDestroyNotify)lookup_data_free);
- g_task_set_return_on_cancel (task, TRUE);
- g_task_run_in_thread_sync (task, do_lookup_by_name);
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
+
+ run_task_in_thread_pool_sync (self, task);
+
addresses = g_task_propagate_pointer (task, error);
g_object_unref (task);
@@ -224,17 +393,19 @@ lookup_by_name_with_flags (GResolver *resolver,
GCancellable *cancellable,
GError **error)
{
+ GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
GTask *task;
GList *addresses;
LookupData *data;
- data = lookup_data_new (hostname, flags_to_family (flags));
+ data = lookup_data_new_by_name (hostname, flags_to_family (flags));
task = g_task_new (resolver, cancellable, NULL, NULL);
g_task_set_source_tag (task, lookup_by_name_with_flags);
g_task_set_name (task, "[gio] resolver lookup");
- g_task_set_task_data (task, data, (GDestroyNotify)lookup_data_free);
- g_task_set_return_on_cancel (task, TRUE);
- g_task_run_in_thread_sync (task, do_lookup_by_name);
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
+
+ run_task_in_thread_pool_sync (self, task);
+
addresses = g_task_propagate_pointer (task, error);
g_object_unref (task);
@@ -249,16 +420,22 @@ lookup_by_name_with_flags_async (GResolver *resolver,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
GTask *task;
LookupData *data;
- data = lookup_data_new (hostname, flags_to_family (flags));
+ data = lookup_data_new_by_name (hostname, flags_to_family (flags));
task = g_task_new (resolver, cancellable, callback, user_data);
+
+ g_debug ("%s: starting new lookup for %s with GTask %p, LookupData %p",
+ G_STRFUNC, hostname, task, data);
+
g_task_set_source_tag (task, lookup_by_name_with_flags_async);
g_task_set_name (task, "[gio] resolver lookup");
- g_task_set_task_data (task, data, (GDestroyNotify)lookup_data_free);
- g_task_set_return_on_cancel (task, TRUE);
- g_task_run_in_thread (task, do_lookup_by_name);
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
+
+ run_task_in_thread_pool_async (self, task);
+
g_object_unref (task);
}
@@ -297,13 +474,11 @@ lookup_by_name_with_flags_finish (GResolver *resolver,
return g_task_propagate_pointer (G_TASK (result), error);
}
-static void
-do_lookup_by_address (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+static gchar *
+do_lookup_by_address (GInetAddress *address,
+ GCancellable *cancellable,
+ GError **error)
{
- GInetAddress *address = task_data;
struct sockaddr_storage sockaddr_address;
gsize sockaddr_address_size;
GSocketAddress *gsockaddr;
@@ -319,7 +494,7 @@ do_lookup_by_address (GTask *task,
retval = getnameinfo ((struct sockaddr *) &sockaddr_address, sockaddr_address_size,
name, sizeof (name), NULL, 0, NI_NAMEREQD);
if (retval == 0)
- g_task_return_pointer (task, g_strdup (name), g_free);
+ return g_strdup (name);
else
{
gchar *phys;
@@ -333,14 +508,16 @@ do_lookup_by_address (GTask *task,
#endif
phys = g_inet_address_to_string (address);
- g_task_return_new_error (task,
- G_RESOLVER_ERROR,
- g_resolver_error_from_addrinfo_error (retval),
- _("Error reverse-resolving “%s”: %s"),
- phys ? phys : "(unknown)",
- error_message);
+ g_set_error (error,
+ G_RESOLVER_ERROR,
+ g_resolver_error_from_addrinfo_error (retval),
+ _("Error reverse-resolving “%s”: %s"),
+ phys ? phys : "(unknown)",
+ error_message);
g_free (phys);
g_free (error_message);
+
+ return NULL;
}
}
@@ -350,15 +527,19 @@ lookup_by_address (GResolver *resolver,
GCancellable *cancellable,
GError **error)
{
+ GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
+ LookupData *data = NULL;
GTask *task;
gchar *name;
+ data = lookup_data_new_by_address (address);
task = g_task_new (resolver, cancellable, NULL, NULL);
g_task_set_source_tag (task, lookup_by_address);
g_task_set_name (task, "[gio] resolver lookup");
- g_task_set_task_data (task, g_object_ref (address), g_object_unref);
- g_task_set_return_on_cancel (task, TRUE);
- g_task_run_in_thread_sync (task, do_lookup_by_address);
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
+
+ run_task_in_thread_pool_sync (self, task);
+
name = g_task_propagate_pointer (task, error);
g_object_unref (task);
@@ -372,14 +553,18 @@ lookup_by_address_async (GResolver *resolver,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
+ LookupData *data = NULL;
GTask *task;
+ data = lookup_data_new_by_address (address);
task = g_task_new (resolver, cancellable, callback, user_data);
g_task_set_source_tag (task, lookup_by_address_async);
g_task_set_name (task, "[gio] resolver lookup");
- g_task_set_task_data (task, g_object_ref (address), g_object_unref);
- g_task_set_return_on_cancel (task, TRUE);
- g_task_run_in_thread (task, do_lookup_by_address);
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
+
+ run_task_in_thread_pool_async (self, task);
+
g_object_unref (task);
}
@@ -1066,18 +1251,6 @@ g_resolver_records_from_DnsQuery (const gchar *rrname,
#endif
-typedef struct {
- char *rrname;
- GResolverRecordType record_type;
-} LookupRecordsData;
-
-static void
-free_lookup_records_data (LookupRecordsData *lrd)
-{
- g_free (lrd->rrname);
- g_slice_free (LookupRecordsData, lrd);
-}
-
static void
free_records (GList *records)
{
@@ -1093,15 +1266,13 @@ int res_query(const char *, int, int, u_char *, int);
#endif
#endif
-static void
-do_lookup_records (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+static GList *
+do_lookup_records (const gchar *rrname,
+ GResolverRecordType record_type,
+ GCancellable *cancellable,
+ GError **error)
{
- LookupRecordsData *lrd = task_data;
GList *records;
- GError *error = NULL;
#if defined(G_OS_UNIX)
gint len = 512;
@@ -1125,21 +1296,21 @@ do_lookup_records (GTask *task,
struct __res_state res = { 0, };
if (res_ninit (&res) != 0)
{
- g_task_return_new_error (task, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
- _("Error resolving “%s”"), lrd->rrname);
- return;
+ g_set_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_INTERNAL,
+ _("Error resolving “%s”"), rrname);
+ return NULL;
}
#endif
- rrtype = g_resolver_record_type_to_rrtype (lrd->record_type);
+ rrtype = g_resolver_record_type_to_rrtype (record_type);
answer = g_byte_array_new ();
for (;;)
{
g_byte_array_set_size (answer, len * 2);
#if defined(HAVE_RES_NQUERY)
- len = res_nquery (&res, lrd->rrname, C_IN, rrtype, answer->data, answer->len);
+ len = res_nquery (&res, rrname, C_IN, rrtype, answer->data, answer->len);
#else
- len = res_query (lrd->rrname, C_IN, rrtype, answer->data, answer->len);
+ len = res_query (rrname, C_IN, rrtype, answer->data, answer->len);
#endif
/* If answer fit in the buffer then we're done */
@@ -1153,7 +1324,7 @@ do_lookup_records (GTask *task,
}
herr = h_errno;
- records = g_resolver_records_from_res_query (lrd->rrname, rrtype, answer->data, len, herr, &error);
+ records = g_resolver_records_from_res_query (rrname, rrtype, answer->data, len, herr, error);
g_byte_array_free (answer, TRUE);
#ifdef HAVE_RES_NQUERY
@@ -1174,18 +1345,15 @@ do_lookup_records (GTask *task,
DNS_RECORD *results = NULL;
WORD dnstype;
- dnstype = g_resolver_record_type_to_dnstype (lrd->record_type);
- status = DnsQuery_A (lrd->rrname, dnstype, DNS_QUERY_STANDARD, NULL, &results, NULL);
- records = g_resolver_records_from_DnsQuery (lrd->rrname, dnstype, status, results, &error);
+ dnstype = g_resolver_record_type_to_dnstype (record_type);
+ status = DnsQuery_A (rrname, dnstype, DNS_QUERY_STANDARD, NULL, &results, NULL);
+ records = g_resolver_records_from_DnsQuery (rrname, dnstype, status, results, error);
if (results != NULL)
DnsRecordListFree (results, DnsFreeRecordList);
#endif
- if (records)
- g_task_return_pointer (task, records, (GDestroyNotify) free_records);
- else
- g_task_return_error (task, error);
+ return g_steal_pointer (&records);
}
static GList *
@@ -1195,21 +1363,20 @@ lookup_records (GResolver *resolver,
GCancellable *cancellable,
GError **error)
{
+ GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
GTask *task;
GList *records;
- LookupRecordsData *lrd;
+ LookupData *data = NULL;
task = g_task_new (resolver, cancellable, NULL, NULL);
g_task_set_source_tag (task, lookup_records);
g_task_set_name (task, "[gio] resolver lookup records");
- lrd = g_slice_new (LookupRecordsData);
- lrd->rrname = g_strdup (rrname);
- lrd->record_type = record_type;
- g_task_set_task_data (task, lrd, (GDestroyNotify) free_lookup_records_data);
+ data = lookup_data_new_records (rrname, record_type);
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
+
+ run_task_in_thread_pool_sync (self, task);
- g_task_set_return_on_cancel (task, TRUE);
- g_task_run_in_thread_sync (task, do_lookup_records);
records = g_task_propagate_pointer (task, error);
g_object_unref (task);
@@ -1224,20 +1391,19 @@ lookup_records_async (GResolver *resolver,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ GThreadedResolver *self = G_THREADED_RESOLVER (resolver);
GTask *task;
- LookupRecordsData *lrd;
+ LookupData *data = NULL;
task = g_task_new (resolver, cancellable, callback, user_data);
g_task_set_source_tag (task, lookup_records_async);
g_task_set_name (task, "[gio] resolver lookup records");
- lrd = g_slice_new (LookupRecordsData);
- lrd->rrname = g_strdup (rrname);
- lrd->record_type = record_type;
- g_task_set_task_data (task, lrd, (GDestroyNotify) free_lookup_records_data);
+ data = lookup_data_new_records (rrname, record_type);
+ g_task_set_task_data (task, g_steal_pointer (&data), (GDestroyNotify) lookup_data_free);
+
+ run_task_in_thread_pool_async (self, task);
- g_task_set_return_on_cancel (task, TRUE);
- g_task_run_in_thread (task, do_lookup_records);
g_object_unref (task);
}
@@ -1251,12 +1417,207 @@ 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);
+ 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);
+ 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);
+}
+
+static void
+run_task_in_thread_pool_sync (GThreadedResolver *self,
+ GTask *task)
+{
+ LookupData *data = g_task_get_task_data (task);
+
+ run_task_in_thread_pool_async (self, task);
+
+ g_mutex_lock (&data->lock);
+ while (!data->has_returned)
+ g_cond_wait (&data->cond, &data->lock);
+ g_mutex_unlock (&data->lock);
+}
+
+static void
+threaded_resolver_worker_cb (gpointer task_data,
+ gpointer user_data)
+{
+ GTask *task = G_TASK (g_steal_pointer (&task_data));
+ LookupData *data = g_task_get_task_data (task);
+ GCancellable *cancellable = g_task_get_cancellable (task);
+ GError *local_error = NULL;
+ gboolean should_return;
+
+ switch (data->lookup_type) {
+ case LOOKUP_BY_NAME:
+ {
+ GList *addresses = do_lookup_by_name (data->lookup_by_name.hostname,
+ data->lookup_by_name.address_family,
+ cancellable,
+ &local_error);
+
+ g_mutex_lock (&data->lock);
+ should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
+ g_mutex_unlock (&data->lock);
+
+ if (should_return)
+ {
+ if (addresses != NULL)
+ g_task_return_pointer (task, g_steal_pointer (&addresses), (GDestroyNotify) g_resolver_free_addresses);
+ else
+ g_task_return_error (task, g_steal_pointer (&local_error));
+ }
+
+ g_clear_pointer (&addresses, g_resolver_free_addresses);
+ }
+ break;
+ case LOOKUP_BY_ADDRESS:
+ {
+ gchar *name = do_lookup_by_address (data->lookup_by_address.address,
+ cancellable,
+ &local_error);
+
+ g_mutex_lock (&data->lock);
+ should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
+ g_mutex_unlock (&data->lock);
+
+ if (should_return)
+ {
+ if (name != NULL)
+ g_task_return_pointer (task, g_steal_pointer (&name), g_free);
+ else
+ g_task_return_error (task, g_steal_pointer (&local_error));
+ }
+
+ g_clear_pointer (&name, g_free);
+ }
+ break;
+ case LOOKUP_RECORDS:
+ {
+ GList *records = do_lookup_records (data->lookup_records.rrname,
+ data->lookup_records.record_type,
+ cancellable,
+ &local_error);
+
+ g_mutex_lock (&data->lock);
+ should_return = g_atomic_int_compare_and_exchange (&data->will_return, NOT_YET, COMPLETED);
+ g_mutex_unlock (&data->lock);
+
+ if (should_return)
+ {
+ if (records != NULL)
+ g_task_return_pointer (task, g_steal_pointer (&records), (GDestroyNotify) free_records);
+ else
+ g_task_return_error (task, g_steal_pointer (&local_error));
+ }
+
+ g_clear_pointer (&records, free_records);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Signal completion of a task. */
+ g_mutex_lock (&data->lock);
+ data->has_returned = TRUE;
+ g_cond_broadcast (&data->cond);
+ g_mutex_unlock (&data->lock);
+
+ g_object_unref (task);
+}
static void
g_threaded_resolver_class_init (GThreadedResolverClass *threaded_class)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (threaded_class);
GResolverClass *resolver_class = G_RESOLVER_CLASS (threaded_class);
+ object_class->finalize = g_threaded_resolver_finalize;
+
resolver_class->lookup_by_name = lookup_by_name;
resolver_class->lookup_by_name_async = lookup_by_name_async;
resolver_class->lookup_by_name_finish = lookup_by_name_finish;
diff --git a/gio/gthreadedresolver.h b/gio/gthreadedresolver.h
index ea76e4bfd..099df5b84 100644
--- a/gio/gthreadedresolver.h
+++ b/gio/gthreadedresolver.h
@@ -21,28 +21,23 @@
#ifndef __G_THREADED_RESOLVER_H__
#define __G_THREADED_RESOLVER_H__
+#include <gio/gio.h>
#include <gio/gresolver.h>
G_BEGIN_DECLS
+/**
+ * GThreadedResolver:
+ *
+ * #GThreadedResolver is an implementation of #GResolver which calls the libc
+ * lookup functions in threads to allow them to run asynchronously.
+ *
+ * Since: 2.20
+ */
#define G_TYPE_THREADED_RESOLVER (g_threaded_resolver_get_type ())
-#define G_THREADED_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_THREADED_RESOLVER, GThreadedResolver))
-#define G_THREADED_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_THREADED_RESOLVER, GThreadedResolverClass))
-#define G_IS_THREADED_RESOLVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_THREADED_RESOLVER))
-#define G_IS_THREADED_RESOLVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_THREADED_RESOLVER))
-#define G_THREADED_RESOLVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_THREADED_RESOLVER, GThreadedResolverClass))
-
-typedef struct {
- GResolver parent_instance;
-} GThreadedResolver;
-
-typedef struct {
- GResolverClass parent_class;
-
-} GThreadedResolverClass;
GIO_AVAILABLE_IN_ALL
-GType g_threaded_resolver_get_type (void) G_GNUC_CONST;
+G_DECLARE_FINAL_TYPE (GThreadedResolver, g_threaded_resolver, G, THREADED_RESOLVER, GResolver)
/* Used for a private test API */
#ifdef G_OS_UNIX
diff --git a/gio/gunixconnection.c b/gio/gunixconnection.c
index b3f2b1c04..7b466cdf9 100644
--- a/gio/gunixconnection.c
+++ b/gio/gunixconnection.c
@@ -177,10 +177,11 @@ g_unix_connection_receive_fd (GUnixConnection *connection,
gint i;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- ngettext("Expecting 1 control message, got %d",
- "Expecting 1 control message, got %d",
- nscm),
- nscm);
+ g_dngettext (NULL,
+ "Expecting 1 control message, got %d",
+ "Expecting 1 control message, got %d",
+ nscm),
+ nscm);
for (i = 0; i < nscm; i++)
g_object_unref (scms[i]);
@@ -211,9 +212,10 @@ g_unix_connection_receive_fd (GUnixConnection *connection,
gint i;
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
- ngettext("Expecting one fd, but got %d\n",
- "Expecting one fd, but got %d\n",
- nfd),
+ g_dngettext (NULL,
+ "Expecting one fd, but got %d\n",
+ "Expecting one fd, but got %d\n",
+ nfd),
nfd);
for (i = 0; i < nfd; i++)
@@ -595,9 +597,10 @@ g_unix_connection_receive_credentials (GUnixConnection *connection,
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
- ngettext("Expecting 1 control message, got %d",
- "Expecting 1 control message, got %d",
- nscm),
+ g_dngettext (NULL,
+ "Expecting 1 control message, got %d",
+ "Expecting 1 control message, got %d",
+ nscm),
nscm);
goto out;
}
diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
index 9e97ef1c9..6477ee3a4 100644
--- a/gio/gunixmounts.c
+++ b/gio/gunixmounts.c
@@ -3036,7 +3036,7 @@ g_unix_mount_point_guess_can_eject (GUnixMountPoint *mount_point)
/* Utility functions {{{1 */
#ifdef HAVE_MNTENT_H
-/* borrowed from gtk/gtkfilesystemunix.c in GTK+ on 02/23/2006 */
+/* borrowed from gtk/gtkfilesystemunix.c in GTK on 02/23/2006 */
static void
_canonicalize_filename (gchar *filename)
{
diff --git a/gio/gwin32networkmonitor.c b/gio/gwin32networkmonitor.c
index 9dd5da757..e219225f0 100644
--- a/gio/gwin32networkmonitor.c
+++ b/gio/gwin32networkmonitor.c
@@ -78,24 +78,24 @@ g_win32_network_monitor_init (GWin32NetworkMonitor *win)
}
static gboolean
-win_network_monitor_get_ip_info (IP_ADDRESS_PREFIX prefix,
- GSocketFamily *family,
- const guint8 **dest,
- gsize *len)
+win_network_monitor_get_ip_info (const IP_ADDRESS_PREFIX *prefix,
+ GSocketFamily *family,
+ const guint8 **dest,
+ gsize *len)
{
- switch (prefix.Prefix.si_family)
+ switch (prefix->Prefix.si_family)
{
case AF_UNSPEC:
/* Fall-through: AF_UNSPEC deliveres both IPV4 and IPV6 infos, let`s stick with IPV4 here */
case AF_INET:
*family = G_SOCKET_FAMILY_IPV4;
- *dest = (guint8 *) &prefix.Prefix.Ipv4.sin_addr;
- *len = prefix.PrefixLength;
+ *dest = (guint8 *) &(prefix->Prefix.Ipv4.sin_addr);
+ *len = prefix->PrefixLength;
break;
case AF_INET6:
*family = G_SOCKET_FAMILY_IPV6;
- *dest = (guint8 *) &prefix.Prefix.Ipv6.sin6_addr;
- *len = prefix.PrefixLength;
+ *dest = (guint8 *) &(prefix->Prefix.Ipv6.sin6_addr);
+ *len = prefix->PrefixLength;
break;
default:
return FALSE;
@@ -152,7 +152,7 @@ win_network_monitor_process_table (GWin32NetworkMonitor *win,
route = routes->Table + i;
- if (!win_network_monitor_get_ip_info (route->DestinationPrefix, &family, &dest, &len))
+ if (!win_network_monitor_get_ip_info (&route->DestinationPrefix, &family, &dest, &len))
continue;
network = get_network_mask (family, dest, len);
@@ -218,13 +218,13 @@ win_network_monitor_invoke_route_changed (gpointer user_data)
switch (route_data->type)
{
case MibDeleteInstance:
- if (!win_network_monitor_get_ip_info (route_data->route->DestinationPrefix, &family, &dest, &len))
+ if (!win_network_monitor_get_ip_info (&route_data->route->DestinationPrefix, &family, &dest, &len))
break;
remove_network (route_data->win, family, dest, len);
break;
case MibAddInstance:
- if (!win_network_monitor_get_ip_info (route_data->route->DestinationPrefix, &family, &dest, &len))
+ if (!win_network_monitor_get_ip_info (&route_data->route->DestinationPrefix, &family, &dest, &len))
break;
add_network (route_data->win, family, dest, len);
diff --git a/gio/inotify/meson.build b/gio/inotify/meson.build
index e5c811520..8c7d6aaa0 100644
--- a/gio/inotify/meson.build
+++ b/gio/inotify/meson.build
@@ -8,9 +8,14 @@ inotify_sources = [
]
inotify_lib = static_library('inotify',
- sources : [inotify_sources, gmodule_visibility_h],
- include_directories : [configinc, glibinc, gmoduleinc],
- dependencies : [gioenumtypes_dep, libglib_dep, libgobject_dep],
+ sources : [inotify_sources],
+ include_directories : [configinc, glibinc],
+ dependencies : [
+ gioenumtypes_dep,
+ libglib_dep,
+ libgobject_dep,
+ gmodule_inc_dep,
+ ],
gnu_symbol_visibility : 'hidden',
pic : true,
c_args : [gio_c_args, gio_c_args_internal])
diff --git a/gio/kqueue/meson.build b/gio/kqueue/meson.build
index 04e48aad3..7447e5631 100644
--- a/gio/kqueue/meson.build
+++ b/gio/kqueue/meson.build
@@ -6,9 +6,12 @@ kqueue_sources = [
]
kqueue_lib = static_library('kqueue',
- sources : kqueue_sources,
- include_directories : [configinc, glibinc, gmoduleinc],
- dependencies : [gioenumtypes_dep],
+ sources : [kqueue_sources],
+ include_directories : [configinc, glibinc],
+ dependencies : [
+ gioenumtypes_dep,
+ gmodule_inc_dep,
+ ],
gnu_symbol_visibility : 'hidden',
pic : true,
c_args : [gio_c_args, gio_c_args_internal])
diff --git a/gio/meson.build b/gio/meson.build
index c9f63ea95..b19c59fa0 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -875,18 +875,20 @@ libgio_dep = declare_dependency(link_with : libgio,
pkg.generate(libgio,
requires : ['glib-2.0', 'gobject-2.0'],
- variables : ['datadir=' + join_paths('${prefix}', get_option('datadir')),
- 'schemasdir=' + join_paths('${datadir}', schemas_subdir),
- 'bindir=' + join_paths('${prefix}', get_option('bindir')),
- 'giomoduledir=' + pkgconfig_giomodulesdir,
- 'gio=' + join_paths('${bindir}', 'gio'),
- 'gio_querymodules=@0@'.format(pkgconfig_multiarch_bindir / 'gio-querymodules'),
- 'glib_compile_schemas=@0@'.format(pkgconfig_multiarch_bindir / 'glib-compile-schemas'),
- 'glib_compile_resources=' + join_paths('${bindir}', 'glib-compile-resources'),
- 'gdbus=' + join_paths('${bindir}', 'gdbus'),
- 'gdbus_codegen=' + join_paths('${bindir}', 'gdbus-codegen'),
- 'gresource=' + join_paths('${bindir}', 'gresource'),
- 'gsettings=' + join_paths('${bindir}', 'gsettings')],
+ variables : [
+ 'datadir=' + '${prefix}' / get_option('datadir'),
+ 'schemasdir=' + '${datadir}' / schemas_subdir,
+ 'bindir=' + '${prefix}' / get_option('bindir'),
+ 'giomoduledir=' + pkgconfig_giomodulesdir,
+ 'gio=' + '${bindir}' / 'gio',
+ 'gio_querymodules=' + pkgconfig_multiarch_bindir / 'gio-querymodules',
+ 'glib_compile_schemas=' + pkgconfig_multiarch_bindir / 'glib-compile-schemas',
+ 'glib_compile_resources=' + '${bindir}' / 'glib-compile-resources',
+ 'gdbus=' + '${bindir}' /'gdbus',
+ 'gdbus_codegen=' + '${bindir}' / 'gdbus-codegen',
+ 'gresource=' + '${bindir}' / 'gresource',
+ 'gsettings=' + '${bindir}' / 'gsettings',
+ ],
version : glib_version,
install_dir : glib_pkgconfigreldir,
filebase : 'gio-2.0',
diff --git a/gio/tests/appmonitor.c b/gio/tests/appmonitor.c
index c1d68b889..0123ea59c 100644
--- a/gio/tests/appmonitor.c
+++ b/gio/tests/appmonitor.c
@@ -22,7 +22,7 @@
#include <gio/gio.h>
#include <gstdio.h>
-#ifdef G_OS_UNIX
+#if defined (G_OS_UNIX) && !defined (__APPLE__)
#include <gio/gdesktopappinfo.h>
#endif
@@ -49,7 +49,7 @@ teardown (Fixture *fixture,
g_clear_pointer (&fixture->applications_dir, g_free);
}
-#ifdef G_OS_UNIX
+#if defined (G_OS_UNIX) && !defined (__APPLE__)
static gboolean
create_app (gpointer data)
{
@@ -97,13 +97,13 @@ timeout_cb (gpointer user_data)
return G_SOURCE_REMOVE;
}
-#endif /* G_OS_UNIX */
+#endif /* defined (G_OS_UNIX) && !defined (__APPLE__) */
static void
test_app_monitor (Fixture *fixture,
gconstpointer user_data)
{
-#ifdef G_OS_UNIX
+#if defined (G_OS_UNIX) && !defined (__APPLE__)
gchar *app_path;
GAppInfoMonitor *monitor;
GMainContext *context = NULL; /* use the global default main context */
@@ -162,9 +162,11 @@ test_app_monitor (Fixture *fixture,
g_object_unref (monitor);
g_free (app_path);
-#else /* if !G_OS_UNIX */
+#elif defined (__APPLE__)
+ g_test_skip (".desktop monitor on macos");
+#else /* if !(defined (G_OS_UNIX) && !defined (__APPLE__)) */
g_test_skip (".desktop monitor on win32");
-#endif /* !G_OS_UNIX */
+#endif /* !(defined (G_OS_UNIX) && !defined (__APPLE__)) */
}
int
diff --git a/gio/tests/desktop-files/usr/applications/glade.desktop b/gio/tests/desktop-files/usr/applications/glade.desktop
index dcede22a1..987f8dc0f 100644
--- a/gio/tests/desktop-files/usr/applications/glade.desktop
+++ b/gio/tests/desktop-files/usr/applications/glade.desktop
@@ -27,15 +27,15 @@ X-GNOME-FullName[en_GB]=Glade Interface Designer
X-GNOME-FullName[eo]=Uzantointerfaca dizajnilo Glado
X-GNOME-FullName[pt]=Designer de Interfaces Glade
X-GNOME-FullName[pt_BR]=Construtor de interfaces Glade
-Comment=Create or open user interface designs for GTK+ applications
-Comment[ca]=Creeu o obriu dissenys d'interfície d'usuari per a aplicacions GTK+
-Comment[ca@valencia]=Creeu o obriu dissenys d'interfície d'usuari per a aplicacions GTK+
-Comment[en@shaw]=𐑒𐑮𐑦𐑱𐑑 𐑹 𐑴𐑐𐑩𐑯 𐑿𐑟𐑼 𐑦𐑯𐑑𐑼𐑓𐑱𐑕 𐑛𐑩𐑟𐑲𐑯𐑟 𐑓𐑹 GTK+ 𐑩𐑐𐑤𐑦𐑒𐑱𐑕𐑩𐑯𐑟
-Comment[en_CA]=Create or open user interface designs for GTK+ applications
-Comment[en_GB]=Create or open user interface designs for GTK+ applications
-Comment[eo]=Krei aŭ malfermi uzantointerfacan dizajnon por aplikaĵoj de GTK+
-Comment[pt]=Criar ou abrir um desenho de interface de utilizador para aplicações GTK+
-Comment[pt_BR]=Crie ou abra projetos de interface com o usuário para aplicativos GTK+
+Comment=Create or open user interface designs for GTK applications
+Comment[ca]=Creeu o obriu dissenys d'interfície d'usuari per a aplicacions GTK
+Comment[ca@valencia]=Creeu o obriu dissenys d'interfície d'usuari per a aplicacions GTK
+Comment[en@shaw]=𐑒𐑮𐑦𐑱𐑑 𐑹 𐑴𐑐𐑩𐑯 𐑿𐑟𐑼 𐑦𐑯𐑑𐑼𐑓𐑱𐑕 𐑛𐑩𐑟𐑲𐑯𐑟 𐑓𐑹 GTK 𐑩𐑐𐑤𐑦𐑒𐑱𐑕𐑩𐑯𐑟
+Comment[en_CA]=Create or open user interface designs for GTK applications
+Comment[en_GB]=Create or open user interface designs for GTK applications
+Comment[eo]=Krei aŭ malfermi uzantointerfacan dizajnon por aplikaĵoj de GTK
+Comment[pt]=Criar ou abrir um desenho de interface de utilizador para aplicações GTK
+Comment[pt_BR]=Crie ou abra projetos de interface com o usuário para aplicativos GTK
Keywords=GUI designer;user interface;ui builder;
Keywords[ca]=dissenyador d'interfícies d'usuari gràfiques;interfície d'usuari;constructor d'interfícies d'usuari;
Keywords[ca@valencia]=dissenyador d'interfícies d'usuari gràfiques;interfície d'usuari;constructor d'interfícies d'usuari;
diff --git a/gio/tests/file.c b/gio/tests/file.c
index ad2f945f9..5ca2e678d 100644
--- a/gio/tests/file.c
+++ b/gio/tests/file.c
@@ -452,15 +452,13 @@ created_cb (GObject *source,
data);
}
-static gboolean
+static void
stop_timeout (gpointer user_data)
{
CreateDeleteData *data = user_data;
data->timed_out = TRUE;
g_main_context_wakeup (data->context);
-
- return G_SOURCE_REMOVE;
}
/*
@@ -518,7 +516,7 @@ test_create_delete (gconstpointer d)
/* Use the global default main context */
data->context = NULL;
- data->timeout = g_timeout_add_seconds (10, stop_timeout, data);
+ data->timeout = g_timeout_add_seconds_once (10, stop_timeout, data);
g_file_create_async (data->file, 0, 0, NULL, created_cb, data);
@@ -2515,6 +2513,80 @@ test_copy_preserve_mode (void)
#endif
}
+typedef struct
+{
+ goffset current_num_bytes;
+ goffset total_num_bytes;
+} CopyProgressData;
+
+static void
+file_copy_progress_cb (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ CopyProgressData *prev_data = user_data;
+
+ g_assert_cmpuint (total_num_bytes, ==, prev_data->total_num_bytes);
+ g_assert_cmpuint (current_num_bytes, >=, prev_data->current_num_bytes);
+
+ /* Update it for the next callback. */
+ prev_data->current_num_bytes = current_num_bytes;
+}
+
+static void
+test_copy_progress (void)
+{
+ GFile *src_tmpfile = NULL;
+ GFile *dest_tmpfile = NULL;
+ GFileIOStream *iostream;
+ GOutputStream *ostream;
+ GError *local_error = NULL;
+ const guint8 buffer[] = { 1, 2, 3, 4, 5 };
+ CopyProgressData progress_data;
+
+ src_tmpfile = g_file_new_tmp ("tmp-copy-progressXXXXXX",
+ &iostream, &local_error);
+ g_assert_no_error (local_error);
+
+ /* Write some content to the file for testing. */
+ ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
+ g_output_stream_write (ostream, buffer, sizeof (buffer), NULL, &local_error);
+ g_assert_no_error (local_error);
+
+ g_io_stream_close ((GIOStream *) iostream, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_clear_object (&iostream);
+
+ /* Grab a unique destination filename. */
+ dest_tmpfile = g_file_new_tmp ("tmp-copy-progressXXXXXX",
+ &iostream, &local_error);
+ g_assert_no_error (local_error);
+ g_io_stream_close ((GIOStream *) iostream, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_clear_object (&iostream);
+
+ /* Set the progress data to an initial offset of zero. The callback will
+ * assert that progress is non-decreasing and reaches the total length of
+ * the file. */
+ progress_data.current_num_bytes = 0;
+ progress_data.total_num_bytes = sizeof (buffer);
+
+ /* Copy the file with progress reporting. */
+ g_file_copy (src_tmpfile, dest_tmpfile, G_FILE_COPY_OVERWRITE,
+ NULL, file_copy_progress_cb, &progress_data, &local_error);
+ g_assert_no_error (local_error);
+
+ g_assert_cmpuint (progress_data.current_num_bytes, ==, progress_data.total_num_bytes);
+ g_assert_cmpuint (progress_data.total_num_bytes, ==, sizeof (buffer));
+
+ /* Clean up. */
+ (void) g_file_delete (src_tmpfile, NULL, NULL);
+ (void) g_file_delete (dest_tmpfile, NULL, NULL);
+
+ g_clear_object (&src_tmpfile);
+ g_clear_object (&dest_tmpfile);
+}
+
static void
test_measure (void)
{
@@ -2541,7 +2613,7 @@ test_measure (void)
g_assert_true (ok);
g_assert_no_error (error);
- g_assert_cmpuint (num_bytes, ==, 74478);
+ g_assert_cmpuint (num_bytes, ==, 74469);
g_assert_cmpuint (num_dirs, ==, 6);
g_assert_cmpuint (num_files, ==, 32);
@@ -2624,7 +2696,7 @@ test_measure_async (void)
file = g_file_new_for_path (path);
g_free (path);
- data->expected_bytes = 74478;
+ data->expected_bytes = 74469;
data->expected_dirs = 6;
data->expected_files = 32;
@@ -3846,6 +3918,7 @@ main (int argc, char *argv[])
g_test_add_func ("/file/async-delete", test_async_delete);
g_test_add_func ("/file/async-make-symlink", test_async_make_symlink);
g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
+ g_test_add_func ("/file/copy/progress", test_copy_progress);
g_test_add_func ("/file/measure", test_measure);
g_test_add_func ("/file/measure-async", test_measure_async);
g_test_add_func ("/file/load-bytes", test_load_bytes);
@@ -3871,3 +3944,4 @@ main (int argc, char *argv[])
return g_test_run ();
}
+
diff --git a/gio/tests/gapplication.c b/gio/tests/gapplication.c
index 1a2600099..b0584eb5f 100644
--- a/gio/tests/gapplication.c
+++ b/gio/tests/gapplication.c
@@ -1117,15 +1117,13 @@ activate (gpointer data)
/* GApplication complains if we don't connect to ::activate */
}
-static gboolean
+static void
quit_already (gpointer user_data)
{
TestReplaceData *data = user_data;
g_application_quit (data->app);
data->timeout_id = 0;
-
- return G_SOURCE_REMOVE;
}
static void
@@ -1177,7 +1175,7 @@ test_replace (gconstpointer data)
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
if (!allow)
- data.timeout_id = g_timeout_add_seconds (1, quit_already, &data);
+ data.timeout_id = g_timeout_add_seconds_once (1, quit_already, &data);
g_application_run (app, G_N_ELEMENTS (argv) - 1, argv);
diff --git a/gio/tests/gdbus-connection-slow.c b/gio/tests/gdbus-connection-slow.c
index 5a3479234..06f59493c 100644
--- a/gio/tests/gdbus-connection-slow.c
+++ b/gio/tests/gdbus-connection-slow.c
@@ -128,14 +128,12 @@ test_connection_flush (void)
/* the test will fail if the service name has not appeared after this amount of seconds */
#define LARGE_MESSAGE_TIMEOUT_SECONDS 10
-static gboolean
+static void
large_message_timeout_cb (gpointer data)
{
(void)data;
g_error ("Error: timeout waiting for dbus name to appear");
-
- return G_SOURCE_REMOVE;
}
static void
@@ -200,9 +198,9 @@ test_connection_large_message (void)
/* this is safe; testserver will exit once the bus goes away */
g_assert (g_spawn_command_line_async (g_test_get_filename (G_TEST_BUILT, "gdbus-testserver", NULL), NULL));
- timeout_id = g_timeout_add_seconds (LARGE_MESSAGE_TIMEOUT_SECONDS,
- large_message_timeout_cb,
- NULL);
+ timeout_id = g_timeout_add_seconds_once (LARGE_MESSAGE_TIMEOUT_SECONDS,
+ large_message_timeout_cb,
+ NULL);
watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
"com.example.TestService",
diff --git a/gio/tests/gmenumodel.c b/gio/tests/gmenumodel.c
index fb52b4c6d..d75f36a29 100644
--- a/gio/tests/gmenumodel.c
+++ b/gio/tests/gmenumodel.c
@@ -7,12 +7,10 @@
#include "glib/glib-private.h"
-static gboolean
+static void
time_out (gpointer unused G_GNUC_UNUSED)
{
g_error ("Timed out");
- /* not reached */
- return FALSE;
}
static guint
@@ -22,7 +20,7 @@ add_timeout (guint seconds)
/* Safety-catch against the main loop having blocked */
alarm (seconds + 5);
#endif
- return g_timeout_add_seconds (seconds, time_out, NULL);
+ return g_timeout_add_seconds_once (seconds, time_out, NULL);
}
static void
diff --git a/gio/tests/gsubprocess.c b/gio/tests/gsubprocess.c
index 515a11267..30947596e 100644
--- a/gio/tests/gsubprocess.c
+++ b/gio/tests/gsubprocess.c
@@ -1288,14 +1288,12 @@ test_communicate_utf8_invalid (void)
g_object_unref (proc);
}
-static gboolean
+static void
send_terminate (gpointer user_data)
{
GSubprocess *proc = user_data;
g_subprocess_force_exit (proc);
-
- return FALSE;
}
static void
@@ -1341,7 +1339,7 @@ test_terminate (void)
g_subprocess_wait_async (proc, NULL, on_request_quit_exited, loop);
- g_timeout_add_seconds (3, send_terminate, proc);
+ g_timeout_add_seconds_once (3, send_terminate, proc);
g_main_loop_run (loop);
@@ -1350,14 +1348,12 @@ test_terminate (void)
}
#ifdef G_OS_UNIX
-static gboolean
+static void
send_signal (gpointer user_data)
{
GSubprocess *proc = user_data;
g_subprocess_send_signal (proc, SIGKILL);
-
- return FALSE;
}
static void
@@ -1378,7 +1374,7 @@ test_signal (void)
g_subprocess_wait_async (proc, NULL, on_request_quit_exited, loop);
- g_timeout_add_seconds (3, send_signal, proc);
+ g_timeout_add_seconds_once (3, send_signal, proc);
g_main_loop_run (loop);
diff --git a/gio/tests/meson.build b/gio/tests/meson.build
index 2f312e056..2ff34a5d0 100644
--- a/gio/tests/meson.build
+++ b/gio/tests/meson.build
@@ -590,8 +590,8 @@ test_extra_programs += {
'proxy' : {'install' : false},
'resolver' : {'install' : false},
'send-data' : {'install' : false},
- 'socket-server' : {'install' : false},
- 'socket-client' : {
+ 'socket-testserver' : {'install' : false},
+ 'socket-testclient' : {
'extra_sources' : ['gtlsconsoleinteraction.c'],
'install' : false,
},
@@ -1096,4 +1096,72 @@ if installed_tests_enabled
)
endif
+if have_bash and have_pkg_config
+ prefix = get_option('prefix')
+ if prefix.endswith(':/')
+ prefix += '/'
+ endif
+
+ pkg_config_tests = [
+ 'pkg-config --validate gio-2.0',
+ 'test "$(pkg-config --modversion gio-2.0)" = "@0@"'.format(glib_version),
+ 'test "$(pkg-config --variable=prefix gio-2.0)" = "@0@"'.format(
+ get_option('prefix')),
+ 'test "$(pkg-config --variable=datadir gio-2.0)" = "@0@"'.format(
+ prefix / get_option('datadir')),
+ 'test "$(pkg-config --variable=schemasdir gio-2.0)" = "@0@"'.format(
+ prefix / get_option('datadir') / schemas_subdir),
+ 'test "$(pkg-config --variable=giomoduledir gio-2.0)" = "@0@"'.format(
+ get_option('gio_module_dir') != '' ?
+ prefix / get_option('gio_module_dir') :
+ prefix / get_option('libdir') / 'gio' / 'modules'),
+ ]
+
+ gio_binaries = [
+ 'gio',
+ 'gio-querymodules',
+ 'glib-compile-schemas',
+ 'glib-compile-resources',
+ 'gdbus',
+ 'gdbus-codegen',
+ 'gresource',
+ 'gsettings',
+ ]
+
+ foreach binary: gio_binaries
+ pkg_config_tests += [
+ 'test "$(pkg-config --variable=@0@ gio-2.0)" = "@1@"'.format(
+ binary.underscorify(),
+ prefix / get_option('bindir') / binary)
+ ]
+ endforeach
+
+ test('gio-2.0-pkg-config',
+ bash,
+ args: [ '-xe', '-c', '\n'.join(pkg_config_tests) ],
+ suite: ['gio', 'no-valgrind', 'pkg-config'],
+ env: {
+ 'PKG_CONFIG_PATH': meson.project_build_root() / 'meson-private',
+ },
+ )
+
+ platform_module = host_system == 'windows' ? 'gio-windows-2.0' : 'gio-unix-2.0'
+ pkg_config_tests = [
+ 'pkg-config --validate ' + platform_module,
+ 'test "$(pkg-config --modversion @0@)" = "@1@"'.format(platform_module,
+ glib_version),
+ 'test "$(pkg-config --variable=prefix @0@)" = "@1@"'.format(platform_module,
+ get_option('prefix')),
+ ]
+
+ test(platform_module + '-pkg-config',
+ bash,
+ args: [ '-xe', '-c', '\n'.join(pkg_config_tests) ],
+ suite: ['gio', 'no-valgrind', 'pkg-config'],
+ env: {
+ 'PKG_CONFIG_PATH': meson.project_build_root() / 'meson-private',
+ },
+ )
+endif
+
subdir('services')
diff --git a/gio/tests/resolver.c b/gio/tests/resolver.c
index ec9b9e9de..b8adb4a36 100644
--- a/gio/tests/resolver.c
+++ b/gio/tests/resolver.c
@@ -41,6 +41,7 @@ static int nlookups = 0;
static gboolean synchronous = FALSE;
static guint connectable_count = 0;
static GResolverRecordType record_type = 0;
+static gint timeout_ms = 0;
static G_NORETURN void
usage (void)
@@ -688,7 +689,7 @@ static gboolean
async_cancel (GIOChannel *source, GIOCondition cond, gpointer cancel)
{
g_cancellable_cancel (cancel);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
#endif
@@ -722,6 +723,7 @@ static const GOptionEntry option_entries[] = {
{ "synchronous", 's', 0, G_OPTION_ARG_NONE, &synchronous, "Synchronous connections", NULL },
{ "connectable", 'c', 0, G_OPTION_ARG_INT, &connectable_count, "Connectable count", "C" },
{ "special-type", 't', 0, G_OPTION_ARG_CALLBACK, record_type_arg, "Record type like MX, TXT, NS or SOA", "RR" },
+ { "timeout", 0, 0, G_OPTION_ARG_INT, &timeout_ms, "Timeout (ms)", "ms" },
G_OPTION_ENTRY_NULL,
};
@@ -732,7 +734,7 @@ main (int argc, char **argv)
GError *error = NULL;
#ifdef G_OS_UNIX
GIOChannel *chan;
- guint watch;
+ GSource *watch_source = NULL;
#endif
context = g_option_context_new ("lookups ...");
@@ -749,6 +751,9 @@ main (int argc, char **argv)
resolver = g_resolver_get_default ();
+ if (timeout_ms != 0)
+ g_resolver_set_timeout (resolver, timeout_ms);
+
cancellable = g_cancellable_new ();
#ifdef G_OS_UNIX
@@ -763,7 +768,9 @@ main (int argc, char **argv)
exit (1);
}
chan = g_io_channel_unix_new (cancel_fds[0]);
- watch = g_io_add_watch (chan, G_IO_IN, async_cancel, cancellable);
+ watch_source = g_io_create_watch (chan, G_IO_IN);
+ g_source_set_callback (watch_source, (GSourceFunc) async_cancel, cancellable, NULL);
+ g_source_attach (watch_source, NULL);
g_io_channel_unref (chan);
#endif
@@ -787,7 +794,8 @@ main (int argc, char **argv)
g_main_loop_unref (loop);
#ifdef G_OS_UNIX
- g_source_remove (watch);
+ g_source_destroy (watch_source);
+ g_clear_pointer (&watch_source, g_source_unref);
#endif
g_object_unref (cancellable);
g_option_context_free (context);
diff --git a/gio/tests/socket-common.c b/gio/tests/socket-common.c
index 18d083a30..b740f68e7 100644
--- a/gio/tests/socket-common.c
+++ b/gio/tests/socket-common.c
@@ -1,4 +1,4 @@
-/* #included into both socket-client.c and socket-server.c */
+/* #included into both socket-testclient.c and socket-testserver.c */
#ifdef G_OS_UNIX
static const char *unix_socket_address_types[] = {
diff --git a/gio/tests/socket-client.c b/gio/tests/socket-testclient.c
index 025632767..025632767 100644
--- a/gio/tests/socket-client.c
+++ b/gio/tests/socket-testclient.c
diff --git a/gio/tests/socket-server.c b/gio/tests/socket-testserver.c
index 61715b02d..61715b02d 100644
--- a/gio/tests/socket-server.c
+++ b/gio/tests/socket-testserver.c
diff --git a/gio/win32/meson.build b/gio/win32/meson.build
index 0a896aca0..08be6b09e 100644
--- a/gio/win32/meson.build
+++ b/gio/win32/meson.build
@@ -9,8 +9,12 @@ giowin32_sources = [
giowin32_lib = static_library('giowin32',
sources : [giowin32_sources],
- include_directories : [configinc, glibinc, gioinc, gmoduleinc],
- dependencies : [libintl, gioenumtypes_dep],
+ include_directories : [configinc, glibinc, gioinc],
+ dependencies : [
+ libintl,
+ gioenumtypes_dep,
+ gmodule_inc_dep,
+ ],
gnu_symbol_visibility : 'hidden',
pic : true,
c_args : [gio_c_args, gio_c_args_internal])