From aca8be64c127d1d782920294fa2b3fa93a0ba0e4 Mon Sep 17 00:00:00 2001 From: Yotam Bar-On Date: Sun, 21 Jan 2024 10:14:45 +0200 Subject: [PATCH] meta/selection-source-memory: Use memfd instead of GBytes MetaSelectionSourceMemory currently uses GBytes for its underlying data. This can cause memory overhead when large items, such as HD images, are stored in the clipboard. This commit changes the underlying data structure to a MetaAnonymousFile object, which writes to memfd instead of heap. When reading, MetaSelectionSourceMemory will create a Gio.UnixInputStream from the file descriptor generated by MetaAnonymousFile. We subclass the UnixInputStream as MetaUnixInputStream, to override the stream's close_fn function so that it invokes meta_anonymous_file_close_fd when the stream terminates. Part-of: --- src/core/meta-clipboard-manager.c | 20 ++++-- src/core/meta-selection-source-memory.c | 96 +++++++++++++++++++++++-- src/meta/meta-selection-source-memory.h | 5 +- 3 files changed, 109 insertions(+), 12 deletions(-) diff --git a/src/core/meta-clipboard-manager.c b/src/core/meta-clipboard-manager.c index eba2d689c..8485f3725 100644 --- a/src/core/meta-clipboard-manager.c +++ b/src/core/meta-clipboard-manager.c @@ -146,12 +146,22 @@ owner_changed_cb (MetaSelection *selection, } else if (!new_owner && display->saved_clipboard) { + g_autoptr (GError) error = NULL; + g_autoptr (MetaSelectionSource) new_source = NULL; + /* Old owner is gone, time to take over */ - new_owner = meta_selection_source_memory_new (display->saved_clipboard_mimetype, - display->saved_clipboard); - g_set_object (&display->selection_source, new_owner); - meta_selection_set_owner (selection, selection_type, new_owner); - g_object_unref (new_owner); + new_source = meta_selection_source_memory_new (display->saved_clipboard_mimetype, + display->saved_clipboard, + &error); + if (!new_source) + { + g_warning ("MetaClipboardManager failed to create new MetaSelectionSourceMemory: %s", + error->message); + return; + } + + g_set_object (&display->selection_source, new_source); + meta_selection_set_owner (selection, selection_type, new_source); } } diff --git a/src/core/meta-selection-source-memory.c b/src/core/meta-selection-source-memory.c index a01a460ec..48f0239d0 100644 --- a/src/core/meta-selection-source-memory.c +++ b/src/core/meta-selection-source-memory.c @@ -21,17 +21,75 @@ #include "meta/meta-selection-source-memory.h" +#include + +#include "core/meta-anonymous-file.h" + struct _MetaSelectionSourceMemory { MetaSelectionSource parent_instance; char *mimetype; - GBytes *content; + MetaAnonymousFile *content; }; G_DEFINE_TYPE (MetaSelectionSourceMemory, meta_selection_source_memory, META_TYPE_SELECTION_SOURCE) +struct _MetaUnixInputStream +{ + GUnixInputStream parent_instance; +}; + +#define META_TYPE_UNIX_INPUT_STREAM (meta_unix_input_stream_get_type ()) +G_DECLARE_FINAL_TYPE (MetaUnixInputStream, + meta_unix_input_stream, + META, UNIX_INPUT_STREAM, + GUnixInputStream) + +G_DEFINE_FINAL_TYPE (MetaUnixInputStream, + meta_unix_input_stream, + G_TYPE_UNIX_INPUT_STREAM) + +static gboolean +meta_unix_input_stream_close_fn (GInputStream *input_stream, + GCancellable *cancellable, + GError **error) +{ + GUnixInputStream *stream = G_UNIX_INPUT_STREAM (input_stream); + int fd; + + if (!g_unix_input_stream_get_close_fd (stream)) + return TRUE; + + fd = g_unix_input_stream_get_fd (stream); + meta_anonymous_file_close_fd (fd); + + return TRUE; +} + +static void +meta_unix_input_stream_class_init (MetaUnixInputStreamClass *klass) +{ + GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (klass); + + input_stream_class->close_fn = meta_unix_input_stream_close_fn; +} + +static void +meta_unix_input_stream_init (MetaUnixInputStream *stream) +{ +} + +static GInputStream * +meta_unix_input_stream_new (int fd) +{ + return G_INPUT_STREAM (g_object_new (META_TYPE_UNIX_INPUT_STREAM, + "fd", fd, + "close-fd", true, + NULL)); +} + static void meta_selection_source_memory_read_async (MetaSelectionSource *source, const char *mimetype, @@ -42,6 +100,7 @@ meta_selection_source_memory_read_async (MetaSelectionSource *source, MetaSelectionSourceMemory *source_mem = META_SELECTION_SOURCE_MEMORY (source); GInputStream *stream; g_autoptr (GTask) task = NULL; + int fd; if (g_strcmp0 (mimetype, source_mem->mimetype) != 0) { @@ -55,7 +114,20 @@ meta_selection_source_memory_read_async (MetaSelectionSource *source, task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, meta_selection_source_memory_read_async); - stream = g_memory_input_stream_new_from_bytes (source_mem->content); + fd = meta_anonymous_file_open_fd (source_mem->content, + META_ANONYMOUS_FILE_MAPMODE_SHARED); + + if (fd == -1) + { + g_task_report_new_error (source, callback, user_data, + meta_selection_source_memory_read_async, + G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to open MetaAnonymousFile"); + return; + } + + stream = meta_unix_input_stream_new (fd); + g_task_return_pointer (task, stream, g_object_unref); } @@ -85,7 +157,7 @@ meta_selection_source_memory_finalize (GObject *object) { MetaSelectionSourceMemory *source_mem = META_SELECTION_SOURCE_MEMORY (object); - g_clear_pointer (&source_mem->content, g_bytes_unref); + g_clear_pointer (&source_mem->content, meta_anonymous_file_free); g_free (source_mem->mimetype); G_OBJECT_CLASS (meta_selection_source_memory_parent_class)->finalize (object); @@ -111,16 +183,30 @@ meta_selection_source_memory_init (MetaSelectionSourceMemory *source) MetaSelectionSource * meta_selection_source_memory_new (const char *mimetype, - GBytes *content) + GBytes *content, + GError **error) { MetaSelectionSourceMemory *source; + MetaAnonymousFile *anon_file; + const uint8_t *data; + size_t size; g_return_val_if_fail (mimetype != NULL, NULL); g_return_val_if_fail (content != NULL, NULL); + data = g_bytes_get_data (content, &size); + anon_file = meta_anonymous_file_new (size, data); + + if (anon_file == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to create MetaAnonymousFile"); + return NULL; + } + source = g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL); source->mimetype = g_strdup (mimetype); - source->content = g_bytes_ref (content); + source->content = anon_file; return META_SELECTION_SOURCE (source); } diff --git a/src/meta/meta-selection-source-memory.h b/src/meta/meta-selection-source-memory.h index 62aeacbe2..5225f44bf 100644 --- a/src/meta/meta-selection-source-memory.h +++ b/src/meta/meta-selection-source-memory.h @@ -30,5 +30,6 @@ G_DECLARE_FINAL_TYPE (MetaSelectionSourceMemory, MetaSelectionSource) META_EXPORT -MetaSelectionSource * meta_selection_source_memory_new (const char *mimetype, - GBytes *content); +MetaSelectionSource * meta_selection_source_memory_new (const char *mimetype, + GBytes *content, + GError **error);