Imported Upstream version 2.66.3 upstream/2.66.3
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 29 Oct 2021 01:26:38 +0000 (10:26 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 29 Oct 2021 01:26:38 +0000 (10:26 +0900)
NEWS
gio/gdbusprivate.c
gio/glocalfileinfo.c
gio/tests/gdbus-peer.c
gio/tests/gsocketclient-slow.c
glib/gmain.c
glib/gtrace-private.h
glib/tests/mainloop.c
meson.build

diff --git a/NEWS b/NEWS
index b8903bd..45692a2 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,21 @@
+Overview of changes in GLib 2.66.3
+==================================
+
+* Fix awkward bug with `GPollFD` handling in some situations (work by Claudio
+  Saavedra and Eugene M) (#1592)
+
+* Fix sending FDs attached to very large D-Bus messages (work by Simon McVittie
+  and Giovanni Campagna) (#2074)
+
+* Bugs fixed:
+ - #1592 Main loop ignores GPollFD sources when there is at least one source ready with priority higher than default one
+ - !1720 Backport !1718 “gtrace: Add G_GNUC_PRINTF annotation” to glib-2-66
+ - !1721 Backport !1713 “gmain: g_main_context_check() can skip updating polled FD sources” to glib-2-66
+ - !1723 Backport !1711 “Fix race in socketclient-slow test” to glib-2-66
+ - !1727 Backport !1725 “gdbus: Cope with sending fds in a message that takes multiple writes” to glib-2-66
+ - !1736 Backport !1734 “glocalfileinfo: Use a single timeout source at a time for hidden file cache” to glib-2-66
+
+
 Overview of changes in GLib 2.66.2
 ==================================
 
index 5c980b4..2551e47 100644 (file)
@@ -1085,8 +1085,11 @@ write_message_continue_writing (MessageToWriteData *data)
   else
     {
 #ifdef G_OS_UNIX
-      if (fd_list != NULL)
+      if (data->total_written == 0 && fd_list != NULL)
         {
+          /* We were trying to write byte 0 of the message, which needs
+           * the fd list to be attached to it, but this connection doesn't
+           * support doing that. */
           g_task_return_new_error (task,
                                    G_IO_ERROR,
                                    G_IO_ERROR_FAILED,
index 90fcb33..a4abef0 100644 (file)
@@ -1547,15 +1547,45 @@ win32_get_file_user_info (const gchar  *filename,
 /* support for '.hidden' files */
 G_LOCK_DEFINE_STATIC (hidden_cache);
 static GHashTable *hidden_cache;
+static GSource *hidden_cache_source = NULL; /* Under the hidden_cache lock */
+static guint hidden_cache_ttl_secs = 5;
+static guint hidden_cache_ttl_jitter_secs = 2;
+
+typedef struct
+{
+  GHashTable *hidden_files;
+  gint64 timestamp_secs;
+} HiddenCacheData;
 
 static gboolean
 remove_from_hidden_cache (gpointer user_data)
 {
+  HiddenCacheData *data;
+  GHashTableIter iter;
+  gboolean retval;
+  gint64 timestamp_secs;
+
   G_LOCK (hidden_cache);
-  g_hash_table_remove (hidden_cache, user_data);
+  timestamp_secs = g_source_get_time (hidden_cache_source) / G_USEC_PER_SEC;
+
+  g_hash_table_iter_init (&iter, hidden_cache);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &data))
+    {
+      if (timestamp_secs > data->timestamp_secs + hidden_cache_ttl_secs)
+        g_hash_table_iter_remove (&iter);
+    }
+
+  if (g_hash_table_size (hidden_cache) == 0)
+    {
+      g_clear_pointer (&hidden_cache_source, g_source_unref);
+      retval = G_SOURCE_REMOVE;
+    }
+  else
+    retval = G_SOURCE_CONTINUE;
+
   G_UNLOCK (hidden_cache);
 
-  return FALSE;
+  return retval;
 }
 
 static GHashTable *
@@ -1593,16 +1623,19 @@ read_hidden_file (const gchar *dirname)
 }
 
 static void
-maybe_unref_hash_table (gpointer data)
+free_hidden_file_data (gpointer user_data)
 {
-  if (data != NULL)
-    g_hash_table_unref (data);
+  HiddenCacheData *data = user_data;
+
+  g_clear_pointer (&data->hidden_files, g_hash_table_unref);
+  g_free (data);
 }
 
 static gboolean
 file_is_hidden (const gchar *path,
                 const gchar *basename)
 {
+  HiddenCacheData *data;
   gboolean result;
   gchar *dirname;
   gpointer table;
@@ -1613,28 +1646,38 @@ file_is_hidden (const gchar *path,
 
   if G_UNLIKELY (hidden_cache == NULL)
     hidden_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                          g_free, maybe_unref_hash_table);
+                                          g_free, free_hidden_file_data);
 
   if (!g_hash_table_lookup_extended (hidden_cache, dirname,
-                                     NULL, &table))
+                                     NULL, (gpointer *) &data))
     {
       gchar *mydirname;
-      GSource *remove_from_cache_source;
+
+      data = g_new0 (HiddenCacheData, 1);
+      data->hidden_files = table = read_hidden_file (dirname);
+      data->timestamp_secs = g_get_monotonic_time () / G_USEC_PER_SEC;
 
       g_hash_table_insert (hidden_cache,
                            mydirname = g_strdup (dirname),
-                           table = read_hidden_file (dirname));
+                           data);
 
-      remove_from_cache_source = g_timeout_source_new_seconds (5);
-      g_source_set_priority (remove_from_cache_source, G_PRIORITY_DEFAULT);
-      g_source_set_callback (remove_from_cache_source, 
-                             remove_from_hidden_cache, 
-                             mydirname, 
-                             NULL);
-      g_source_attach (remove_from_cache_source, 
-                       GLIB_PRIVATE_CALL (g_get_worker_context) ());
-      g_source_unref (remove_from_cache_source);
+      if (!hidden_cache_source)
+        {
+          hidden_cache_source =
+            g_timeout_source_new_seconds (hidden_cache_ttl_secs +
+                                          hidden_cache_ttl_jitter_secs);
+          g_source_set_priority (hidden_cache_source, G_PRIORITY_DEFAULT);
+          g_source_set_name (hidden_cache_source,
+                             "[gio] remove_from_hidden_cache");
+          g_source_set_callback (hidden_cache_source,
+                                 remove_from_hidden_cache,
+                                 NULL, NULL);
+          g_source_attach (hidden_cache_source,
+                           GLIB_PRIVATE_CALL (g_get_worker_context) ());
+        }
     }
+  else
+    table = data->hidden_files;
 
   result = table != NULL && g_hash_table_contains (table, basename);
 
index 617d756..8450a3b 100644 (file)
@@ -76,6 +76,12 @@ typedef struct
   gboolean signal_received;
 } PeerData;
 
+/* This needs to be enough to usually take more than one write(),
+ * to reproduce
+ * <https://gitlab.gnome.org/GNOME/glib/-/issues/2074>.
+ * 1 MiB ought to be enough. */
+#define BIG_MESSAGE_ARRAY_SIZE (1024 * 1024)
+
 static const gchar *test_interface_introspection_xml =
   "<node>"
   "  <interface name='org.gtk.GDBus.PeerTestInterface'>"
@@ -88,6 +94,11 @@ static const gchar *test_interface_introspection_xml =
   "    <method name='OpenFile'>"
   "      <arg type='s' name='path' direction='in'/>"
   "    </method>"
+  "    <method name='OpenFileWithBigMessage'>"
+  "      <arg type='s' name='path' direction='in'/>"
+  "      <arg type='h' name='handle' direction='out'/>"
+  "      <arg type='ay' name='junk' direction='out'/>"
+  "    </method>"
   "    <signal name='PeerSignal'>"
   "      <arg type='s' name='a_string'/>"
   "    </signal>"
@@ -164,7 +175,8 @@ test_interface_method_call (GDBusConnection       *connection,
 
       g_dbus_method_invocation_return_value (invocation, NULL);
     }
-  else if (g_strcmp0 (method_name, "OpenFile") == 0)
+  else if (g_strcmp0 (method_name, "OpenFile") == 0 ||
+           g_strcmp0 (method_name, "OpenFileWithBigMessage") == 0)
     {
 #ifdef G_OS_UNIX
       const gchar *path;
@@ -190,6 +202,21 @@ test_interface_method_call (GDBusConnection       *connection,
       g_object_unref (fd_list);
       g_object_unref (invocation);
 
+      if (g_strcmp0 (method_name, "OpenFileWithBigMessage") == 0)
+        {
+          char *junk;
+
+          junk = g_new0 (char, BIG_MESSAGE_ARRAY_SIZE);
+          g_dbus_message_set_body (reply,
+                                   g_variant_new ("(h@ay)",
+                                                  0,
+                                                  g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
+                                                                             junk,
+                                                                             BIG_MESSAGE_ARRAY_SIZE,
+                                                                             1)));
+          g_free (junk);
+        }
+
       error = NULL;
       g_dbus_connection_send_message (connection,
                                       reply,
@@ -723,6 +750,7 @@ do_test_peer (void)
   const gchar *s;
   GThread *service_thread;
   gulong signal_handler_id;
+  gsize i;
 
   memset (&data, '\0', sizeof (PeerData));
   data.current_connections = g_ptr_array_new_with_free_func (g_object_unref);
@@ -843,73 +871,116 @@ do_test_peer (void)
   g_assert_cmpint (data.num_method_calls, ==, 3);
   g_signal_handler_disconnect (proxy, signal_handler_id);
 
-  /* check for UNIX fd passing */
+  /*
+   * Check for UNIX fd passing.
+   *
+   * The first time through, we use a very simple method call. Note that
+   * because this does not have a G_VARIANT_TYPE_HANDLE in the message body
+   * to refer to the fd, it is a GDBus-specific idiom that would not
+   * interoperate with libdbus or sd-bus
+   * (see <https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1726>).
+   *
+   * The second time, we call a method that returns a fd attached to a
+   * large message, to reproduce
+   * <https://gitlab.gnome.org/GNOME/glib/-/issues/2074>. It also happens
+   * to follow the more usual pattern for D-Bus messages containing a
+   * G_VARIANT_TYPE_HANDLE to refer to attached fds.
+   */
+  for (i = 0; i < 2; i++)
+    {
 #ifdef G_OS_UNIX
-  {
-    GDBusMessage *method_call_message;
-    GDBusMessage *method_reply_message;
-    GUnixFDList *fd_list;
-    gint fd;
-    gchar *buf;
-    gsize len;
-    gchar *buf2;
-    gsize len2;
-    const char *testfile = g_test_get_filename (G_TEST_DIST, "file.c", NULL);
-
-    method_call_message = g_dbus_message_new_method_call (NULL, /* name */
-                                                          "/org/gtk/GDBus/PeerTestObject",
-                                                          "org.gtk.GDBus.PeerTestInterface",
-                                                          "OpenFile");
-    g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", testfile));
-    error = NULL;
-    method_reply_message = g_dbus_connection_send_message_with_reply_sync (c,
-                                                                           method_call_message,
-                                                                           G_DBUS_SEND_MESSAGE_FLAGS_NONE,
-                                                                           -1,
-                                                                           NULL, /* out_serial */
-                                                                           NULL, /* cancellable */
-                                                                           &error);
-    g_assert_no_error (error);
-    g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN);
-    fd_list = g_dbus_message_get_unix_fd_list (method_reply_message);
-    g_assert (fd_list != NULL);
-    g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1);
-    error = NULL;
-    fd = g_unix_fd_list_get (fd_list, 0, &error);
-    g_assert_no_error (error);
-    g_object_unref (method_call_message);
-    g_object_unref (method_reply_message);
+      GDBusMessage *method_call_message;
+      GDBusMessage *method_reply_message;
+      GUnixFDList *fd_list;
+      gint fd;
+      gchar *buf;
+      gsize len;
+      gchar *buf2;
+      gsize len2;
+      const char *testfile = g_test_get_filename (G_TEST_DIST, "file.c", NULL);
+      const char *method = "OpenFile";
+      GVariant *body;
+
+      if (i == 1)
+        method = "OpenFileWithBigMessage";
+
+      method_call_message = g_dbus_message_new_method_call (NULL, /* name */
+                                                            "/org/gtk/GDBus/PeerTestObject",
+                                                            "org.gtk.GDBus.PeerTestInterface",
+                                                            method);
+      g_dbus_message_set_body (method_call_message, g_variant_new ("(s)", testfile));
+      error = NULL;
+      method_reply_message = g_dbus_connection_send_message_with_reply_sync (c,
+                                                                             method_call_message,
+                                                                             G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                                                             -1,
+                                                                             NULL, /* out_serial */
+                                                                             NULL, /* cancellable */
+                                                                             &error);
+      g_assert_no_error (error);
+      g_assert (g_dbus_message_get_message_type (method_reply_message) == G_DBUS_MESSAGE_TYPE_METHOD_RETURN);
 
-    error = NULL;
-    len = 0;
-    buf = read_all_from_fd (fd, &len, &error);
-    g_assert_no_error (error);
-    g_assert (buf != NULL);
-    close (fd);
+      body = g_dbus_message_get_body (method_reply_message);
 
-    error = NULL;
-    g_file_get_contents (testfile,
-                         &buf2,
-                         &len2,
-                         &error);
-    g_assert_no_error (error);
-    g_assert_cmpmem (buf, len, buf2, len2);
-    g_free (buf2);
-    g_free (buf);
-  }
+      if (i == 1)
+        {
+          gint32 handle = -1;
+          GVariant *junk = NULL;
+
+          g_assert_cmpstr (g_variant_get_type_string (body), ==, "(hay)");
+          g_variant_get (body, "(h@ay)", &handle, &junk);
+          g_assert_cmpint (handle, ==, 0);
+          g_assert_cmpuint (g_variant_n_children (junk), ==, BIG_MESSAGE_ARRAY_SIZE);
+          g_variant_unref (junk);
+        }
+      else
+        {
+          g_assert_null (body);
+        }
+
+      fd_list = g_dbus_message_get_unix_fd_list (method_reply_message);
+      g_assert (fd_list != NULL);
+      g_assert_cmpint (g_unix_fd_list_get_length (fd_list), ==, 1);
+      error = NULL;
+      fd = g_unix_fd_list_get (fd_list, 0, &error);
+      g_assert_no_error (error);
+      g_object_unref (method_call_message);
+      g_object_unref (method_reply_message);
+
+      error = NULL;
+      len = 0;
+      buf = read_all_from_fd (fd, &len, &error);
+      g_assert_no_error (error);
+      g_assert (buf != NULL);
+      close (fd);
+
+      error = NULL;
+      g_file_get_contents (testfile,
+                           &buf2,
+                           &len2,
+                           &error);
+      g_assert_no_error (error);
+      g_assert_cmpmem (buf, len, buf2, len2);
+      g_free (buf2);
+      g_free (buf);
 #else
-  error = NULL;
-  result = g_dbus_proxy_call_sync (proxy,
-                                   "OpenFile",
-                                   g_variant_new ("(s)", "boo"),
-                                   G_DBUS_CALL_FLAGS_NONE,
-                                   -1,
-                                   NULL,  /* GCancellable */
-                                   &error);
-  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
-  g_assert (result == NULL);
-  g_error_free (error);
+      /* We do the same number of iterations on non-Unix, so that
+       * the method call count will match. In this case we use
+       * OpenFile both times, because the difference between this
+       * and OpenFileWithBigMessage is only relevant on Unix. */
+      error = NULL;
+      result = g_dbus_proxy_call_sync (proxy,
+                                       "OpenFile",
+                                       g_variant_new ("(s)", "boo"),
+                                       G_DBUS_CALL_FLAGS_NONE,
+                                       -1,
+                                       NULL,  /* GCancellable */
+                                       &error);
+      g_assert_error (error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
+      g_assert (result == NULL);
+      g_error_free (error);
 #endif /* G_OS_UNIX */
+    }
 
   /* Check that g_socket_get_credentials() work - (though this really
    * should be in socket.c)
@@ -1017,7 +1088,7 @@ do_test_peer (void)
   g_variant_get (result, "(&s)", &s);
   g_assert_cmpstr (s, ==, "You greeted me with 'Hey Again Peer!'.");
   g_variant_unref (result);
-  g_assert_cmpint (data.num_method_calls, ==, 5);
+  g_assert_cmpint (data.num_method_calls, ==, 6);
 
 #if 0
   /* TODO: THIS TEST DOESN'T WORK YET */
index 34410f4..803ed90 100644 (file)
@@ -79,23 +79,26 @@ on_connected_cancelled (GObject      *source_object,
   g_main_loop_quit (user_data);
 }
 
-static int
-on_timer (GCancellable *cancel)
+typedef struct
 {
-  g_cancellable_cancel (cancel);
-  return G_SOURCE_REMOVE;
-}
+  GCancellable *cancellable;
+  gboolean completed;
+} EventCallbackData;
 
 static void
 on_event (GSocketClient      *client,
           GSocketClientEvent  event,
           GSocketConnectable *connectable,
           GIOStream          *connection,
-          gboolean           *got_completed_event)
+          EventCallbackData  *data)
 {
-  if (event == G_SOCKET_CLIENT_COMPLETE)
+  if (data->cancellable && event == G_SOCKET_CLIENT_CONNECTED)
+    {
+      g_cancellable_cancel (data->cancellable);
+    }
+  else if (event == G_SOCKET_CLIENT_COMPLETE)
     {
-      *got_completed_event = TRUE;
+      data->completed = TRUE;
       g_assert_null (connection);
     }
 }
@@ -108,8 +111,7 @@ test_happy_eyeballs_cancel_delayed (void)
   GError *error = NULL;
   guint16 port;
   GMainLoop *loop;
-  GCancellable *cancel;
-  gboolean got_completed_event = FALSE;
+  EventCallbackData data = { NULL, FALSE };
 
   /* This just tests that cancellation works as expected, still emits the completed signal,
    * and never returns a connection */
@@ -122,17 +124,16 @@ test_happy_eyeballs_cancel_delayed (void)
   g_socket_service_start (service);
 
   client = g_socket_client_new ();
-  cancel = g_cancellable_new ();
-  g_socket_client_connect_to_host_async (client, "localhost", port, cancel, on_connected_cancelled, loop);
-  g_timeout_add (1, (GSourceFunc) on_timer, cancel);
-  g_signal_connect (client, "event", G_CALLBACK (on_event), &got_completed_event);
+  data.cancellable = g_cancellable_new ();
+  g_socket_client_connect_to_host_async (client, "localhost", port, data.cancellable, on_connected_cancelled, loop);
+  g_signal_connect (client, "event", G_CALLBACK (on_event), &data);
   g_main_loop_run (loop);
 
-  g_assert_true (got_completed_event);
+  g_assert_true (data.completed);
   g_main_loop_unref (loop);
   g_object_unref (service);
   g_object_unref (client);
-  g_object_unref (cancel);
+  g_object_unref (data.cancellable);
 }
 
 static void
@@ -144,7 +145,7 @@ test_happy_eyeballs_cancel_instant (void)
   guint16 port;
   GMainLoop *loop;
   GCancellable *cancel;
-  gboolean got_completed_event = FALSE;
+  EventCallbackData data = { NULL, FALSE };
 
   /* This tests the same things as above, test_happy_eyeballs_cancel_delayed(), but
    * with different timing since it sends an already cancelled cancellable */
@@ -160,10 +161,10 @@ test_happy_eyeballs_cancel_instant (void)
   cancel = g_cancellable_new ();
   g_cancellable_cancel (cancel);
   g_socket_client_connect_to_host_async (client, "localhost", port, cancel, on_connected_cancelled, loop);
-  g_signal_connect (client, "event", G_CALLBACK (on_event), &got_completed_event);
+  g_signal_connect (client, "event", G_CALLBACK (on_event), &data);
   g_main_loop_run (loop);
 
-  g_assert_true (got_completed_event);
+  g_assert_true (data.completed);
   g_main_loop_unref (loop);
   g_object_unref (service);
   g_object_unref (client);
index e67bf76..62d45a3 100644 (file)
@@ -3733,7 +3733,10 @@ g_main_context_prepare (GMainContext *context,
  *       store #GPollFD records that need to be polled.
  * @n_fds: (in): length of @fds.
  *
- * Determines information necessary to poll this main loop.
+ * Determines information necessary to poll this main loop. You should
+ * be careful to pass the resulting @fds array and its length @n_fds
+ * as is when calling g_main_context_check(), as this function relies
+ * on assumptions made when the array is filled.
  *
  * You must have successfully acquired the context with
  * g_main_context_acquire() before you may call this function.
@@ -3757,6 +3760,10 @@ g_main_context_query (GMainContext *context,
 
   TRACE (GLIB_MAIN_CONTEXT_BEFORE_QUERY (context, max_priority));
 
+  /* fds is filled sequentially from poll_records. Since poll_records
+   * are incrementally sorted by file descriptor identifier, fds will
+   * also be incrementally sorted.
+   */
   n_poll = 0;
   lastpollrec = NULL;
   for (pollrec = context->poll_records; pollrec; pollrec = pollrec->next)
@@ -3771,6 +3778,10 @@ g_main_context_query (GMainContext *context,
        */
       events = pollrec->fd->events & ~(G_IO_ERR|G_IO_HUP|G_IO_NVAL);
 
+      /* This optimization --using the same GPollFD to poll for more
+       * than one poll record-- relies on the poll records being
+       * incrementally sorted.
+       */
       if (lastpollrec && pollrec->fd->fd == lastpollrec->fd->fd)
         {
           if (n_poll - 1 < n_fds)
@@ -3816,7 +3827,10 @@ g_main_context_query (GMainContext *context,
  *       the last call to g_main_context_query()
  * @n_fds: return value of g_main_context_query()
  *
- * Passes the results of polling back to the main loop.
+ * Passes the results of polling back to the main loop. You should be
+ * careful to pass @fds and its length @n_fds as received from
+ * g_main_context_query(), as this functions relies on assumptions
+ * on how @fds is filled.
  *
  * You must have successfully acquired the context with
  * g_main_context_acquire() before you may call this function.
@@ -3871,10 +3885,22 @@ g_main_context_check (GMainContext *context,
       return FALSE;
     }
 
+  /* The linear iteration below relies on the assumption that both
+   * poll records and the fds array are incrementally sorted by file
+   * descriptor identifier.
+   */
   pollrec = context->poll_records;
   i = 0;
   while (pollrec && i < n_fds)
     {
+      /* Make sure that fds is sorted by file descriptor identifier. */
+      g_assert (i <= 0 || fds[i - 1].fd < fds[i].fd);
+
+      /* Skip until finding the first GPollRec matching the current GPollFD. */
+      while (pollrec && pollrec->fd->fd != fds[i].fd)
+        pollrec = pollrec->next;
+
+      /* Update all consecutive GPollRecs that match. */
       while (pollrec && pollrec->fd->fd == fds[i].fd)
         {
           if (pollrec->priority <= max_priority)
@@ -3885,6 +3911,7 @@ g_main_context_check (GMainContext *context,
           pollrec = pollrec->next;
         }
 
+      /* Iterate to next GPollFD. */
       i++;
     }
 
@@ -4495,6 +4522,7 @@ g_main_context_add_poll_unlocked (GMainContext *context,
   newrec->fd = fd;
   newrec->priority = priority;
 
+  /* Poll records are incrementally sorted by file descriptor identifier. */
   prevrec = NULL;
   nextrec = context->poll_records;
   while (nextrec)
index 2455015..78bf04a 100644 (file)
@@ -54,7 +54,7 @@ void (g_trace_mark) (gint64       begin_time_nsec,
                      const gchar *group,
                      const gchar *name,
                      const gchar *message_format,
-                     ...);
+                     ...) G_GNUC_PRINTF (5, 6);
 
 #ifndef HAVE_SYSPROF
 /* Optimise the whole call out */
index 1368e9b..a46e79c 100644 (file)
@@ -1541,6 +1541,62 @@ test_unix_file_poll (void)
   close (fd);
 }
 
+static void
+test_unix_fd_priority (void)
+{
+  gint fd1, fd2;
+  GMainLoop *loop;
+  GSource *source;
+
+  gint s1 = 0;
+  gboolean s2 = FALSE, s3 = FALSE;
+
+  g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/1592");
+
+  loop = g_main_loop_new (NULL, FALSE);
+
+  source = g_idle_source_new ();
+  g_source_set_callback (source, count_calls, &s1, NULL);
+  g_source_set_priority (source, 0);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+
+  fd1 = open ("/dev/random", O_RDONLY);
+  g_assert_cmpint (fd1, >=, 0);
+  source = g_unix_fd_source_new (fd1, G_IO_IN);
+  g_source_set_callback (source, G_SOURCE_FUNC (flag_bool), &s2, NULL);
+  g_source_set_priority (source, 10);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+
+  fd2 = open ("/dev/random", O_RDONLY);
+  g_assert_cmpint (fd2, >=, 0);
+  source = g_unix_fd_source_new (fd2, G_IO_IN);
+  g_source_set_callback (source, G_SOURCE_FUNC (flag_bool), &s3, NULL);
+  g_source_set_priority (source, 0);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+
+  /* This tests a bug that depends on the source with the lowest FD
+     identifier to have the lowest priority. Make sure that this is
+     the case. */
+  g_assert_cmpint (fd1, <, fd2);
+
+  g_assert_true (g_main_context_iteration (NULL, FALSE));
+
+  /* Idle source should have been dispatched. */
+  g_assert_cmpint (s1, ==, 1);
+  /* Low priority FD source shouldn't have been dispatched. */
+  g_assert_false (s2);
+  /* Default priority FD source should have been dispatched. */
+  g_assert_true (s3);
+
+  g_main_loop_unref (loop);
+
+  close (fd1);
+  close (fd2);
+}
+
 #endif
 
 #ifdef G_OS_UNIX
@@ -2034,6 +2090,7 @@ main (int argc, char *argv[])
   g_test_add_func ("/mainloop/source-unix-fd-api", test_source_unix_fd_api);
   g_test_add_func ("/mainloop/wait", test_mainloop_wait);
   g_test_add_func ("/mainloop/unix-file-poll", test_unix_file_poll);
+  g_test_add_func ("/mainloop/unix-fd-priority", test_unix_fd_priority);
 #endif
   g_test_add_func ("/mainloop/nfds", test_nfds);
 
index 8e8cfcb..a493c51 100644 (file)
@@ -1,5 +1,5 @@
 project('glib', 'c', 'cpp',
-  version : '2.66.2',
+  version : '2.66.3',
   # NOTE: We keep this pinned at 0.49 because that's what Debian 10 ships
   meson_version : '>= 0.49.2',
   default_options : [