From 585b698fad12e1c8676aba6a5d8e249355554d2b Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Fri, 29 Oct 2021 10:26:38 +0900 Subject: [PATCH] Imported Upstream version 2.66.3 --- NEWS | 18 ++++ gio/gdbusprivate.c | 5 +- gio/glocalfileinfo.c | 79 ++++++++++++---- gio/tests/gdbus-peer.c | 199 ++++++++++++++++++++++++++++------------- gio/tests/gsocketclient-slow.c | 39 ++++---- glib/gmain.c | 32 ++++++- glib/gtrace-private.h | 2 +- glib/tests/mainloop.c | 57 ++++++++++++ meson.build | 2 +- 9 files changed, 327 insertions(+), 106 deletions(-) diff --git a/NEWS b/NEWS index b8903bd..45692a2 100644 --- 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 ================================== diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index 5c980b4..2551e47 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -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, diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c index 90fcb33..a4abef0 100644 --- a/gio/glocalfileinfo.c +++ b/gio/glocalfileinfo.c @@ -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); diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 617d756..8450a3b 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -76,6 +76,12 @@ typedef struct gboolean signal_received; } PeerData; +/* This needs to be enough to usually take more than one write(), + * to reproduce + * . + * 1 MiB ought to be enough. */ +#define BIG_MESSAGE_ARRAY_SIZE (1024 * 1024) + static const gchar *test_interface_introspection_xml = "" " " @@ -88,6 +94,11 @@ static const gchar *test_interface_introspection_xml = " " " " " " + " " + " " + " " + " " + " " " " " " " " @@ -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 ). + * + * The second time, we call a method that returns a fd attached to a + * large message, to reproduce + * . 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 */ diff --git a/gio/tests/gsocketclient-slow.c b/gio/tests/gsocketclient-slow.c index 34410f4..803ed90 100644 --- a/gio/tests/gsocketclient-slow.c +++ b/gio/tests/gsocketclient-slow.c @@ -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); diff --git a/glib/gmain.c b/glib/gmain.c index e67bf76..62d45a3 100644 --- a/glib/gmain.c +++ b/glib/gmain.c @@ -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) diff --git a/glib/gtrace-private.h b/glib/gtrace-private.h index 2455015..78bf04a 100644 --- a/glib/gtrace-private.h +++ b/glib/gtrace-private.h @@ -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 */ diff --git a/glib/tests/mainloop.c b/glib/tests/mainloop.c index 1368e9b..a46e79c 100644 --- a/glib/tests/mainloop.c +++ b/glib/tests/mainloop.c @@ -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); diff --git a/meson.build b/meson.build index 8e8cfcb..a493c51 100644 --- a/meson.build +++ b/meson.build @@ -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 : [ -- 2.7.4