diff options
Diffstat (limited to 'gio')
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, + ©_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 (©_file_range_error)); + goto out; + } + else + { + g_clear_error (©_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]) |