From 50e6278327561fb4f3d739ed086d5dbd8db45b7c Mon Sep 17 00:00:00 2001 From: o9000 Date: Tue, 1 Mar 2016 20:59:13 +0100 Subject: [PATCH] Move icon cache to a separate file; protect cache with file locks --- CMakeLists.txt | 5 +- src/launcher/icon-theme-common.c | 84 ++--------------- src/launcher/icon-theme-common.h | 4 +- src/tint2conf/CMakeLists.txt | 5 +- src/util/cache.c | 156 +++++++++++++++++++++++++++++++ src/util/cache.h | 39 ++++++++ tint2.files | 2 + 7 files changed, 214 insertions(+), 81 deletions(-) create mode 100644 src/util/cache.c create mode 100644 src/util/cache.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a34cf16..d9d294a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,7 @@ include_directories( ${PROJECT_BINARY_DIR} src/launcher src/tooltip src/util - src/execplugin + src/execplugin src/freespace ${X11_INCLUDE_DIRS} ${PANGOCAIRO_INCLUDE_DIRS} @@ -112,12 +112,13 @@ set( SOURCES src/config.c src/taskbar/taskbar.c src/taskbar/taskbarname.c src/tooltip/tooltip.c - src/execplugin/execplugin.c + src/execplugin/execplugin.c src/freespace/freespace.c src/util/area.c src/util/common.c src/util/strnatcmp.c src/util/timer.c + src/util/cache.c src/util/window.c ) if( ENABLE_BATTERY ) diff --git a/src/launcher/icon-theme-common.c b/src/launcher/icon-theme-common.c index c94bbfa..57c62c5 100644 --- a/src/launcher/icon-theme-common.c +++ b/src/launcher/icon-theme-common.c @@ -27,6 +27,7 @@ #include "apps-common.h" #include "common.h" +#include "cache.h" #define ICON_DIR_TYPE_SCALABLE 0 #define ICON_DIR_TYPE_FIXED 1 @@ -317,7 +318,7 @@ void free_themes(IconThemeWrapper *wrapper) } g_slist_free(wrapper->themes_fallback); g_slist_free_full(wrapper->_queued, free); - g_hash_table_destroy(wrapper->_cache); + free_cache(&wrapper->_cache); free(wrapper); } @@ -452,72 +453,9 @@ gchar *get_icon_cache_path() return g_build_filename(g_get_user_cache_dir(), "tint2", "icon.cache", NULL); } -void load_cache(GHashTable **cache, gchar *cache_path) -{ - *cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - - FILE *f = fopen(cache_path, "rt"); - if (!f) - return; - - char *line = NULL; - size_t line_size; - - while (getline(&line, &line_size, f) >= 0) { - char *key, *value; - - size_t line_len = strlen(line); - gboolean has_newline = FALSE; - if (line_len >= 1) { - if (line[line_len - 1] == '\n') { - line[line_len - 1] = '\0'; - line_len--; - has_newline = TRUE; - } - } - if (!has_newline) - break; - - if (line_len == 0) - continue; - - if (parse_theme_line(line, &key, &value)) { - g_hash_table_insert(*cache, g_strdup(key), g_strdup(value)); - } - } - free(line); - fclose(f); -} - -void write_cache_line(gpointer key, gpointer value, gpointer user_data) -{ - gchar *k = key; - gchar *v = value; - FILE *f = user_data; - - fprintf(f, "%s=%s\n", k, v); -} - -void save_cache(GHashTable *cache, gchar *cache_path) -{ - FILE *f = fopen(cache_path, "w"); - if (!f) { - gchar *dir_path = g_path_get_dirname(cache_path); - g_mkdir_with_parents(dir_path, 0700); - g_free(dir_path); - f = fopen(cache_path, "w"); - if (!f) { - fprintf(stderr, RED "Could not save icon theme cache!" RESET "\n"); - return; - } - } - g_hash_table_foreach(cache, write_cache_line, f); - fclose(f); -} - void load_icon_cache(IconThemeWrapper *wrapper) { - if (wrapper->_cache) + if (wrapper->_cache.loaded) return; fprintf(stderr, GREEN "Loading icon theme cache..." RESET "\n"); @@ -529,12 +467,12 @@ void load_icon_cache(IconThemeWrapper *wrapper) void save_icon_cache(IconThemeWrapper *wrapper) { - if (!wrapper || !wrapper->_cache || !wrapper->_cache_dirty) + if (!wrapper || !wrapper->_cache.dirty) return; fprintf(stderr, GREEN "Saving icon theme cache..." RESET "\n"); gchar *cache_path = get_icon_cache_path(); - save_cache(wrapper->_cache, cache_path); + save_cache(&wrapper->_cache, cache_path); g_free(cache_path); } @@ -761,8 +699,9 @@ char *get_icon_path_from_cache(IconThemeWrapper *wrapper, const char *icon_name, load_icon_cache(wrapper); gchar *key = g_strdup_printf("%s\t%s\t%d", wrapper->icon_theme_name, icon_name, size); - gchar *value = g_hash_table_lookup(wrapper->_cache, key); + const gchar *value = get_from_cache(&wrapper->_cache, key); g_free(key); + if (!value) { fprintf(stderr, YELLOW "Icon path not found in cache: theme = %s, icon = %s, size = %d" RESET "\n", @@ -787,13 +726,8 @@ void add_icon_path_to_cache(IconThemeWrapper *wrapper, const char *icon_name, in load_icon_cache(wrapper); gchar *key = g_strdup_printf("%s\t%s\t%d", wrapper->icon_theme_name, icon_name, size); - gchar *value = g_hash_table_lookup(wrapper->_cache, key); - if (value && g_str_equal(value, path)) { - g_free(key); - return; - } - g_hash_table_insert(wrapper->_cache, key, g_strdup(path)); - wrapper->_cache_dirty = TRUE; + add_to_cache(&wrapper->_cache, key, path); + g_free(key); } char *get_icon_path(IconThemeWrapper *wrapper, const char *icon_name, int size) diff --git a/src/launcher/icon-theme-common.h b/src/launcher/icon-theme-common.h index 13a8654..e6d851f 100644 --- a/src/launcher/icon-theme-common.h +++ b/src/launcher/icon-theme-common.h @@ -7,6 +7,7 @@ #define ICON_THEME_COMMON_H #include +#include "cache.h" typedef struct IconThemeWrapper { // The icon theme name for which this wrapper was created @@ -19,8 +20,7 @@ typedef struct IconThemeWrapper { GSList *themes_fallback; // Fallback themes are loaded lazily when needed. gboolean _fallback_loaded; - GHashTable *_cache; - gboolean _cache_dirty; + Cache _cache; // List of icon theme names that have been queued for loading. // Used to avoid loading the same theme twice, and to avoid cycles. GSList *_queued; diff --git a/src/tint2conf/CMakeLists.txt b/src/tint2conf/CMakeLists.txt index 134caa0..9595f6a 100644 --- a/src/tint2conf/CMakeLists.txt +++ b/src/tint2conf/CMakeLists.txt @@ -20,8 +20,9 @@ include_directories( ../util ${RSVG_INCLUDE_DIRS} ) set(SOURCES ../util/common.c - ../util/strnatcmp.c - ../config.c + ../util/strnatcmp.c + ../util/cache.c + ../config.c ../server.c ../launcher/apps-common.c ../launcher/icon-theme-common.c diff --git a/src/util/cache.c b/src/util/cache.c new file mode 100644 index 0000000..58fdbca --- /dev/null +++ b/src/util/cache.c @@ -0,0 +1,156 @@ +/************************************************************************** +* +* Tint2 : cache +* +* Copyright (C) 2016 @o9000 +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License version 2 +* as published by the Free Software Foundation. +* +* This program 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 General Public License for more details. +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**************************************************************************/ + +#include "cache.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +void init_cache(Cache *cache) +{ + if (cache->_table) + free_cache(cache); + cache->_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + cache->dirty = FALSE; + cache->loaded = FALSE; +} + +void free_cache(Cache *cache) +{ + if (cache->_table) + g_hash_table_destroy(cache->_table); + cache->_table = NULL; + cache->dirty = FALSE; + cache->loaded = FALSE; +} + +void load_cache(Cache *cache, const gchar *cache_path) +{ + init_cache(cache); + + int fd = open(cache_path, O_RDONLY); + if (fd == -1) + return; + flock(fd, LOCK_SH); + + FILE *f = fopen(cache_path, "rt"); + if (!f) + goto unlock; + + char *line = NULL; + size_t line_size; + + while (getline(&line, &line_size, f) >= 0) { + char *key, *value; + + size_t line_len = strlen(line); + gboolean has_newline = FALSE; + if (line_len >= 1) { + if (line[line_len - 1] == '\n') { + line[line_len - 1] = '\0'; + line_len--; + has_newline = TRUE; + } + } + if (!has_newline) + break; + + if (line_len == 0) + continue; + + if (parse_line(line, &key, &value)) { + g_hash_table_insert(cache->_table, g_strdup(key), g_strdup(value)); + } + } + free(line); + fclose(f); + + cache->loaded = TRUE; + +unlock: + flock(fd, LOCK_UN); + close(fd); +} + +void write_cache_line(gpointer key, gpointer value, gpointer user_data) +{ + gchar *k = key; + gchar *v = value; + FILE *f = user_data; + + fprintf(f, "%s=%s\n", k, v); +} + +void save_cache(Cache *cache, const gchar *cache_path) +{ + int fd = open(cache_path, O_RDONLY | O_CREAT); + if (fd == -1) + return; + flock(fd, LOCK_EX); + + FILE *f = fopen(cache_path, "w"); + if (!f) { + gchar *dir_path = g_path_get_dirname(cache_path); + g_mkdir_with_parents(dir_path, 0700); + g_free(dir_path); + f = fopen(cache_path, "w"); + if (!f) { + fprintf(stderr, RED "Could not save icon theme cache!" RESET "\n"); + goto unlock; + } + } + g_hash_table_foreach(cache->_table, write_cache_line, f); + fclose(f); + cache->dirty = FALSE; + +unlock: + flock(fd, LOCK_UN); + close(fd); +} + +const gchar *get_from_cache(Cache *cache, const gchar *key) +{ + if (!cache->_table) + return NULL; + return g_hash_table_lookup(cache->_table, key); +} + +void add_to_cache(Cache *cache, const gchar *key, const gchar *value) +{ + if (!cache->_table) + init_cache(cache); + + if (!key || !value) + return; + + gchar *old_value = g_hash_table_lookup(cache->_table, key); + if (old_value && g_str_equal(old_value, value)) + return; + + g_hash_table_insert(cache->_table, g_strdup(key), g_strdup(value)); + cache->dirty = TRUE; +} diff --git a/src/util/cache.h b/src/util/cache.h new file mode 100644 index 0000000..248657a --- /dev/null +++ b/src/util/cache.h @@ -0,0 +1,39 @@ +#ifndef CACHE_H +#define CACHE_H + +#include + +// A cache with string keys and values, backed by a file. +// The strings must not be NULL and are stripped of any whitespace at start and end. +typedef struct Cache { + gboolean dirty; + gboolean loaded; + GHashTable *_table; +} Cache; + +// Initializes the cache. You can also call load_cache directly if you set the memory contents to zero first. +void init_cache(Cache *cache); + +// Clears the cache contents and releases all memory, but not the object. +// You can use init_cache or load_cache afterwards. +void free_cache(Cache *cache); + +// Clears the cache contents and loads new contents from a file. +// Sets the loaded flag to TRUE. +void load_cache(Cache *cache, const gchar *cache_path); + +// Saves the cache contents to a file. +// Clears the dirty flag. +void save_cache(Cache *cache, const gchar *cache_path); + +// Returns a pointer to the value in the cache, or NULL if not found. +// Do not free the returned value! +const gchar *get_from_cache(Cache *cache, const gchar *key); + +// Adds a key-value pair to the cache. NULL keys or values are not allowed. +// If the key already exists, the old value is and replaced with the new value. +// Does not take ownership of the pointers (neither key, nor value); instead it makes copies. +// Sets the dirty flag to TRUE. +void add_to_cache(Cache *cache, const gchar *key, const gchar *value); + +#endif diff --git a/tint2.files b/tint2.files index 4b84421..c50f911 100644 --- a/tint2.files +++ b/tint2.files @@ -177,3 +177,5 @@ tint2.files tint2.includes tint2.svg src/tint2conf/po/ru.po +src/util/cache.c +src/util/cache.h