[gegl] buffer: improve handling of dynamically-changing swap dir
- From: Ell <ell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl] buffer: improve handling of dynamically-changing swap dir
- Date: Sat, 24 Nov 2018 15:53:24 +0000 (UTC)
commit b61f9015bf19611225df9832db3cfd9ee2558fc9
Author: Ell <ell_se yahoo com>
Date: Sat Nov 24 09:06:00 2018 -0500
buffer: improve handling of dynamically-changing swap dir
The current swap-dir management code doesn't cope well with
programs that modify the swap dir after initialization, which is
what GIMP is now doing (see issue gimp#2224.)
Move all the swap-dir management code to a new gegl-buffer-swap.c
file, and have gegl-init.c and gegl-buffer-config.c configure the
swap through this file.
The new file provides two new public functions:
gegl_buffer_swap_create_file(), which returns a unique filename for
a swap file in the current swap dir, and
gegl_buffer_swap_remove_file(), which removes and deletes a
previously-created swap file. All swap files created with
create_file(), which hasn't been explicitly removed with
remove_file(), are automatically deleted on shutdown. This allows
swap files to be safely deleted, even if the swap dir has been
changed since their creation.
Deprecate gegl_tile_backend_unlink_swap() in favor of the new
functions.
Use gegl_buffer_swap_{create,remove}_file() in the swap backend.
gegl/Makefile.am | 1 +
gegl/buffer/Makefile.am | 3 +
gegl/buffer/gegl-buffer-swap-private.h | 31 ++++
gegl/buffer/gegl-buffer-swap.c | 283 +++++++++++++++++++++++++++++++++
gegl/buffer/gegl-buffer-swap.h | 54 +++++++
gegl/gegl-config.c | 18 +--
gegl/gegl-init.c | 172 +-------------------
7 files changed, 384 insertions(+), 178 deletions(-)
---
diff --git a/gegl/Makefile.am b/gegl/Makefile.am
index c3dca9f2b..51df572f1 100644
--- a/gegl/Makefile.am
+++ b/gegl/Makefile.am
@@ -63,6 +63,7 @@ GEGL_introspectable_headers = \
buffer/gegl-buffer-iterator.h \
buffer/gegl-buffer-iterator2.h \
buffer/gegl-buffer-backend.h \
+ buffer/gegl-buffer-swap.h \
buffer/gegl-rectangle.h \
buffer/gegl-tile-backend.h \
buffer/gegl-tile-handler.h \
diff --git a/gegl/buffer/Makefile.am b/gegl/buffer/Makefile.am
index 0c1f79ca4..4256cfa78 100644
--- a/gegl/buffer/Makefile.am
+++ b/gegl/buffer/Makefile.am
@@ -40,6 +40,7 @@ libbuffer_la_SOURCES = \
gegl-rectangle.c \
gegl-buffer-load.c \
gegl-buffer-save.c \
+ gegl-buffer-swap.c \
gegl-sampler.c \
gegl-sampler-cubic.c \
gegl-sampler-linear.c \
@@ -70,6 +71,8 @@ libbuffer_la_SOURCES = \
gegl-rectangle.h \
gegl-buffer-types.h \
gegl-buffer-formats.h \
+ gegl-buffer-swap.h \
+ gegl-buffer-swap-private.h \
gegl-sampler.h \
gegl-sampler-cubic.h \
gegl-sampler-linear.h \
diff --git a/gegl/buffer/gegl-buffer-swap-private.h b/gegl/buffer/gegl-buffer-swap-private.h
new file mode 100644
index 000000000..d9b573cfc
--- /dev/null
+++ b/gegl/buffer/gegl-buffer-swap-private.h
@@ -0,0 +1,31 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GEGL_BUFFER_SWAP_PRIVATE_H__
+#define __GEGL_BUFFER_SWAP_PRIVATE_H__
+
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void gegl_buffer_swap_init (void);
+void gegl_buffer_swap_cleanup (void);
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-buffer-swap.c b/gegl/buffer/gegl-buffer-swap.c
new file mode 100644
index 000000000..205bfe790
--- /dev/null
+++ b/gegl/buffer/gegl-buffer-swap.c
@@ -0,0 +1,283 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#include <process.h>
+#define getpid() _getpid()
+#else
+#include <signal.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include "gegl-buffer-config.h"
+#include "gegl-buffer-swap.h"
+#include "gegl-buffer-swap-private.h"
+
+
+#define SWAP_PREFIX "gegl-swap-"
+
+
+/* local function prototypes */
+
+static void gegl_buffer_swap_notify_swap (GeglBufferConfig *config);
+
+static void gegl_buffer_swap_clean_dir (void);
+static gboolean gegl_buffer_swap_pid_is_running (gint pid);
+
+
+/* local variables */
+
+static GMutex swap_mutex;
+static gchar *swap_dir;
+static GHashTable *swap_files;
+static guint swap_file_counter;
+
+
+/* public functions */
+
+void
+gegl_buffer_swap_init (void)
+{
+ swap_files = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ g_signal_connect (gegl_buffer_config (), "notify::swap",
+ G_CALLBACK (gegl_buffer_swap_notify_swap),
+ NULL);
+
+ gegl_buffer_swap_notify_swap (gegl_buffer_config ());
+}
+
+void
+gegl_buffer_swap_cleanup (void)
+{
+ GHashTableIter iter;
+ const gchar *path;
+
+ g_signal_handlers_disconnect_by_func (gegl_buffer_config (),
+ gegl_buffer_swap_notify_swap,
+ NULL);
+
+ g_mutex_lock (&swap_mutex);
+
+ g_hash_table_iter_init (&iter, swap_files);
+
+ while (g_hash_table_iter_next (&iter, (gpointer) &path, NULL))
+ g_unlink (path);
+
+ g_clear_pointer (&swap_files, g_hash_table_destroy);
+
+ g_clear_pointer (&swap_dir, g_free);
+
+ g_mutex_unlock (&swap_mutex);
+}
+
+gchar *
+gegl_buffer_swap_create_file (const gchar *suffix)
+{
+ gchar *basename;
+ gchar *path;
+ gboolean added;
+
+ if (! swap_dir)
+ return NULL;
+
+ g_mutex_lock (&swap_mutex);
+
+ if (! swap_dir)
+ {
+ g_mutex_unlock (&swap_mutex);
+
+ return NULL;
+ }
+
+ if (suffix)
+ {
+ basename = g_strdup_printf (SWAP_PREFIX "%d-%u-%s",
+ (gint) getpid (),
+ swap_file_counter++,
+ suffix);
+ }
+ else
+ {
+ basename = g_strdup_printf (SWAP_PREFIX "%d-%u",
+ (gint) getpid (),
+ swap_file_counter++);
+ }
+
+ path = g_build_filename (swap_dir, basename, NULL);
+
+ added = g_hash_table_add (swap_files, path);
+
+ g_mutex_unlock (&swap_mutex);
+
+ g_free (basename);
+
+ if (! added)
+ {
+ g_warning ("swap file collision '%s'", path);
+
+ g_free (path);
+
+ return NULL;
+ }
+
+ return g_strdup (path);
+}
+
+void
+gegl_buffer_swap_remove_file (const gchar *path)
+{
+ gboolean removed;
+
+ g_return_if_fail (path != NULL);
+
+ g_mutex_lock (&swap_mutex);
+
+ removed = g_hash_table_remove (swap_files, path);
+
+ g_mutex_unlock (&swap_mutex);
+
+ if (removed)
+ g_unlink (path);
+ else
+ g_warning ("attempt to remove unregistered swap file '%s'", path);
+}
+
+
+/* private functions */
+
+static void
+gegl_buffer_swap_notify_swap (GeglBufferConfig *config)
+{
+ gchar *dir = config->swap;
+
+ if (dir)
+ {
+ dir = g_strstrip (g_strdup (dir));
+
+ /* Remove any trailing separator, unless the path is only made of a
+ * leading separator.
+ */
+ while (strlen (dir) > strlen (G_DIR_SEPARATOR_S) &&
+ g_str_has_suffix (dir, G_DIR_SEPARATOR_S))
+ {
+ dir[strlen (dir) - strlen (G_DIR_SEPARATOR_S)] = '\0';
+ }
+ }
+
+ g_mutex_lock (&swap_mutex);
+
+ if (! g_strcmp0 (dir, swap_dir))
+ {
+ g_mutex_unlock (&swap_mutex);
+
+ g_free (dir);
+
+ return;
+ }
+
+ g_clear_pointer (&swap_dir, g_free);
+
+ if (dir &&
+ ! g_file_test (dir, G_FILE_TEST_IS_DIR) &&
+ g_mkdir_with_parents (dir, S_IRUSR | S_IWUSR | S_IXUSR) != 0)
+ {
+ g_mutex_unlock (&swap_mutex);
+
+ g_free (dir);
+
+ return;
+ }
+
+ swap_dir = dir;
+
+ gegl_buffer_swap_clean_dir ();
+
+ g_mutex_unlock (&swap_mutex);
+
+ return;
+}
+
+static void
+gegl_buffer_swap_clean_dir (void)
+{
+ GDir *dir;
+
+ if (! swap_dir)
+ return;
+
+ dir = g_dir_open (swap_dir, 0, NULL);
+
+ if (dir != NULL)
+ {
+ const gchar *basename;
+
+ while ((basename = g_dir_read_name (dir)) != NULL)
+ {
+ if (g_str_has_prefix (basename, SWAP_PREFIX))
+ {
+ gint pid = atoi (basename + strlen (SWAP_PREFIX));
+
+ if (! gegl_buffer_swap_pid_is_running (pid))
+ {
+ gchar *path = g_build_filename (swap_dir, basename, NULL);
+
+ g_unlink (path);
+
+ g_free (path);
+ }
+ }
+ }
+
+ g_dir_close (dir);
+ }
+}
+
+#ifdef G_OS_WIN32
+
+static gboolean
+gegl_buffer_swap_pid_is_running (gint pid)
+{
+ HANDLE h;
+ DWORD exitcode = 0;
+
+ h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid);
+ GetExitCodeProcess (h, &exitcode);
+ CloseHandle (h);
+
+ return exitcode == STILL_ACTIVE;
+}
+
+#else
+
+static gboolean
+gegl_buffer_swap_pid_is_running (gint pid)
+{
+ return kill (pid, 0) == 0;
+}
+
+#endif
diff --git a/gegl/buffer/gegl-buffer-swap.h b/gegl/buffer/gegl-buffer-swap.h
new file mode 100644
index 000000000..8e6ca074d
--- /dev/null
+++ b/gegl/buffer/gegl-buffer-swap.h
@@ -0,0 +1,54 @@
+/* This file is part of GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GEGL_BUFFER_SWAP_H__
+#define __GEGL_BUFFER_SWAP_H__
+
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/**
+ * gegl_buffer_swap_create_file:
+ * @suffix: (nullable): a string to suffix the filename with, for
+ * identification purposes, or %NULL.
+ *
+ * Generates a unique filename in the GEGL swap directory, suitable for
+ * using as swap space. When the file is no longer needed, it may be
+ * removed with gegl_buffer_swap_remove_file(); otherwise, it will be
+ * removed when gegl_exit() is called.
+ *
+ * Returns: (type filename) (nullable): a string containing the full
+ * file path, or %NULL is the swap is disabled. The returned string
+ * should be freed with g_free() when no longer needed.
+ */
+gchar * gegl_buffer_swap_create_file (const gchar *suffix);
+
+/**
+ * gegl_buffer_swap_remove_file:
+ * @path: (type filename): the swap file to remove, as returned by
+ * gegl_buffer_swap_create_file()
+ *
+ * Removes a swap file, generated using gegl_buffer_swap_create_file(),
+ * unlinking the file, if exists.
+ */
+void gegl_buffer_swap_remove_file (const gchar *path);
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/gegl-config.c b/gegl/gegl-config.c
index 0a5043690..b24388970 100644
--- a/gegl/gegl-config.c
+++ b/gegl/gegl-config.c
@@ -183,24 +183,21 @@ gegl_config_class_init (GeglConfigClass *klass)
"Tile width",
"default tile width for created buffers.",
0, G_MAXINT, 128,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_TILE_HEIGHT,
g_param_spec_int ("tile-height",
"Tile height",
"default tile height for created buffers.",
0, G_MAXINT, 128,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_TILE_CACHE_SIZE,
g_param_spec_uint64 ("tile-cache-size",
"Tile Cache size",
"size of tile cache in bytes",
0, G_MAXUINT64, 512 * 1024 * 1024,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_CHUNK_SIZE,
g_param_spec_int ("chunk-size",
@@ -223,8 +220,7 @@ gegl_config_class_init (GeglConfigClass *klass)
"Swap",
"where gegl stores it's swap files",
NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE));
_gegl_threads = g_get_num_processors ();
_gegl_threads = MIN (_gegl_threads, GEGL_MAX_THREADS);
@@ -250,8 +246,7 @@ gegl_config_class_init (GeglConfigClass *klass)
"Queue size",
"Maximum size of a file backend's writer thread queue
(in bytes)",
2, G_MAXINT, 50 * 1024 *1024,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, PROP_APPLICATION_LICENSE,
g_param_spec_string ("application-license",
@@ -274,7 +269,8 @@ gegl_config_init (GeglConfig *self)
GeglBufferConfig *bconf = gegl_buffer_config ();
for (int i = 0; forward_props[i]; i++)
g_object_bind_property (bconf, forward_props[i],
- self, forward_props[i], G_BINDING_BIDIRECTIONAL);
+ self, forward_props[i],
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
}
#undef gegl_config_threads
diff --git a/gegl/gegl-init.c b/gegl/gegl-init.c
index 414ebeb1f..631f2a517 100644
--- a/gegl/gegl-init.c
+++ b/gegl/gegl-init.c
@@ -56,29 +56,6 @@ DllMain (HINSTANCE hinstDLL,
return TRUE;
}
-static inline gboolean
-pid_is_running (gint pid)
-{
- HANDLE h;
- DWORD exitcode = 0;
-
- h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid);
- GetExitCodeProcess (h, &exitcode);
- CloseHandle (h);
-
- return exitcode == STILL_ACTIVE;
-}
-
-#else
-
-#include <sys/types.h>
-#include <signal.h>
-
-static inline gboolean
-pid_is_running (gint pid)
-{
- return (kill (pid, 0) == 0);
-}
#endif
@@ -99,6 +76,7 @@ guint gegl_debug_flags = 0;
#include "operation/gegl-operation-handlers-private.h"
#include "buffer/gegl-buffer-private.h"
#include "buffer/gegl-buffer-iterator-private.h"
+#include "buffer/gegl-buffer-swap-private.h"
#include "buffer/gegl-tile-backend-ram.h"
#include "buffer/gegl-tile-backend-file.h"
#include "gegl-config.h"
@@ -121,58 +99,6 @@ static GeglModuleDB *module_db = NULL;
static glong global_time = 0;
-static gboolean swap_init_done = FALSE;
-
-static gchar *swap_dir = NULL;
-
-static void
-gegl_init_swap_dir (void)
-{
- gchar *swapdir = NULL;
-
- if (config->swap)
- {
- if (g_ascii_strcasecmp (config->swap, "ram") == 0)
- {
- swapdir = NULL;
- }
- else
- {
- swapdir = g_strstrip (g_strdup (config->swap));
-
- /* Remove any trailing separator, unless the path is only made of a leading separator. */
- while (strlen (swapdir) > strlen (G_DIR_SEPARATOR_S) && g_str_has_suffix (swapdir,
G_DIR_SEPARATOR_S))
- swapdir[strlen (swapdir) - strlen (G_DIR_SEPARATOR_S)] = '\0';
- }
- }
-
- if (swapdir &&
- ! g_file_test (swapdir, G_FILE_TEST_IS_DIR) &&
- g_mkdir_with_parents (swapdir, S_IRUSR | S_IWUSR | S_IXUSR) != 0)
- {
- g_clear_pointer (&swapdir, g_free);
- }
-
- g_object_set (config, "swap", swapdir, NULL);
-
- swap_dir = swapdir;
-
- swap_init_done = TRUE;
-}
-
-/* Return the swap directory, or NULL if swapping is disabled */
-const gchar *
-gegl_swap_dir (void)
-{
- if (!swap_init_done)
- {
- g_critical ("swap parsing not done");
- gegl_init_swap_dir ();
- }
-
- return swap_dir;
-}
-
static void load_module_path(gchar *path, GeglModuleDB *db);
static void
@@ -334,19 +260,6 @@ gegl_get_option_group (void)
return group;
}
-static void gegl_config_set_defaults (GeglConfig *config)
-{
- gchar *swapdir = g_build_filename (g_get_user_cache_dir(),
- GEGL_LIBRARY,
- "swap",
- NULL);
- g_object_set (config,
- "swap", swapdir,
- NULL);
-
- g_free (swapdir);
-}
-
static void gegl_config_parse_env (GeglConfig *config)
{
if (g_getenv ("GEGL_QUALITY"))
@@ -415,10 +328,8 @@ static void gegl_config_parse_env (GeglConfig *config)
GeglConfig *gegl_config (void)
{
if (!config)
- {
- config = g_object_new (GEGL_TYPE_CONFIG, NULL);
- gegl_config_set_defaults (config);
- }
+ config = g_object_new (GEGL_TYPE_CONFIG, NULL);
+
return config;
}
@@ -435,43 +346,6 @@ void gegl_reset_stats (void)
gegl_stats_reset (gegl_stats ());
}
-static void swap_clean (void)
-{
- const gchar *swap_dir = gegl_swap_dir ();
- GDir *dir;
-
- if (! swap_dir)
- return;
-
- dir = g_dir_open (gegl_swap_dir (), 0, NULL);
-
- if (dir != NULL)
- {
- GPatternSpec *pattern = g_pattern_spec_new ("*");
- const gchar *name;
-
- while ((name = g_dir_read_name (dir)) != NULL)
- {
- if (g_pattern_match_string (pattern, name))
- {
- gint readpid = atoi (name);
-
- if (!pid_is_running (readpid))
- {
- gchar *fname = g_build_filename (gegl_swap_dir (),
- name,
- NULL);
- g_unlink (fname);
- g_free (fname);
- }
- }
- }
-
- g_pattern_spec_free (pattern);
- g_dir_close (dir);
- }
-}
-
void gegl_temp_buffer_free (void);
void
@@ -491,6 +365,7 @@ gegl_exit (void)
gegl_operation_handlers_cleanup ();
gegl_random_cleanup ();
gegl_parallel_cleanup ();
+ gegl_buffer_swap_cleanup ();
gegl_cl_cleanup ();
gegl_temp_buffer_free ();
@@ -526,44 +401,10 @@ gegl_exit (void)
#endif
}
- if (gegl_swap_dir ())
- {
- /* remove all files matching <$GEGL_SWAP>/GEGL-<pid>-*.swap */
-
- guint pid = getpid ();
- GDir *dir = g_dir_open (gegl_swap_dir (), 0, NULL);
-
- gchar *glob = g_strdup_printf ("%i-*", pid);
- GPatternSpec *pattern = g_pattern_spec_new (glob);
- g_free (glob);
-
- if (dir != NULL)
- {
- const gchar *name;
-
- while ((name = g_dir_read_name (dir)) != NULL)
- {
- if (g_pattern_match_string (pattern, name))
- {
- gchar *fname = g_build_filename (gegl_swap_dir (),
- name,
- NULL);
- g_unlink (fname);
- g_free (fname);
- }
- }
-
- g_dir_close (dir);
- }
-
- g_pattern_spec_free (pattern);
- }
g_clear_object (&config);
global_time = 0;
}
-static void swap_clean (void);
-
void
gegl_get_version (int *major,
int *minor,
@@ -700,10 +541,9 @@ gegl_post_parse_hook (GOptionContext *context,
if (cmd_gegl_disable_opencl)
gegl_cl_hard_disable ();
- gegl_init_swap_dir ();
-
GEGL_INSTRUMENT_START();
+ gegl_buffer_swap_init ();
gegl_parallel_init ();
gegl_operation_gtype_init ();
gegl_tile_cache_init ();
@@ -720,8 +560,6 @@ gegl_post_parse_hook (GOptionContext *context,
gegl_instrument ("gegl", "gegl_init", gegl_ticks () - global_time);
- swap_clean ();
-
g_signal_connect (G_OBJECT (config),
"notify::use-opencl",
G_CALLBACK (gegl_config_use_opencl_notify),
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]